8057148: Skip nested functions on reparse

Mon, 08 Sep 2014 18:40:58 +0200

author
attila
date
Mon, 08 Sep 2014 18:40:58 +0200
changeset 994
f5be4bdd0f6e
parent 993
45f9decf4fb5
child 995
33bde22b7740

8057148: Skip nested functions on reparse
Reviewed-by: hannesw, lagergren

src/jdk/nashorn/internal/codegen/AssignSymbols.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/Block.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/FunctionNode.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/LexicalContext.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/Parser.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/parser/TokenStream.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptFunctionData.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Timing.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/tools/Shell.java file | annotate | diff | comparison | revisions
test/script/basic/optimistic_check_type.js file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Thu Sep 04 18:57:14 2014 +0200
     1.2 +++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Mon Sep 08 18:40:58 2014 +0200
     1.3 @@ -194,12 +194,12 @@
     1.4       */
     1.5      private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
     1.6          // This visitor will assign symbol to all declared variables, except "var" declarations in for loop initializers.
     1.7 -        //
     1.8          body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
     1.9              @Override
    1.10 -            public boolean enterFunctionNode(final FunctionNode nestedFn) {
    1.11 -                // Don't descend into nested functions
    1.12 -                return false;
    1.13 +            protected boolean enterDefault(final Node node) {
    1.14 +                // Don't bother visiting expressions; var is a statement, it can't be inside an expression.
    1.15 +                // This will also prevent visiting nested functions (as FunctionNode is an expression).
    1.16 +                return !(node instanceof Expression);
    1.17              }
    1.18  
    1.19              @Override
    1.20 @@ -443,12 +443,27 @@
    1.21  
    1.22          if (lc.isFunctionBody()) {
    1.23              block.clearSymbols();
    1.24 +            final FunctionNode fn = lc.getCurrentFunction();
    1.25 +            if (isUnparsedFunction(fn)) {
    1.26 +                // It's a skipped nested function. Just mark the symbols being used by it as being in use.
    1.27 +                for(final String name: compiler.getScriptFunctionData(fn.getId()).getExternalSymbolNames()) {
    1.28 +                    nameIsUsed(name, null);
    1.29 +                }
    1.30 +                // Don't bother descending into it, it must be empty anyway.
    1.31 +                assert block.getStatements().isEmpty();
    1.32 +                return false;
    1.33 +            }
    1.34 +
    1.35              enterFunctionBody();
    1.36          }
    1.37  
    1.38          return true;
    1.39      }
    1.40  
    1.41 +    private boolean isUnparsedFunction(final FunctionNode fn) {
    1.42 +        return compiler.isOnDemandCompilation() && fn != lc.getOutermostFunction();
    1.43 +    }
    1.44 +
    1.45      @Override
    1.46      public boolean enterCatchNode(final CatchNode catchNode) {
    1.47          final IdentNode exception = catchNode.getException();
    1.48 @@ -492,18 +507,13 @@
    1.49  
    1.50      @Override
    1.51      public boolean enterFunctionNode(final FunctionNode functionNode) {
    1.52 -        // TODO: once we have information on symbols used by nested functions, we can stop descending into nested
    1.53 -        // functions with on-demand compilation, e.g. add
    1.54 -        // if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) {
    1.55 -        //    return false;
    1.56 -        // }
    1.57          start(functionNode, false);
    1.58  
    1.59          thisProperties.push(new HashSet<String>());
    1.60  
    1.61 -        //an outermost function in our lexical context that is not a program
    1.62 -        //is possible - it is a function being compiled lazily
    1.63          if (functionNode.isDeclared()) {
    1.64 +            // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that
    1.65 +            // is not a program - it is a function being compiled on-demand.
    1.66              final Iterator<Block> blocks = lc.getBlocks();
    1.67              if (blocks.hasNext()) {
    1.68                  final IdentNode ident = functionNode.getIdent();
    1.69 @@ -511,6 +521,11 @@
    1.70              }
    1.71          }
    1.72  
    1.73 +        // Every function has a body, even the ones skipped on reparse (they have an empty one). We're
    1.74 +        // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that
    1.75 +        // are used in them.
    1.76 +        assert functionNode.getBody() != null;
    1.77 +
    1.78          return true;
    1.79      }
    1.80  
    1.81 @@ -533,7 +548,7 @@
    1.82  
    1.83      /**
    1.84       * This has to run before fix assignment types, store any type specializations for
    1.85 -     * paramters, then turn then to objects for the generic version of this method
    1.86 +     * parameters, then turn them into objects for the generic version of this method.
    1.87       *
    1.88       * @param functionNode functionNode
    1.89       */
    1.90 @@ -733,14 +748,20 @@
    1.91  
    1.92      @Override
    1.93      public Node leaveBlock(final Block block) {
    1.94 -        // It's not necessary to guard the marking of symbols as locals with this "if"condition for correctness, it's
    1.95 -        // just an optimization -- runtime type calculation is not used when the compilation is not an on-demand
    1.96 -        // optimistic compilation, so we can skip locals marking then.
    1.97 +        // It's not necessary to guard the marking of symbols as locals with this "if" condition for
    1.98 +        // correctness, it's just an optimization -- runtime type calculation is not used when the compilation
    1.99 +        // is not an on-demand optimistic compilation, so we can skip locals marking then.
   1.100          if (compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
   1.101 -            for (final Symbol symbol: block.getSymbols()) {
   1.102 -                if (!symbol.isScope()) {
   1.103 -                    assert symbol.isVar() || symbol.isParam();
   1.104 -                    compiler.declareLocalSymbol(symbol.getName());
   1.105 +            // OTOH, we must not declare symbols from nested functions to be locals. As we're doing on-demand
   1.106 +            // compilation, and we're skipping parsing the function bodies for nested functions, this
   1.107 +            // basically only means their parameters. It'd be enough to mistakenly declare to be a local a
   1.108 +            // symbol in the outer function named the same as one of the parameters, though.
   1.109 +            if (lc.getFunction(block) == lc.getOutermostFunction()) {
   1.110 +                for (final Symbol symbol: block.getSymbols()) {
   1.111 +                    if (!symbol.isScope()) {
   1.112 +                        assert symbol.isVar() || symbol.isParam();
   1.113 +                        compiler.declareLocalSymbol(symbol.getName());
   1.114 +                    }
   1.115                  }
   1.116              }
   1.117          }
   1.118 @@ -811,24 +832,45 @@
   1.119  
   1.120      @Override
   1.121      public Node leaveFunctionNode(final FunctionNode functionNode) {
   1.122 -
   1.123 -        return markProgramBlock(
   1.124 +        final FunctionNode finalizedFunction;
   1.125 +        if (isUnparsedFunction(functionNode)) {
   1.126 +            finalizedFunction = functionNode;
   1.127 +        } else {
   1.128 +            finalizedFunction =
   1.129 +               markProgramBlock(
   1.130                 removeUnusedSlots(
   1.131                 createSyntheticInitializers(
   1.132                 finalizeParameters(
   1.133                         lc.applyTopFlags(functionNode))))
   1.134 -                       .setThisProperties(lc, thisProperties.pop().size())
   1.135 -                       .setState(lc, CompilationState.SYMBOLS_ASSIGNED));
   1.136 +                       .setThisProperties(lc, thisProperties.pop().size()));
   1.137 +        }
   1.138 +        return finalizedFunction.setState(lc, CompilationState.SYMBOLS_ASSIGNED);
   1.139      }
   1.140  
   1.141      @Override
   1.142      public Node leaveIdentNode(final IdentNode identNode) {
   1.143 -        final String name = identNode.getName();
   1.144 -
   1.145          if (identNode.isPropertyName()) {
   1.146              return identNode;
   1.147          }
   1.148  
   1.149 +        final Symbol symbol = nameIsUsed(identNode.getName(), identNode);
   1.150 +
   1.151 +        if (!identNode.isInitializedHere()) {
   1.152 +            symbol.increaseUseCount();
   1.153 +        }
   1.154 +
   1.155 +        IdentNode newIdentNode = identNode.setSymbol(symbol);
   1.156 +
   1.157 +        // If a block-scoped var is used before its declaration mark it as dead.
   1.158 +        // We can only statically detect this for local vars, cross-function symbols require runtime checks.
   1.159 +        if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
   1.160 +            newIdentNode = newIdentNode.markDead();
   1.161 +        }
   1.162 +
   1.163 +        return end(newIdentNode);
   1.164 +    }
   1.165 +
   1.166 +    private Symbol nameIsUsed(final String name, final IdentNode origin) {
   1.167          final Block block = lc.getCurrentBlock();
   1.168  
   1.169          Symbol symbol = findSymbol(block, name);
   1.170 @@ -847,24 +889,11 @@
   1.171              maybeForceScope(symbol);
   1.172          } else {
   1.173              log.info("No symbol exists. Declare as global: ", name);
   1.174 -            symbol = defineSymbol(block, name, identNode, IS_GLOBAL | IS_SCOPE);
   1.175 +            symbol = defineSymbol(block, name, origin, IS_GLOBAL | IS_SCOPE);
   1.176          }
   1.177  
   1.178          functionUsesSymbol(symbol);
   1.179 -
   1.180 -        if (!identNode.isInitializedHere()) {
   1.181 -            symbol.increaseUseCount();
   1.182 -        }
   1.183 -
   1.184 -        IdentNode newIdentNode = identNode.setSymbol(symbol);
   1.185 -
   1.186 -        // If a block-scoped var is used before its declaration mark it as dead.
   1.187 -        // We can only statically detect this for local vars, cross-function symbols require runtime checks.
   1.188 -        if (symbol.isBlockScoped() && !symbol.hasBeenDeclared() && !identNode.isDeclaredHere() && isLocal(lc.getCurrentFunction(), symbol)) {
   1.189 -            newIdentNode = newIdentNode.markDead();
   1.190 -        }
   1.191 -
   1.192 -        return end(newIdentNode);
   1.193 +        return symbol;
   1.194      }
   1.195  
   1.196      @Override
   1.197 @@ -912,7 +941,6 @@
   1.198              return functionNode;
   1.199          }
   1.200  
   1.201 -        assert functionNode.getId() == 1;
   1.202          return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
   1.203      }
   1.204  
     2.1 --- a/src/jdk/nashorn/internal/ir/Block.java	Thu Sep 04 18:57:14 2014 +0200
     2.2 +++ b/src/jdk/nashorn/internal/ir/Block.java	Mon Sep 08 18:40:58 2014 +0200
     2.3 @@ -277,6 +277,14 @@
     2.4      }
     2.5  
     2.6      /**
     2.7 +     * Returns the number of statements in the block.
     2.8 +     * @return the number of statements in the block.
     2.9 +     */
    2.10 +    public int getStatementCount() {
    2.11 +        return statements.size();
    2.12 +    }
    2.13 +
    2.14 +    /**
    2.15       * Returns the line number of the first statement in the block.
    2.16       * @return the line number of the first statement in the block, or -1 if the block has no statements.
    2.17       */
     3.1 --- a/src/jdk/nashorn/internal/ir/FunctionNode.java	Thu Sep 04 18:57:14 2014 +0200
     3.2 +++ b/src/jdk/nashorn/internal/ir/FunctionNode.java	Mon Sep 08 18:40:58 2014 +0200
     3.3 @@ -48,6 +48,7 @@
     3.4  import jdk.nashorn.internal.ir.annotations.Ignore;
     3.5  import jdk.nashorn.internal.ir.annotations.Immutable;
     3.6  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
     3.7 +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
     3.8  import jdk.nashorn.internal.runtime.ScriptFunction;
     3.9  import jdk.nashorn.internal.runtime.Source;
    3.10  import jdk.nashorn.internal.runtime.UserAccessorProperty;
    3.11 @@ -110,8 +111,11 @@
    3.12      /** Source of entity. */
    3.13      private final Source source;
    3.14  
    3.15 -    /** Unique ID used for recompilation among other things */
    3.16 -    private final int id;
    3.17 +    /**
    3.18 +     * Opaque object representing parser state at the end of the function. Used when reparsing outer functions
    3.19 +     * to skip parsing inner functions.
    3.20 +     */
    3.21 +    private final Object endParserState;
    3.22  
    3.23      /** External function identifier. */
    3.24      @Ignore
    3.25 @@ -256,6 +260,14 @@
    3.26      /** trace callsite values in this function? */
    3.27      public static final int IS_TRACE_VALUES    = 1 << 26;
    3.28  
    3.29 +    /**
    3.30 +     * Whether this function needs the callee {@link ScriptFunction} instance passed to its code as a
    3.31 +     * parameter on invocation. Note that we aren't, in fact using this flag in function nodes.
    3.32 +     * Rather, it is always calculated (see {@link #needsCallee()}). {@link RecompilableScriptFunctionData}
    3.33 +     * will, however, cache the value of this flag.
    3.34 +     */
    3.35 +    public static final int NEEDS_CALLEE       = 1 << 27;
    3.36 +
    3.37      /** extension callsite flags mask */
    3.38      public static final int EXTENSION_CALLSITE_FLAGS = IS_PRINT_PARSE |
    3.39          IS_PRINT_LOWER_PARSE | IS_PRINT_AST | IS_PRINT_LOWER_AST |
    3.40 @@ -271,16 +283,9 @@
    3.41      /** Does this function potentially need "arguments"? Note that this is not a full test, as further negative check of REDEFINES_ARGS is needed. */
    3.42      private static final int MAYBE_NEEDS_ARGUMENTS = USES_ARGUMENTS | HAS_EVAL;
    3.43  
    3.44 -    /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval.
    3.45 -     *  We also pessimistically need a parent scope if we have lazy children that have not yet been compiled */
    3.46 +    /** Does this function need the parent scope? It needs it if either it or its descendants use variables from it, or have a deep eval. */
    3.47      private static final int NEEDS_PARENT_SCOPE = USES_ANCESTOR_SCOPE | HAS_DEEP_EVAL;
    3.48  
    3.49 -    /** Used to signify "null", e.g. if someone asks for the parent of the program node */
    3.50 -    public static final int NO_FUNCTION_ID = 0;
    3.51 -
    3.52 -    /** Where to start assigning global and unique function node ids */
    3.53 -    public static final int FIRST_FUNCTION_ID = NO_FUNCTION_ID + 1;
    3.54 -
    3.55      /** What is the return type of this function? */
    3.56      private Type returnType = Type.UNKNOWN;
    3.57  
    3.58 @@ -288,11 +293,10 @@
    3.59       * Constructor
    3.60       *
    3.61       * @param source     the source
    3.62 -     * @param id         unique id
    3.63       * @param lineNumber line number
    3.64       * @param token      token
    3.65       * @param finish     finish
    3.66 -     * @param firstToken first token of the funtion node (including the function declaration)
    3.67 +     * @param firstToken first token of the function node (including the function declaration)
    3.68       * @param namespace  the namespace
    3.69       * @param ident      the identifier
    3.70       * @param name       the name of the function
    3.71 @@ -302,7 +306,6 @@
    3.72       */
    3.73      public FunctionNode(
    3.74          final Source source,
    3.75 -        final int id,
    3.76          final int lineNumber,
    3.77          final long token,
    3.78          final int finish,
    3.79 @@ -316,7 +319,6 @@
    3.80          super(token, finish);
    3.81  
    3.82          this.source           = source;
    3.83 -        this.id               = id;
    3.84          this.lineNumber       = lineNumber;
    3.85          this.ident            = ident;
    3.86          this.name             = name;
    3.87 @@ -331,11 +333,13 @@
    3.88          this.body             = null;
    3.89          this.thisProperties   = 0;
    3.90          this.rootClass        = null;
    3.91 +        this.endParserState    = null;
    3.92      }
    3.93  
    3.94      private FunctionNode(
    3.95          final FunctionNode functionNode,
    3.96          final long lastToken,
    3.97 +        Object endParserState,
    3.98          final int flags,
    3.99          final String name,
   3.100          final Type returnType,
   3.101 @@ -347,6 +351,7 @@
   3.102          final Class<?> rootClass) {
   3.103          super(functionNode);
   3.104  
   3.105 +        this.endParserState    = endParserState;
   3.106          this.lineNumber       = functionNode.lineNumber;
   3.107          this.flags            = flags;
   3.108          this.name             = name;
   3.109 @@ -361,7 +366,6 @@
   3.110  
   3.111          // the fields below never change - they are final and assigned in constructor
   3.112          this.source          = functionNode.source;
   3.113 -        this.id              = functionNode.id;
   3.114          this.ident           = functionNode.ident;
   3.115          this.namespace       = functionNode.namespace;
   3.116          this.kind            = functionNode.kind;
   3.117 @@ -429,11 +433,11 @@
   3.118      }
   3.119  
   3.120      /**
   3.121 -     * Get the unique ID for this function
   3.122 +     * Get the unique ID for this function within the script file.
   3.123       * @return the id
   3.124       */
   3.125      public int getId() {
   3.126 -        return id;
   3.127 +        return position();
   3.128      }
   3.129  
   3.130      /**
   3.131 @@ -535,6 +539,7 @@
   3.132                  new FunctionNode(
   3.133                          this,
   3.134                          lastToken,
   3.135 +                        endParserState,
   3.136                          flags,
   3.137                          name,
   3.138                          returnType,
   3.139 @@ -606,6 +611,7 @@
   3.140                  new FunctionNode(
   3.141                          this,
   3.142                          lastToken,
   3.143 +                        endParserState,
   3.144                          flags,
   3.145                          name,
   3.146                          returnType,
   3.147 @@ -644,15 +650,24 @@
   3.148      }
   3.149  
   3.150      /**
   3.151 -     * Check if the {@code eval} keyword is used in this function
   3.152 +     * Check if this function has a call expression for the identifier "eval" (that is, {@code eval(...)}).
   3.153       *
   3.154 -     * @return true if {@code eval} is used
   3.155 +     * @return true if {@code eval} is called.
   3.156       */
   3.157      public boolean hasEval() {
   3.158          return getFlag(HAS_EVAL);
   3.159      }
   3.160  
   3.161      /**
   3.162 +     * Returns true if a function nested (directly or transitively) within this function {@link #hasEval()}.
   3.163 +     *
   3.164 +     * @return true if a nested function calls {@code eval}.
   3.165 +     */
   3.166 +    public boolean hasNestedEval() {
   3.167 +        return getFlag(HAS_NESTED_EVAL);
   3.168 +    }
   3.169 +
   3.170 +    /**
   3.171       * Get the first token for this function
   3.172       * @return the first token
   3.173       */
   3.174 @@ -741,6 +756,7 @@
   3.175                  new FunctionNode(
   3.176                          this,
   3.177                          lastToken,
   3.178 +                        endParserState,
   3.179                          flags |
   3.180                              (body.needsScope() ?
   3.181                                      FunctionNode.HAS_SCOPE_BLOCK :
   3.182 @@ -839,6 +855,7 @@
   3.183                  new FunctionNode(
   3.184                          this,
   3.185                          lastToken,
   3.186 +                        endParserState,
   3.187                          flags,
   3.188                          name,
   3.189                          returnType,
   3.190 @@ -899,6 +916,7 @@
   3.191                  new FunctionNode(
   3.192                          this,
   3.193                          lastToken,
   3.194 +                        endParserState,
   3.195                          flags,
   3.196                          name,
   3.197                          returnType,
   3.198 @@ -911,6 +929,41 @@
   3.199      }
   3.200  
   3.201      /**
   3.202 +     * Returns the end parser state for this function.
   3.203 +     * @return the end parser state for this function.
   3.204 +     */
   3.205 +    public Object getEndParserState() {
   3.206 +        return endParserState;
   3.207 +    }
   3.208 +
   3.209 +    /**
   3.210 +     * Set the end parser state for this function.
   3.211 +     * @param lc lexical context
   3.212 +     * @param endParserState the parser state to set
   3.213 +     * @return function node or a new one if state was changed
   3.214 +     */
   3.215 +    public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) {
   3.216 +        if (this.endParserState == endParserState) {
   3.217 +            return this;
   3.218 +        }
   3.219 +        return Node.replaceInLexicalContext(
   3.220 +                lc,
   3.221 +                this,
   3.222 +                new FunctionNode(
   3.223 +                        this,
   3.224 +                        lastToken,
   3.225 +                        endParserState,
   3.226 +                        flags,
   3.227 +                        name,
   3.228 +                        returnType,
   3.229 +                        compileUnit,
   3.230 +                        compilationState,
   3.231 +                        body,
   3.232 +                        parameters,
   3.233 +                        thisProperties, rootClass));
   3.234 +    }
   3.235 +
   3.236 +    /**
   3.237       * Get the name of this function
   3.238       * @return the name
   3.239       */
   3.240 @@ -934,6 +987,7 @@
   3.241                  new FunctionNode(
   3.242                          this,
   3.243                          lastToken,
   3.244 +                        endParserState,
   3.245                          flags,
   3.246                          name,
   3.247                          returnType,
   3.248 @@ -999,6 +1053,7 @@
   3.249                  new FunctionNode(
   3.250                          this,
   3.251                          lastToken,
   3.252 +                        endParserState,
   3.253                          flags,
   3.254                          name,
   3.255                          returnType,
   3.256 @@ -1077,6 +1132,7 @@
   3.257              new FunctionNode(
   3.258                  this,
   3.259                  lastToken,
   3.260 +                endParserState,
   3.261                  flags,
   3.262                  name,
   3.263                  type,
   3.264 @@ -1123,6 +1179,7 @@
   3.265                  new FunctionNode(
   3.266                          this,
   3.267                          lastToken,
   3.268 +                        endParserState,
   3.269                          flags,
   3.270                          name,
   3.271                          returnType,
   3.272 @@ -1178,6 +1235,7 @@
   3.273                  new FunctionNode(
   3.274                          this,
   3.275                          lastToken,
   3.276 +                        endParserState,
   3.277                          flags,
   3.278                          name,
   3.279                          returnType,
     4.1 --- a/src/jdk/nashorn/internal/ir/LexicalContext.java	Thu Sep 04 18:57:14 2014 +0200
     4.2 +++ b/src/jdk/nashorn/internal/ir/LexicalContext.java	Mon Sep 08 18:40:58 2014 +0200
     4.3 @@ -351,8 +351,7 @@
     4.4      }
     4.5  
     4.6      /**
     4.7 -     * Get the function for this block. If the block is itself a function
     4.8 -     * this returns identity
     4.9 +     * Get the function for this block.
    4.10       * @param block block for which to get function
    4.11       * @return function for block
    4.12       */
     5.1 --- a/src/jdk/nashorn/internal/parser/Parser.java	Thu Sep 04 18:57:14 2014 +0200
     5.2 +++ b/src/jdk/nashorn/internal/parser/Parser.java	Mon Sep 08 18:40:58 2014 +0200
     5.3 @@ -148,7 +148,7 @@
     5.4      /** to receive line information from Lexer when scanning multine literals. */
     5.5      protected final Lexer.LineInfoReceiver lineInfoReceiver;
     5.6  
     5.7 -    private int nextFunctionId;
     5.8 +    private RecompilableScriptFunctionData reparsedFunction;
     5.9  
    5.10      /**
    5.11       * Constructor
    5.12 @@ -171,7 +171,7 @@
    5.13       * @param log debug logger if one is needed
    5.14       */
    5.15      public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) {
    5.16 -        this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0, log);
    5.17 +        this(env, source, errors, strict, 0, log);
    5.18      }
    5.19  
    5.20      /**
    5.21 @@ -181,15 +181,13 @@
    5.22       * @param source  source to parse
    5.23       * @param errors  error manager
    5.24       * @param strict  parser created with strict mode enabled.
    5.25 -     * @param nextFunctionId  starting value for assigning new unique ids to function nodes
    5.26       * @param lineOffset line offset to start counting lines from
    5.27       * @param log debug logger if one is needed
    5.28       */
    5.29 -    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset, final DebugLogger log) {
    5.30 +    public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) {
    5.31          super(source, errors, strict, lineOffset);
    5.32          this.env = env;
    5.33          this.namespace = new Namespace(env.getNamespace());
    5.34 -        this.nextFunctionId    = nextFunctionId;
    5.35          this.scripting = env._scripting;
    5.36          if (this.scripting) {
    5.37              this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
    5.38 @@ -228,6 +226,16 @@
    5.39      }
    5.40  
    5.41      /**
    5.42 +     * Sets the {@link RecompilableScriptFunctionData} representing the function being reparsed (when this
    5.43 +     * parser instance is used to reparse a previously parsed function, as part of its on-demand compilation).
    5.44 +     * This will trigger various special behaviors, such as skipping nested function bodies.
    5.45 +     * @param reparsedFunction the function being reparsed.
    5.46 +     */
    5.47 +    public void setReparsedFunction(final RecompilableScriptFunctionData reparsedFunction) {
    5.48 +        this.reparsedFunction = reparsedFunction;
    5.49 +    }
    5.50 +
    5.51 +    /**
    5.52       * Execute parse and return the resulting function node.
    5.53       * Errors will be thrown and the error manager will contain information
    5.54       * if parsing should fail
    5.55 @@ -472,7 +480,6 @@
    5.56          final FunctionNode functionNode =
    5.57              new FunctionNode(
    5.58                  source,
    5.59 -                nextFunctionId++,
    5.60                  functionLine,
    5.61                  token,
    5.62                  Token.descPosition(token),
    5.63 @@ -2828,10 +2835,14 @@
    5.64          FunctionNode functionNode = null;
    5.65          long lastToken = 0L;
    5.66  
    5.67 +        final boolean parseBody;
    5.68 +        Object endParserState = null;
    5.69          try {
    5.70              // Create a new function block.
    5.71              functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine);
    5.72 -
    5.73 +            assert functionNode != null;
    5.74 +            final int functionId = functionNode.getId();
    5.75 +            parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId();
    5.76              // Nashorn extension: expression closures
    5.77              if (!env._no_syntax_extensions && type != LBRACE) {
    5.78                  /*
    5.79 @@ -2847,34 +2858,152 @@
    5.80                  assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
    5.81                  // EOL uses length field to store the line number
    5.82                  final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
    5.83 -                final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
    5.84 -                appendStatement(returnNode);
    5.85 +                // Only create the return node if we aren't skipping nested functions. Note that we aren't
    5.86 +                // skipping parsing of these extended functions; they're considered to be small anyway. Also,
    5.87 +                // they don't end with a single well known token, so it'd be very hard to get correctly (see
    5.88 +                // the note below for reasoning on skipping happening before instead of after RBRACE for
    5.89 +                // details).
    5.90 +                if (parseBody) {
    5.91 +                    final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
    5.92 +                    appendStatement(returnNode);
    5.93 +                }
    5.94                  functionNode.setFinish(lastFinish);
    5.95 -
    5.96              } else {
    5.97                  expect(LBRACE);
    5.98 -
    5.99 -                // Gather the function elements.
   5.100 -                final List<Statement> prevFunctionDecls = functionDeclarations;
   5.101 -                functionDeclarations = new ArrayList<>();
   5.102 -                try {
   5.103 -                    sourceElements(false);
   5.104 -                    addFunctionDeclarations(functionNode);
   5.105 -                } finally {
   5.106 -                    functionDeclarations = prevFunctionDecls;
   5.107 +                final int lastLexed = stream.last();
   5.108 +                if (parseBody || !skipFunctionBody(functionNode)) {
   5.109 +                    // Gather the function elements.
   5.110 +                    final List<Statement> prevFunctionDecls = functionDeclarations;
   5.111 +                    functionDeclarations = new ArrayList<>();
   5.112 +                    try {
   5.113 +                        sourceElements(false);
   5.114 +                        addFunctionDeclarations(functionNode);
   5.115 +                    } finally {
   5.116 +                        functionDeclarations = prevFunctionDecls;
   5.117 +                    }
   5.118 +
   5.119 +                    lastToken = token;
   5.120 +                    // Avoiding storing parser state if the function body was small (that is, the next token
   5.121 +                    // to be read from the token stream is before the last token lexed before we entered
   5.122 +                    // function body). That'll force the function to be reparsed instead of skipped. Skipping
   5.123 +                    // involves throwing away and recreating the lexer and the token stream, so for small
   5.124 +                    // functions it is likely more economical to not bother with skipping (both in terms of
   5.125 +                    // storing the state, and in terms of throwing away lexer and token stream).
   5.126 +                    if (parseBody && lastLexed < stream.first()) {
   5.127 +                        // Since the lexer can read ahead and lexify some number of tokens in advance and have
   5.128 +                        // them buffered in the TokenStream, we need to produce a lexer state as it was just
   5.129 +                        // before it lexified RBRACE, and not whatever is its current (quite possibly well read
   5.130 +                        // ahead) state.
   5.131 +                        endParserState = new ParserState(Token.descPosition(token), line, linePosition);
   5.132 +
   5.133 +                        // NOTE: you might wonder why do we capture/restore parser state before RBRACE instead of
   5.134 +                        // after RBRACE; after all, we could skip the below "expect(RBRACE);" if we captured the
   5.135 +                        // state after it. The reason is that RBRACE is a well-known token that we can expect and
   5.136 +                        // will never involve us getting into a weird lexer state, and as such is a great reparse
   5.137 +                        // point. Typical example of a weird lexer state after RBRACE would be:
   5.138 +                        //     function this_is_skipped() { ... } "use strict";
   5.139 +                        // because lexer is doing weird off-by-one maneuvers around string literal quotes. Instead
   5.140 +                        // of compensating for the possibility of a string literal (or similar) after RBRACE,
   5.141 +                        // we'll rather just restart parsing from this well-known, friendly token instead.
   5.142 +                    }
   5.143                  }
   5.144 -
   5.145 -                lastToken = token;
   5.146                  expect(RBRACE);
   5.147                  functionNode.setFinish(finish);
   5.148              }
   5.149          } finally {
   5.150              functionNode = restoreFunctionNode(functionNode, lastToken);
   5.151          }
   5.152 +
   5.153 +        // NOTE: we can only do alterations to the function node after restoreFunctionNode.
   5.154 +
   5.155 +        if (parseBody) {
   5.156 +            functionNode = functionNode.setEndParserState(lc, endParserState);
   5.157 +        } else if (functionNode.getBody().getStatementCount() > 0){
   5.158 +            // This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see
   5.159 +            // skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to
   5.160 +            // enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as
   5.161 +            // an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away
   5.162 +            // nested bodies early if we were supposed to skip 'em.
   5.163 +            functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null,
   5.164 +                    Collections.<Statement>emptyList()));
   5.165 +        }
   5.166 +
   5.167 +        if (reparsedFunction != null) {
   5.168 +            // We restore the flags stored in the function's ScriptFunctionData that we got when we first
   5.169 +            // eagerly parsed the code. We're doing it because some flags would be set based on the
   5.170 +            // content of the function, or even content of its nested functions, most of which are normally
   5.171 +            // skipped during an on-demand compilation.
   5.172 +            final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
   5.173 +            if (data != null) {
   5.174 +                // Data can be null if when we originally parsed the file, we removed the function declaration
   5.175 +                // as it was dead code.
   5.176 +                functionNode = functionNode.setFlags(lc, data.getFunctionFlags());
   5.177 +                // This compensates for missing markEval() in case the function contains an inner function
   5.178 +                // that contains eval(), that now we didn't discover since we skipped the inner function.
   5.179 +                if (functionNode.hasNestedEval()) {
   5.180 +                    assert functionNode.hasScopeBlock();
   5.181 +                    functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null));
   5.182 +                }
   5.183 +            }
   5.184 +        }
   5.185          printAST(functionNode);
   5.186          return functionNode;
   5.187      }
   5.188  
   5.189 +    private boolean skipFunctionBody(final FunctionNode functionNode) {
   5.190 +        if (reparsedFunction == null) {
   5.191 +            // Not reparsing, so don't skip any function body.
   5.192 +            return false;
   5.193 +        }
   5.194 +        // Skip to the RBRACE of this function, and continue parsing from there.
   5.195 +        final RecompilableScriptFunctionData data = reparsedFunction.getScriptFunctionData(functionNode.getId());
   5.196 +        if (data == null) {
   5.197 +            // Nested function is not known to the reparsed function. This can happen if the FunctionNode was
   5.198 +            // in dead code that was removed. Both FoldConstants and Lower prune dead code. In that case, the
   5.199 +            // FunctionNode was dropped before a RecompilableScriptFunctionData could've been created for it.
   5.200 +            return false;
   5.201 +        }
   5.202 +        final ParserState parserState = (ParserState)data.getEndParserState();
   5.203 +        if (parserState == null) {
   5.204 +            // The function has no stored parser state; it was deemed too small to be skipped.
   5.205 +            return false;
   5.206 +        }
   5.207 +
   5.208 +        stream.reset();
   5.209 +        lexer = parserState.createLexer(source, lexer, stream, scripting && !env._no_syntax_extensions);
   5.210 +        line = parserState.line;
   5.211 +        linePosition = parserState.linePosition;
   5.212 +        // Doesn't really matter, but it's safe to treat it as if there were a semicolon before
   5.213 +        // the RBRACE.
   5.214 +        type = SEMICOLON;
   5.215 +        k = -1;
   5.216 +        next();
   5.217 +
   5.218 +        return true;
   5.219 +    }
   5.220 +
   5.221 +    /**
   5.222 +     * Encapsulates part of the state of the parser, enough to reconstruct the state of both parser and lexer
   5.223 +     * for resuming parsing after skipping a function body.
   5.224 +     */
   5.225 +    private static class ParserState {
   5.226 +        private final int position;
   5.227 +        private final int line;
   5.228 +        private final int linePosition;
   5.229 +
   5.230 +        ParserState(final int position, final int line, final int linePosition) {
   5.231 +            this.position = position;
   5.232 +            this.line = line;
   5.233 +            this.linePosition = linePosition;
   5.234 +        }
   5.235 +
   5.236 +        Lexer createLexer(final Source source, final Lexer lexer, final TokenStream stream, final boolean scripting) {
   5.237 +            final Lexer newLexer = new Lexer(source, position, lexer.limit - position, stream, scripting);
   5.238 +            newLexer.restoreState(new Lexer.State(position, Integer.MAX_VALUE, line, -1, linePosition, SEMICOLON));
   5.239 +            return newLexer;
   5.240 +        }
   5.241 +    }
   5.242 +
   5.243      private void printAST(final FunctionNode functionNode) {
   5.244          if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
   5.245              env.getErr().println(new ASTWriter(functionNode));
   5.246 @@ -3247,6 +3376,9 @@
   5.247              } else {
   5.248                  lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL);
   5.249              }
   5.250 +            // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip
   5.251 +            // parsing a nested function. functionBody() contains code to compensate for the lack of invoking
   5.252 +            // this method when the parser skips a nested function.
   5.253              lc.setBlockNeedsScope(lc.getFunctionBody(fn));
   5.254          }
   5.255      }
     6.1 --- a/src/jdk/nashorn/internal/parser/TokenStream.java	Thu Sep 04 18:57:14 2014 +0200
     6.2 +++ b/src/jdk/nashorn/internal/parser/TokenStream.java	Mon Sep 08 18:40:58 2014 +0200
     6.3 @@ -209,4 +209,8 @@
     6.4          in = count;
     6.5          buffer = newBuffer;
     6.6      }
     6.7 +
     6.8 +    void reset() {
     6.9 +        in = out = count = base = 0;
    6.10 +    }
    6.11  }
     7.1 --- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Thu Sep 04 18:57:14 2014 +0200
     7.2 +++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Mon Sep 08 18:40:58 2014 +0200
     7.3 @@ -84,6 +84,12 @@
     7.4      /** Allocator map from makeMap() */
     7.5      private final PropertyMap allocatorMap;
     7.6  
     7.7 +    /**
     7.8 +     * Opaque object representing parser state at the end of the function. Used when reparsing outer function
     7.9 +     * to help with skipping parsing inner functions.
    7.10 +     */
    7.11 +    private final Object endParserState;
    7.12 +
    7.13      /** Code installer used for all further recompilation/specialization of this ScriptFunction */
    7.14      private transient CodeInstaller<ScriptEnvironment> installer;
    7.15  
    7.16 @@ -98,9 +104,8 @@
    7.17      /** Id to parent function if one exists */
    7.18      private RecompilableScriptFunctionData parent;
    7.19  
    7.20 -    private final boolean isDeclared;
    7.21 -    private final boolean isAnonymous;
    7.22 -    private final boolean needsCallee;
    7.23 +    /** Copy of the {@link FunctionNode} flags. */
    7.24 +    private final int functionFlags;
    7.25  
    7.26      private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
    7.27  
    7.28 @@ -136,15 +141,14 @@
    7.29  
    7.30          super(functionName(functionNode),
    7.31                Math.min(functionNode.getParameters().size(), MAX_ARITY),
    7.32 -              getFlags(functionNode));
    7.33 +              getDataFlags(functionNode));
    7.34  
    7.35          this.functionName        = functionNode.getName();
    7.36          this.lineNumber          = functionNode.getLineNumber();
    7.37 -        this.isDeclared          = functionNode.isDeclared();
    7.38 -        this.needsCallee         = functionNode.needsCallee();
    7.39 -        this.isAnonymous         = functionNode.isAnonymous();
    7.40 +        this.functionFlags       = functionNode.getFlags() | (functionNode.needsCallee() ? FunctionNode.NEEDS_CALLEE : 0);
    7.41          this.functionNodeId      = functionNode.getId();
    7.42          this.source              = functionNode.getSource();
    7.43 +        this.endParserState      = functionNode.getEndParserState();
    7.44          this.token               = tokenFor(functionNode);
    7.45          this.installer           = installer;
    7.46          this.allocatorClassName  = allocatorClassName;
    7.47 @@ -202,6 +206,24 @@
    7.48      }
    7.49  
    7.50      /**
    7.51 +     * Returns the names of all external symbols this function uses.
    7.52 +     * @return the names of all external symbols this function uses.
    7.53 +     */
    7.54 +    public Set<String> getExternalSymbolNames() {
    7.55 +        return externalScopeDepths == null ? Collections.<String>emptySet() :
    7.56 +            Collections.unmodifiableSet(externalScopeDepths.keySet());
    7.57 +    }
    7.58 +
    7.59 +    /**
    7.60 +     * Returns the opaque object representing the parser state at the end of this function's body, used to
    7.61 +     * skip parsing this function when reparsing its containing outer function.
    7.62 +     * @return the object representing the end parser state
    7.63 +     */
    7.64 +    public Object getEndParserState() {
    7.65 +        return endParserState;
    7.66 +    }
    7.67 +
    7.68 +    /**
    7.69       * Get the parent of this RecompilableScriptFunctionData. If we are
    7.70       * a nested function, we have a parent. Note that "null" return value
    7.71       * can also mean that we have a parent but it is unknown, so this can
    7.72 @@ -269,7 +291,7 @@
    7.73  
    7.74      @Override
    7.75      public boolean inDynamicContext() {
    7.76 -        return (flags & IN_DYNAMIC_CONTEXT) != 0;
    7.77 +        return getFunctionFlag(FunctionNode.IN_DYNAMIC_CONTEXT);
    7.78      }
    7.79  
    7.80      private static String functionName(final FunctionNode fn) {
    7.81 @@ -293,7 +315,7 @@
    7.82          return Token.toDesc(TokenType.FUNCTION, position, length);
    7.83      }
    7.84  
    7.85 -    private static int getFlags(final FunctionNode functionNode) {
    7.86 +    private static int getDataFlags(final FunctionNode functionNode) {
    7.87          int flags = IS_CONSTRUCTOR;
    7.88          if (functionNode.isStrict()) {
    7.89              flags |= IS_STRICT;
    7.90 @@ -307,9 +329,6 @@
    7.91          if (functionNode.isVarArg()) {
    7.92              flags |= IS_VARIABLE_ARITY;
    7.93          }
    7.94 -        if (functionNode.inDynamicContext()) {
    7.95 -            flags |= IN_DYNAMIC_CONTEXT;
    7.96 -        }
    7.97          return flags;
    7.98      }
    7.99  
   7.100 @@ -337,7 +356,6 @@
   7.101      }
   7.102  
   7.103      FunctionNode reparse() {
   7.104 -        final boolean isProgram = functionNodeId == FunctionNode.FIRST_FUNCTION_ID;
   7.105          // NOTE: If we aren't recompiling the top-level program, we decrease functionNodeId 'cause we'll have a synthetic program node
   7.106          final int descPosition = Token.descPosition(token);
   7.107          final Context context = Context.getContextTrusted();
   7.108 @@ -346,18 +364,27 @@
   7.109              source,
   7.110              new Context.ThrowErrorManager(),
   7.111              isStrict(),
   7.112 -            functionNodeId - (isProgram ? 0 : 1),
   7.113              lineNumber - 1,
   7.114              context.getLogger(Parser.class)); // source starts at line 0, so even though lineNumber is the correct declaration line, back off one to make it exclusive
   7.115  
   7.116 -        if (isAnonymous) {
   7.117 +        if (getFunctionFlag(FunctionNode.IS_ANONYMOUS)) {
   7.118              parser.setFunctionName(functionName);
   7.119          }
   7.120 +        parser.setReparsedFunction(this);
   7.121  
   7.122 -        final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition, Token.descLength(token), true);
   7.123 -        // Parser generates a program AST even if we're recompiling a single function, so when we are only recompiling a
   7.124 -        // single function, extract it from the program.
   7.125 -        return (isProgram ? program : extractFunctionFromScript(program)).setName(null, functionName);
   7.126 +        final FunctionNode program = parser.parse(CompilerConstants.PROGRAM.symbolName(), descPosition,
   7.127 +                Token.descLength(token), true);
   7.128 +        // Parser generates a program AST even if we're recompiling a single function, so when we are only
   7.129 +        // recompiling a single function, extract it from the program.
   7.130 +        return (isProgram() ? program : extractFunctionFromScript(program)).setName(null, functionName);
   7.131 +    }
   7.132 +
   7.133 +    private boolean getFunctionFlag(final int flag) {
   7.134 +        return (functionFlags & flag) != 0;
   7.135 +    }
   7.136 +
   7.137 +    private boolean isProgram() {
   7.138 +        return getFunctionFlag(FunctionNode.IS_PROGRAM);
   7.139      }
   7.140  
   7.141      TypeMap typeMap(final MethodType fnCallSiteType) {
   7.142 @@ -546,7 +573,7 @@
   7.143          assert fns.size() == 1 : "got back more than one method in recompilation";
   7.144          final FunctionNode f = fns.iterator().next();
   7.145          assert f.getId() == functionNodeId;
   7.146 -        if (!isDeclared && f.isDeclared()) {
   7.147 +        if (!getFunctionFlag(FunctionNode.IS_DECLARED) && f.isDeclared()) {
   7.148              return f.clearFlag(null, FunctionNode.IS_DECLARED);
   7.149          }
   7.150          return f;
   7.151 @@ -669,7 +696,15 @@
   7.152  
   7.153      @Override
   7.154      public boolean needsCallee() {
   7.155 -        return needsCallee;
   7.156 +        return getFunctionFlag(FunctionNode.NEEDS_CALLEE);
   7.157 +    }
   7.158 +
   7.159 +    /**
   7.160 +     * Returns the {@link FunctionNode} flags associated with this function data.
   7.161 +     * @return the {@link FunctionNode} flags associated with this function data.
   7.162 +     */
   7.163 +    public int getFunctionFlags() {
   7.164 +        return functionFlags;
   7.165      }
   7.166  
   7.167      @Override
     8.1 --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Thu Sep 04 18:57:14 2014 +0200
     8.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Mon Sep 08 18:40:58 2014 +0200
     8.3 @@ -90,8 +90,6 @@
     8.4      public static final int USES_THIS      = 1 << 4;
     8.5      /** Is this a variable arity function? */
     8.6      public static final int IS_VARIABLE_ARITY = 1 << 5;
     8.7 -    /** Is this declared in a dynamic context */
     8.8 -    public static final int IN_DYNAMIC_CONTEXT = 1 << 6;
     8.9  
    8.10      /** Flag for strict or built-in functions */
    8.11      public static final int IS_STRICT_OR_BUILTIN = IS_STRICT | IS_BUILTIN;
     9.1 --- a/src/jdk/nashorn/internal/runtime/Timing.java	Thu Sep 04 18:57:14 2014 +0200
     9.2 +++ b/src/jdk/nashorn/internal/runtime/Timing.java	Mon Sep 08 18:40:58 2014 +0200
     9.3 @@ -189,7 +189,7 @@
     9.4              maxKeyLength++;
     9.5  
     9.6              final StringBuilder sb = new StringBuilder();
     9.7 -            sb.append("Accumulated complation phase Timings:\n\n");
     9.8 +            sb.append("Accumulated compilation phase timings:\n\n");
     9.9              for (final Map.Entry<String, Long> entry : timings.entrySet()) {
    9.10                  int len;
    9.11  
    10.1 --- a/src/jdk/nashorn/tools/Shell.java	Thu Sep 04 18:57:14 2014 +0200
    10.2 +++ b/src/jdk/nashorn/tools/Shell.java	Mon Sep 08 18:40:58 2014 +0200
    10.3 @@ -246,7 +246,7 @@
    10.4  
    10.5              // For each file on the command line.
    10.6              for (final String fileName : files) {
    10.7 -                final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, FunctionNode.FIRST_FUNCTION_ID, 0, context.getLogger(Parser.class)).parse();
    10.8 +                final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors, env._strict, 0, context.getLogger(Parser.class)).parse();
    10.9  
   10.10                  if (errors.getNumberOfErrors() != 0) {
   10.11                      return COMPILATION_ERROR;
    11.1 --- a/test/script/basic/optimistic_check_type.js	Thu Sep 04 18:57:14 2014 +0200
    11.2 +++ b/test/script/basic/optimistic_check_type.js	Mon Sep 08 18:40:58 2014 +0200
    11.3 @@ -36,13 +36,18 @@
    11.4  
    11.5  // Testing conditional operator
    11.6  print(inspect("" ? b : x.a, "ternary operator"))
    11.7 -print(inspect(x.b ? b : x.a, "ternary operator"))
    11.8 -print(inspect(c ? b : a, "ternary operator"))
    11.9 -print(inspect(!c ? b : a, "ternary operator"))
   11.10 -print(inspect(d ? b : x.c, "ternary operator"))
   11.11 +var b1 = b;
   11.12 +print(inspect(x.b ? b1 : x.a, "ternary operator"))
   11.13 +var b2 = b;
   11.14 +print(inspect(c ? b2 : a, "ternary operator"))
   11.15 +var b3 = b;
   11.16 +print(inspect(!c ? b3 : a, "ternary operator"))
   11.17 +var b4 = b;
   11.18 +print(inspect(d ? b4 : x.c, "ternary operator"))
   11.19  print(inspect(x.c ? a : c, "ternary operator"))
   11.20  print(inspect(c ? d : a, "ternary operator"))
   11.21 -print(inspect(c ? +a : b, "ternary operator"))
   11.22 +var b5 = b;
   11.23 +print(inspect(c ? +a : b5, "ternary operator"))
   11.24  
   11.25  // Testing format methods
   11.26  print(inspect(b.toFixed(2), "global double toFixed()"))
   11.27 @@ -53,11 +58,14 @@
   11.28  print(inspect(trees[1], "member object"))
   11.29  trees[1] = undefined;
   11.30  print(inspect(trees[1], "member undefined"))
   11.31 -print(inspect(1 in trees ? b : a, "conditional on array member"))
   11.32 +var b6=b;
   11.33 +print(inspect(1 in trees ? b6 : a, "conditional on array member"))
   11.34  delete trees[2]
   11.35 -print(inspect(2 in trees ? b : a, "conditional on array member"))
   11.36 +var b7=b;
   11.37 +print(inspect(2 in trees ? b7 : a, "conditional on array member"))
   11.38  print(inspect(3 in trees ? trees[2]="bay" : a, "conditional on array member"))
   11.39 -print(inspect("oak" in trees ? b : a, "conditional on array member"))
   11.40 +var b8=b;
   11.41 +print(inspect("oak" in trees ? b8 : a, "conditional on array member"))
   11.42  
   11.43  // Testing nested functions and return value
   11.44  function f1() {

mercurial