src/jdk/nashorn/internal/codegen/AssignSymbols.java

Mon, 20 Oct 2014 12:06:36 +0200

author
attila
date
Mon, 20 Oct 2014 12:06:36 +0200
changeset 1064
03c06c337d9d
parent 1052
c3fb7c0a95d9
child 1105
c3a510b73875
permissions
-rw-r--r--

8059844: Implement optimistic splitter
Reviewed-by: hannesw, lagergren

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.codegen;
    28 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
    29 import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
    30 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
    31 import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
    32 import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
    33 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
    34 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
    35 import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
    36 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
    37 import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
    38 import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
    39 import static jdk.nashorn.internal.ir.Symbol.IS_CONST;
    40 import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
    41 import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
    42 import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
    43 import static jdk.nashorn.internal.ir.Symbol.IS_LET;
    44 import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
    45 import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
    46 import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
    47 import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
    48 import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
    49 import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
    51 import java.util.ArrayDeque;
    52 import java.util.ArrayList;
    53 import java.util.Deque;
    54 import java.util.HashMap;
    55 import java.util.HashSet;
    56 import java.util.Iterator;
    57 import java.util.List;
    58 import java.util.ListIterator;
    59 import java.util.Map;
    60 import java.util.Set;
    61 import jdk.nashorn.internal.ir.AccessNode;
    62 import jdk.nashorn.internal.ir.BinaryNode;
    63 import jdk.nashorn.internal.ir.Block;
    64 import jdk.nashorn.internal.ir.CatchNode;
    65 import jdk.nashorn.internal.ir.Expression;
    66 import jdk.nashorn.internal.ir.ForNode;
    67 import jdk.nashorn.internal.ir.FunctionNode;
    68 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
    69 import jdk.nashorn.internal.ir.IdentNode;
    70 import jdk.nashorn.internal.ir.IndexNode;
    71 import jdk.nashorn.internal.ir.LexicalContext;
    72 import jdk.nashorn.internal.ir.LexicalContextNode;
    73 import jdk.nashorn.internal.ir.LiteralNode;
    74 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
    75 import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
    76 import jdk.nashorn.internal.ir.Node;
    77 import jdk.nashorn.internal.ir.RuntimeNode;
    78 import jdk.nashorn.internal.ir.RuntimeNode.Request;
    79 import jdk.nashorn.internal.ir.Statement;
    80 import jdk.nashorn.internal.ir.SwitchNode;
    81 import jdk.nashorn.internal.ir.Symbol;
    82 import jdk.nashorn.internal.ir.TryNode;
    83 import jdk.nashorn.internal.ir.UnaryNode;
    84 import jdk.nashorn.internal.ir.VarNode;
    85 import jdk.nashorn.internal.ir.WithNode;
    86 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
    87 import jdk.nashorn.internal.runtime.Context;
    88 import jdk.nashorn.internal.runtime.ECMAErrors;
    89 import jdk.nashorn.internal.runtime.ErrorManager;
    90 import jdk.nashorn.internal.runtime.JSErrorType;
    91 import jdk.nashorn.internal.runtime.ParserException;
    92 import jdk.nashorn.internal.runtime.Source;
    93 import jdk.nashorn.internal.runtime.logging.DebugLogger;
    94 import jdk.nashorn.internal.runtime.logging.Loggable;
    95 import jdk.nashorn.internal.runtime.logging.Logger;
    97 /**
    98  * This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
    99  * possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
   100  * nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
   101  * for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
   102  * during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
   103  * visitor.
   104  */
   105 @Logger(name="symbols")
   106 final class AssignSymbols extends NodeVisitor<LexicalContext> implements Loggable {
   107     private final DebugLogger log;
   108     private final boolean     debug;
   110     private static boolean isParamOrVar(final IdentNode identNode) {
   111         final Symbol symbol = identNode.getSymbol();
   112         return symbol.isParam() || symbol.isVar();
   113     }
   115     private static String name(final Node node) {
   116         final String cn = node.getClass().getName();
   117         final int lastDot = cn.lastIndexOf('.');
   118         if (lastDot == -1) {
   119             return cn;
   120         }
   121         return cn.substring(lastDot + 1);
   122     }
   124     /**
   125      * Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
   126      * needing a slot after all.
   127      * @param functionNode the function node
   128      * @return the passed in node, for easy chaining
   129      */
   130     private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
   131         if (!functionNode.needsCallee()) {
   132             functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
   133         }
   134         if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
   135             functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
   136         }
   137         // Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
   138         if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
   139             final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
   140             if(selfSymbol != null) {
   141                 if(selfSymbol.isFunctionSelf()) {
   142                     selfSymbol.setNeedsSlot(false);
   143                     selfSymbol.clearFlag(Symbol.IS_VAR);
   144                 }
   145             } else {
   146                 assert functionNode.isProgram();
   147             }
   148         }
   149         return functionNode;
   150     }
   152     private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
   153     private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
   154     private final Compiler compiler;
   156     public AssignSymbols(final Compiler compiler) {
   157         super(new LexicalContext());
   158         this.compiler = compiler;
   159         this.log   = initLogger(compiler.getContext());
   160         this.debug = log.isEnabled();
   161     }
   163     @Override
   164     public DebugLogger getLogger() {
   165         return log;
   166     }
   168     @Override
   169     public DebugLogger initLogger(final Context context) {
   170         return context.getLogger(this.getClass());
   171     }
   173     /**
   174      * Define symbols for all variable declarations at the top of the function scope. This way we can get around
   175      * problems like
   176      *
   177      * while (true) {
   178      *   break;
   179      *   if (true) {
   180      *     var s;
   181      *   }
   182      * }
   183      *
   184      * to an arbitrary nesting depth.
   185      *
   186      * see NASHORN-73
   187      *
   188      * @param functionNode the FunctionNode we are entering
   189      * @param body the body of the FunctionNode we are entering
   190      */
   191     private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
   192         // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
   193         body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
   194             @Override
   195             protected boolean enterDefault(final Node node) {
   196                 // Don't bother visiting expressions; var is a statement, it can't be inside an expression.
   197                 // This will also prevent visiting nested functions (as FunctionNode is an expression).
   198                 return !(node instanceof Expression);
   199             }
   201             @Override
   202             public Node leaveVarNode(final VarNode varNode) {
   203                 if (varNode.isStatement()) {
   204                     final IdentNode ident  = varNode.getName();
   205                     final Block block = varNode.isBlockScoped() ? getLexicalContext().getCurrentBlock() : body;
   206                     final Symbol symbol = defineSymbol(block, ident.getName(), ident, varNode.getSymbolFlags());
   207                     if (varNode.isFunctionDeclaration()) {
   208                         symbol.setIsFunctionDeclaration();
   209                     }
   210                     return varNode.setName(ident.setSymbol(symbol));
   211                 }
   212                 return varNode;
   213             }
   214         });
   215     }
   217     private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
   218         return createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
   219     }
   221     /**
   222      * Creates an ident node for an implicit identifier within the function (one not declared in the script source
   223      * code). These identifiers are defined with function's token and finish.
   224      * @param name the name of the identifier
   225      * @return an ident node representing the implicit identifier.
   226      */
   227     private IdentNode createImplicitIdentifier(final String name) {
   228         final FunctionNode fn = lc.getCurrentFunction();
   229         return new IdentNode(fn.getToken(), fn.getFinish(), name);
   230     }
   232     private Symbol createSymbol(final String name, final int flags) {
   233         if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
   234             //reuse global symbols so they can be hashed
   235             Symbol global = globalSymbols.get(name);
   236             if (global == null) {
   237                 global = new Symbol(name, flags);
   238                 globalSymbols.put(name, global);
   239             }
   240             return global;
   241         }
   242         return new Symbol(name, flags);
   243     }
   245     /**
   246      * Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
   247      * used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
   248      * expressions as well as for assignment of {@code :arguments} to {@code arguments}.
   249      *
   250      * @param name the ident node identifying the variable to initialize
   251      * @param initConstant the compiler constant it is initialized to
   252      * @param fn the function node the assignment is for
   253      * @return a var node with the appropriate assignment
   254      */
   255     private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
   256         final IdentNode init = compilerConstantIdentifier(initConstant);
   257         assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
   259         final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
   261         final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
   262         assert nameSymbol != null;
   264         return (VarNode)synthVar.setName(name.setSymbol(nameSymbol)).accept(this);
   265     }
   267     private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
   268         final List<VarNode> syntheticInitializers = new ArrayList<>(2);
   270         // Must visit the new var nodes in the context of the body. We could also just set the new statements into the
   271         // block and then revisit the entire block, but that seems to be too much double work.
   272         final Block body = functionNode.getBody();
   273         lc.push(body);
   274         try {
   275             if (functionNode.usesSelfSymbol()) {
   276                 // "var fn = :callee"
   277                 syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
   278             }
   280             if (functionNode.needsArguments()) {
   281                 // "var arguments = :arguments"
   282                 syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
   283                         ARGUMENTS, functionNode));
   284             }
   286             if (syntheticInitializers.isEmpty()) {
   287                 return functionNode;
   288             }
   290             for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
   291                 it.set((VarNode)it.next().accept(this));
   292             }
   293         } finally {
   294             lc.pop(body);
   295         }
   297         final List<Statement> stmts = body.getStatements();
   298         final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
   299         newStatements.addAll(syntheticInitializers);
   300         newStatements.addAll(stmts);
   301         return functionNode.setBody(lc, body.setStatements(lc, newStatements));
   302     }
   304     /**
   305      * Defines a new symbol in the given block.
   306      *
   307      * @param block        the block in which to define the symbol
   308      * @param name         name of symbol.
   309      * @param origin       origin node
   310      * @param symbolFlags  Symbol flags.
   311      *
   312      * @return Symbol for given name or null for redefinition.
   313      */
   314     private Symbol defineSymbol(final Block block, final String name, final Node origin, final int symbolFlags) {
   315         int    flags  = symbolFlags;
   316         final boolean isBlockScope = (flags & IS_LET) != 0 || (flags & IS_CONST) != 0;
   317         final boolean isGlobal     = (flags & KINDMASK) == IS_GLOBAL;
   319         Symbol symbol;
   320         final FunctionNode function;
   321         if (isBlockScope) {
   322             // block scoped variables always live in current block, no need to look for existing symbols in parent blocks.
   323             symbol = block.getExistingSymbol(name);
   324             function = lc.getCurrentFunction();
   325         } else {
   326             symbol = findSymbol(block, name);
   327             function = lc.getFunction(block);
   328         }
   330         // Global variables are implicitly always scope variables too.
   331         if (isGlobal) {
   332             flags |= IS_SCOPE;
   333         }
   335         if (lc.getCurrentFunction().isProgram()) {
   336             flags |= IS_PROGRAM_LEVEL;
   337         }
   339         final boolean isParam = (flags & KINDMASK) == IS_PARAM;
   340         final boolean isVar =   (flags & KINDMASK) == IS_VAR;
   342         if (symbol != null) {
   343             // Symbol was already defined. Check if it needs to be redefined.
   344             if (isParam) {
   345                 if (!isLocal(function, symbol)) {
   346                     // Not defined in this function. Create a new definition.
   347                     symbol = null;
   348                 } else if (symbol.isParam()) {
   349                     // Duplicate parameter. Null return will force an error.
   350                     throw new AssertionError("duplicate parameter");
   351                 }
   352             } else if (isVar) {
   353                 if (isBlockScope) {
   354                     // Check redeclaration in same block
   355                     if (symbol.hasBeenDeclared()) {
   356                         throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
   357                     } else {
   358                         symbol.setHasBeenDeclared();
   359                     }
   360                 } else if ((flags & IS_INTERNAL) != 0) {
   361                     // Always create a new definition.
   362                     symbol = null;
   363                 } else {
   364                     // Found LET or CONST in parent scope of same function - s SyntaxError
   365                     if (symbol.isBlockScoped() && isLocal(lc.getCurrentFunction(), symbol)) {
   366                         throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
   367                     }
   368                     // Not defined in this function. Create a new definition.
   369                     if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
   370                         symbol = null;
   371                     }
   372                 }
   373             }
   374         }
   376         if (symbol == null) {
   377             // If not found, then create a new one.
   378             final Block symbolBlock;
   380             // Determine where to create it.
   381             if (isVar && ((flags & IS_INTERNAL) != 0 || isBlockScope)) {
   382                 symbolBlock = block; //internal vars are always defined in the block closest to them
   383             } else if (isGlobal) {
   384                 symbolBlock = lc.getOutermostFunction().getBody();
   385             } else {
   386                 symbolBlock = lc.getFunctionBody(function);
   387             }
   389             // Create and add to appropriate block.
   390             symbol = createSymbol(name, flags);
   391             symbolBlock.putSymbol(lc, symbol);
   393             if ((flags & IS_SCOPE) == 0) {
   394                 // Initial assumption; symbol can lose its slot later
   395                 symbol.setNeedsSlot(true);
   396             }
   397         } else if (symbol.less(flags)) {
   398             symbol.setFlags(flags);
   399         }
   401         return symbol;
   402     }
   404     private <T extends Node> T end(final T node) {
   405         return end(node, true);
   406     }
   408     private <T extends Node> T end(final T node, final boolean printNode) {
   409         if (debug) {
   410             final StringBuilder sb = new StringBuilder();
   412             sb.append("[LEAVE ").
   413                 append(name(node)).
   414                 append("] ").
   415                 append(printNode ? node.toString() : "").
   416                 append(" in '").
   417                 append(lc.getCurrentFunction().getName()).
   418                 append('\'');
   420             if (node instanceof IdentNode) {
   421                 final Symbol symbol = ((IdentNode)node).getSymbol();
   422                 if (symbol == null) {
   423                     sb.append(" <NO SYMBOL>");
   424                 } else {
   425                     sb.append(" <symbol=").append(symbol).append('>');
   426                 }
   427             }
   429             log.unindent();
   430             log.info(sb);
   431         }
   433         return node;
   434     }
   436     @Override
   437     public boolean enterBlock(final Block block) {
   438         start(block);
   440         if (lc.isFunctionBody()) {
   441             block.clearSymbols();
   442             final FunctionNode fn = lc.getCurrentFunction();
   443             if (isUnparsedFunction(fn)) {
   444                 // It's a skipped nested function. Just mark the symbols being used by it as being in use.
   445                 for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
   446                     nameIsUsed(name, null);
   447                 }
   448                 // Don't bother descending into it, it must be empty anyway.
   449                 assert block.getStatements().isEmpty();
   450                 return false;
   451             }
   453             enterFunctionBody();
   454         }
   456         return true;
   457     }
   459     private boolean isUnparsedFunction(final FunctionNode fn) {
   460         return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
   461     }
   463     @Override
   464     public boolean enterCatchNode(final CatchNode catchNode) {
   465         final IdentNode exception = catchNode.getException();
   466         final Block     block     = lc.getCurrentBlock();
   468         start(catchNode);
   470         // define block-local exception variable
   471         final String exname = exception.getName();
   472         // If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
   473         // symbol is naturally internal, and should be treated as such.
   474         final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
   475         // IS_LET flag is required to make sure symbol is not visible outside catch block. However, we need to
   476         // clear the IS_LET flag after creation to allow redefinition of symbol inside the catch block.
   477         final Symbol symbol = defineSymbol(block, exname, catchNode, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
   478         symbol.clearFlag(IS_LET);
   480         return true;
   481     }
   483     private void enterFunctionBody() {
   484         final FunctionNode functionNode = lc.getCurrentFunction();
   485         final Block body = lc.getCurrentBlock();
   487         initFunctionWideVariables(functionNode, body);
   489         if (!functionNode.isProgram() && !functionNode.isDeclared() && !functionNode.isAnonymous()) {
   490             // It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
   491             // anonymous.
   492             final String name = functionNode.getIdent().getName();
   493             assert name != null;
   494             assert body.getExistingSymbol(name) == null;
   495             defineSymbol(body, name, functionNode, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
   496             if(functionNode.allVarsInScope()) { // basically, has deep eval
   497                 lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
   498             }
   499         }
   501         acceptDeclarations(functionNode, body);
   502     }
   504     @Override
   505     public boolean enterFunctionNode(final FunctionNode functionNode) {
   506         start(functionNode, false);
   508         thisProperties.push(new HashSet<String>());
   510         // Every function has a body, even the ones skipped on reparse (they have an empty one). We're
   511         // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
   512         // are used in them.
   513         assert functionNode.getBody() != null;
   515         return true;
   516     }
   518     @Override
   519     public boolean enterVarNode(final VarNode varNode) {
   520         start(varNode);
   521         // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function
   522         // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the
   523         // body of the declared function for self-reference.
   524         if (varNode.isFunctionDeclaration()) {
   525             defineVarIdent(varNode);
   526         }
   527         return true;
   528     }
   530     @Override
   531     public Node leaveVarNode(final VarNode varNode) {
   532         if (!varNode.isFunctionDeclaration()) {
   533             defineVarIdent(varNode);
   534         }
   535         return super.leaveVarNode(varNode);
   536     }
   538     private void defineVarIdent(final VarNode varNode) {
   539         final IdentNode ident = varNode.getName();
   540         final int flags;
   541         if (varNode.isAnonymousFunctionDeclaration()) {
   542             flags = IS_INTERNAL;
   543         } else if (lc.getCurrentFunction().isProgram()) {
   544             flags = IS_SCOPE;
   545         } else {
   546             flags = 0;
   547         }
   548         defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags);
   549     }
   551     private Symbol exceptionSymbol() {
   552         return newObjectInternal(EXCEPTION_PREFIX);
   553     }
   555     /**
   556      * This has to run before fix assignment types, store any type specializations for
   557      * parameters, then turn them into objects for the generic version of this method.
   558      *
   559      * @param functionNode functionNode
   560      */
   561     private FunctionNode finalizeParameters(final FunctionNode functionNode) {
   562         final List<IdentNode> newParams = new ArrayList<>();
   563         final boolean isVarArg = functionNode.isVarArg();
   565         final Block body = functionNode.getBody();
   566         for (final IdentNode param : functionNode.getParameters()) {
   567             final Symbol paramSymbol = body.getExistingSymbol(param.getName());
   568             assert paramSymbol != null;
   569             assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
   570             newParams.add(param.setSymbol(paramSymbol));
   572             // parameters should not be slots for a function that uses variable arity signature
   573             if (isVarArg) {
   574                 paramSymbol.setNeedsSlot(false);
   575             }
   576         }
   578         return functionNode.setParameters(lc, newParams);
   579     }
   581     /**
   582      * Search for symbol in the lexical context starting from the given block.
   583      * @param name Symbol name.
   584      * @return Found symbol or null if not found.
   585      */
   586     private Symbol findSymbol(final Block block, final String name) {
   587         for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
   588             final Symbol symbol = blocks.next().getExistingSymbol(name);
   589             if (symbol != null) {
   590                 return symbol;
   591             }
   592         }
   593         return null;
   594     }
   596     /**
   597      * Marks the current function as one using any global symbol. The function and all its parent functions will all be
   598      * marked as needing parent scope.
   599      * @see FunctionNode#needsParentScope()
   600      */
   601     private void functionUsesGlobalSymbol() {
   602         for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
   603             lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
   604         }
   605     }
   607     /**
   608      * Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
   609      * its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
   610      * functions up to (but not including) the function containing the defining block will be marked as needing parent
   611      * function scope.
   612      * @see FunctionNode#needsParentScope()
   613      */
   614     private void functionUsesScopeSymbol(final Symbol symbol) {
   615         final String name = symbol.getName();
   616         for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
   617             final LexicalContextNode node = contextNodeIter.next();
   618             if (node instanceof Block) {
   619                 final Block block = (Block)node;
   620                 if (block.getExistingSymbol(name) != null) {
   621                     assert lc.contains(block);
   622                     lc.setBlockNeedsScope(block);
   623                     break;
   624                 }
   625             } else if (node instanceof FunctionNode) {
   626                 lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
   627             }
   628         }
   629     }
   631     /**
   632      * Declares that the current function is using the symbol.
   633      * @param symbol the symbol used by the current function.
   634      */
   635     private void functionUsesSymbol(final Symbol symbol) {
   636         assert symbol != null;
   637         if (symbol.isScope()) {
   638             if (symbol.isGlobal()) {
   639                 functionUsesGlobalSymbol();
   640             } else {
   641                 functionUsesScopeSymbol(symbol);
   642             }
   643         } else {
   644             assert !symbol.isGlobal(); // Every global is also scope
   645         }
   646     }
   648     private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
   649         defineSymbol(block, cc.symbolName(), null, flags).setNeedsSlot(true);
   650     }
   652     private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
   653         initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
   654         initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
   656         if (functionNode.isVarArg()) {
   657             initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
   658             if (functionNode.needsArguments()) {
   659                 initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
   660                 defineSymbol(body, ARGUMENTS_VAR.symbolName(), null, IS_VAR | HAS_OBJECT_VALUE);
   661             }
   662         }
   664         initParameters(functionNode, body);
   665         initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
   666         initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
   667     }
   669     /**
   670      * Initialize parameters for function node.
   671      * @param functionNode the function node
   672      */
   673     private void initParameters(final FunctionNode functionNode, final Block body) {
   674         final boolean isVarArg = functionNode.isVarArg();
   675         final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
   676         for (final IdentNode param : functionNode.getParameters()) {
   677             final Symbol symbol = defineSymbol(body, param.getName(), param, IS_PARAM);
   678             if(scopeParams) {
   679                 // NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
   680                 // It will force creation of scopes where they would otherwise not necessarily be needed (functions
   681                 // using arguments object and other variable arity functions). Tracked by JDK-8038942.
   682                 symbol.setIsScope();
   683                 assert symbol.hasSlot();
   684                 if(isVarArg) {
   685                     symbol.setNeedsSlot(false);
   686                 }
   687             }
   688         }
   689     }
   691     /**
   692      * Is the symbol local to (that is, defined in) the specified function?
   693      * @param function the function
   694      * @param symbol the symbol
   695      * @return true if the symbol is defined in the specified function
   696      */
   697     private boolean isLocal(final FunctionNode function, final Symbol symbol) {
   698         final FunctionNode definingFn = lc.getDefiningFunction(symbol);
   699         assert definingFn != null;
   700         return definingFn == function;
   701     }
   703     private void checkConstAssignment(final IdentNode ident) {
   704         // Check for reassignment of constant
   705         final Symbol symbol = ident.getSymbol();
   706         if (symbol.isConst()) {
   707             throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
   708         }
   709     }
   711     @Override
   712     public Node leaveBinaryNode(final BinaryNode binaryNode) {
   713         if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) {
   714             checkConstAssignment((IdentNode) binaryNode.lhs());
   715         }
   716         switch (binaryNode.tokenType()) {
   717         case ASSIGN:
   718             return leaveASSIGN(binaryNode);
   719         default:
   720             return super.leaveBinaryNode(binaryNode);
   721         }
   722     }
   724     private Node leaveASSIGN(final BinaryNode binaryNode) {
   725         // If we're assigning a property of the this object ("this.foo = ..."), record it.
   726         final Expression lhs = binaryNode.lhs();
   727         if (lhs instanceof AccessNode) {
   728             final AccessNode accessNode = (AccessNode) lhs;
   729             final Expression base = accessNode.getBase();
   730             if (base instanceof IdentNode) {
   731                 final Symbol symbol = ((IdentNode)base).getSymbol();
   732                 if(symbol.isThis()) {
   733                     thisProperties.peek().add(accessNode.getProperty());
   734                 }
   735             }
   736         }
   737         return binaryNode;
   738     }
   740     @Override
   741     public Node leaveUnaryNode(final UnaryNode unaryNode) {
   742         if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) {
   743             checkConstAssignment((IdentNode) unaryNode.getExpression());
   744         }
   745         switch (unaryNode.tokenType()) {
   746         case DELETE:
   747             return leaveDELETE(unaryNode);
   748         case TYPEOF:
   749             return leaveTYPEOF(unaryNode);
   750         default:
   751             return super.leaveUnaryNode(unaryNode);
   752         }
   753     }
   755     @Override
   756     public Node leaveBlock(final Block block) {
   757         // It's not necessary to guard the marking of symbols as locals with this "if" condition for
   758         // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
   759         // is not an on-demand optimistic compilation, so we can skip locals marking then.
   760         if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
   761             // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
   762             // compilation, and we're skipping parsing the function bodies for nested functions, this
   763             // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
   764             // symbol in the outer function named the same as one of the parameters, though.
   765             if (lc.getFunction(block) == lc.getOutermostFunction()) {
   766                 for (final Symbol symbol: block.getSymbols()) {
   767                     if (!symbol.isScope()) {
   768                         assert symbol.isVar() || symbol.isParam();
   769                         compiler.declareLocalSymbol(symbol.getName());
   770                     }
   771                 }
   772             }
   773         }
   774         return block;
   775     }
   777     private Node leaveDELETE(final UnaryNode unaryNode) {
   778         final FunctionNode currentFunctionNode = lc.getCurrentFunction();
   779         final boolean      strictMode          = currentFunctionNode.isStrict();
   780         final Expression   rhs                 = unaryNode.getExpression();
   781         final Expression   strictFlagNode      = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
   783         Request request = Request.DELETE;
   784         final List<Expression> args = new ArrayList<>();
   786         if (rhs instanceof IdentNode) {
   787             final IdentNode ident = (IdentNode)rhs;
   788             // If this is a declared variable or a function parameter, delete always fails (except for globals).
   789             final String name = ident.getName();
   790             final Symbol symbol = ident.getSymbol();
   791             final boolean failDelete = strictMode || (!symbol.isScope() && (symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel())));
   793             if (failDelete && symbol.isThis()) {
   794                 return LiteralNode.newInstance(unaryNode, true).accept(this);
   795             }
   796             final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
   798             if (!failDelete) {
   799                 args.add(compilerConstantIdentifier(SCOPE));
   800             }
   801             args.add(literalNode);
   802             args.add(strictFlagNode);
   804             if (failDelete) {
   805                 request = Request.FAIL_DELETE;
   806             }
   807         } else if (rhs instanceof AccessNode) {
   808             final Expression base     = ((AccessNode)rhs).getBase();
   809             final String     property = ((AccessNode)rhs).getProperty();
   811             args.add(base);
   812             args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
   813             args.add(strictFlagNode);
   815         } else if (rhs instanceof IndexNode) {
   816             final IndexNode indexNode = (IndexNode)rhs;
   817             final Expression base  = indexNode.getBase();
   818             final Expression index = indexNode.getIndex();
   820             args.add(base);
   821             args.add(index);
   822             args.add(strictFlagNode);
   824         } else {
   825             return LiteralNode.newInstance(unaryNode, true).accept(this);
   826         }
   827         return new RuntimeNode(unaryNode, request, args).accept(this);
   828     }
   830     @Override
   831     public Node leaveForNode(final ForNode forNode) {
   832         if (forNode.isForIn()) {
   833             forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
   834         }
   836         return end(forNode);
   837     }
   839     @Override
   840     public Node leaveFunctionNode(final FunctionNode functionNode) {
   841         final FunctionNode finalizedFunction;
   842         if (isUnparsedFunction(functionNode)) {
   843             finalizedFunction = functionNode;
   844         } else {
   845             finalizedFunction =
   846                markProgramBlock(
   847                removeUnusedSlots(
   848                createSyntheticInitializers(
   849                finalizeParameters(
   850                        lc.applyTopFlags(functionNode))))
   851                        .setThisProperties(lc, thisProperties.pop().size()));
   852         }
   853         return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
   854     }
   856     @Override
   857     public Node leaveIdentNode(final IdentNode identNode) {
   858         if (identNode.isPropertyName()) {
   859             return identNode;
   860         }
   862         final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
   864         if (!identNode.isInitializedHere()) {
   865             symbol.increaseUseCount();
   866         }
   868         IdentNode newIdentNode = identNode.setSymbol(symbol);
   870         // If a block-scoped var is used before its declaration mark it as dead.
   871         // We can only statically detect this for local vars, cross-function symbols require runtime checks.
   872         if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
   873             newIdentNode = newIdentNode.markDead();
   874         }
   876         return end(newIdentNode);
   877     }
   879     private Symbol nameIsUsed(final String name, final IdentNode origin) {
   880         final Block block = lc.getCurrentBlock();
   882         Symbol symbol = findSymbol(block, name);
   884         //If an existing symbol with the name is found, use that otherwise, declare a new one
   885         if (symbol != null) {
   886             log.info("Existing symbol = ", symbol);
   887             if (symbol.isFunctionSelf()) {
   888                 final FunctionNode functionNode = lc.getDefiningFunction(symbol);
   889                 assert functionNode != null;
   890                 assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
   891                 lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
   892             }
   894             // if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
   895             maybeForceScope(symbol);
   896         } else {
   897             log.info("No symbol exists. Declare as global: ", name);
   898             symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
   899         }
   901         functionUsesSymbol(symbol);
   902         return symbol;
   903     }
   905     @Override
   906     public Node leaveSwitchNode(final SwitchNode switchNode) {
   907         // We only need a symbol for the tag if it's not an integer switch node
   908         if(!switchNode.isInteger()) {
   909             switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
   910         }
   911         return switchNode;
   912     }
   914     @Override
   915     public Node leaveTryNode(final TryNode tryNode) {
   916         tryNode.setException(exceptionSymbol());
   917         if (tryNode.getFinallyBody() != null) {
   918             tryNode.setFinallyCatchAll(exceptionSymbol());
   919         }
   921         end(tryNode);
   923         return tryNode;
   924     }
   926     private Node leaveTYPEOF(final UnaryNode unaryNode) {
   927         final Expression rhs = unaryNode.getExpression();
   929         final List<Expression> args = new ArrayList<>();
   930         if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
   931             args.add(compilerConstantIdentifier(SCOPE));
   932             args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
   933         } else {
   934             args.add(rhs);
   935             args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
   936         }
   938         final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this);
   940         end(unaryNode);
   942         return runtimeNode;
   943     }
   945     private FunctionNode markProgramBlock(final FunctionNode functionNode) {
   946         if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
   947             return functionNode;
   948         }
   950         return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
   951     }
   953     /**
   954      * If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
   955      * promoted to a scope symbol and its block marked as needing a scope.
   956      * @param symbol the symbol that might be scoped
   957      */
   958     private void maybeForceScope(final Symbol symbol) {
   959         if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
   960             Symbol.setSymbolIsScope(lc, symbol);
   961         }
   962     }
   964     private Symbol newInternal(final CompilerConstants cc, final int flags) {
   965         return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), null, IS_VAR | IS_INTERNAL | flags); //NASHORN-73
   966     }
   968     private Symbol newObjectInternal(final CompilerConstants cc) {
   969         return newInternal(cc, HAS_OBJECT_VALUE);
   970     }
   972     private boolean start(final Node node) {
   973         return start(node, true);
   974     }
   976     private boolean start(final Node node, final boolean printNode) {
   977         if (debug) {
   978             final StringBuilder sb = new StringBuilder();
   980             sb.append("[ENTER ").
   981                 append(name(node)).
   982                 append("] ").
   983                 append(printNode ? node.toString() : "").
   984                 append(" in '").
   985                 append(lc.getCurrentFunction().getName()).
   986                 append("'");
   987             log.info(sb);
   988             log.indent();
   989         }
   991         return true;
   992     }
   994     /**
   995      * Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
   996      * be reached from the current block by traversing a function node, a split node, or a with node.
   997      * @param symbol the symbol checked for needing to be a scope symbol
   998      * @return true if the symbol has to be a scope symbol.
   999      */
  1000     private boolean symbolNeedsToBeScope(final Symbol symbol) {
  1001         if (symbol.isThis() || symbol.isInternal()) {
  1002             return false;
  1005         final FunctionNode func = lc.getCurrentFunction();
  1006         if ( func.allVarsInScope() || (!symbol.isBlockScoped() && func.isProgram())) {
  1007             return true;
  1010         boolean previousWasBlock = false;
  1011         for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
  1012             final LexicalContextNode node = it.next();
  1013             if (node instanceof FunctionNode || isSplitArray(node)) {
  1014                 // We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
  1015                 // It needs to be in scope.
  1016                 return true;
  1017             } else if (node instanceof WithNode) {
  1018                 if (previousWasBlock) {
  1019                     // We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
  1020                     // preceded by a block, this means we're currently processing its expression, not its body,
  1021                     // therefore it doesn't count.
  1022                     return true;
  1024                 previousWasBlock = false;
  1025             } else if (node instanceof Block) {
  1026                 if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
  1027                     // We reached the block that defines the symbol without reaching either the function boundary, or a
  1028                     // WithNode. The symbol need not be scoped.
  1029                     return false;
  1031                 previousWasBlock = true;
  1032             } else {
  1033                 previousWasBlock = false;
  1036         throw new AssertionError();
  1039     private static boolean isSplitArray(final LexicalContextNode expr) {
  1040         if(!(expr instanceof ArrayLiteralNode)) {
  1041             return false;
  1043         final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
  1044         return !(units == null || units.isEmpty());
  1047     private void throwParserException(final String message, final Node origin) {
  1048         if (origin == null) {
  1049             throw new ParserException(message);
  1051         final Source source = compiler.getSource();
  1052         final long token = origin.getToken();
  1053         final int line = source.getLine(origin.getStart());
  1054         final int column = source.getColumn(origin.getStart());
  1055         final String formatted = ErrorManager.format(message, source, line, column, token);
  1056         throw new ParserException(JSErrorType.SYNTAX_ERROR, formatted, source, line, column, token);

mercurial