changeset 834:1b9bd93570f8

8038945: Simplify strict undefined checks Reviewed-by: jlaskey, hannesw
author lagergren
date Tue, 01 Apr 2014 16:12:38 +0200
parents 899b6f171676
children 2aaf89857444
files src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java src/jdk/nashorn/internal/codegen/Attr.java src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/CompilationEnvironment.java src/jdk/nashorn/internal/codegen/CompilerConstants.java src/jdk/nashorn/internal/codegen/FinalizeTypes.java src/jdk/nashorn/internal/codegen/FindScopeDepths.java src/jdk/nashorn/internal/ir/RuntimeNode.java src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java test/script/basic/JDK-8038945.js test/script/basic/JDK-8038945.js.EXPECTED
diffstat 11 files changed, 374 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/internal/dynalink/support/CallSiteDescriptorFactory.java	Tue Apr 01 16:12:38 2014 +0200
@@ -86,6 +86,7 @@
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.invoke.MethodType;
+import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Collections;
@@ -103,7 +104,7 @@
  * @author Attila Szegedi
  */
 public class CallSiteDescriptorFactory {
-    private static final WeakHashMap<CallSiteDescriptor, WeakReference<CallSiteDescriptor>> publicDescs =
+    private static final WeakHashMap<CallSiteDescriptor, Reference<CallSiteDescriptor>> publicDescs =
             new WeakHashMap<>();
 
 
@@ -134,18 +135,27 @@
 
     static CallSiteDescriptor getCanonicalPublicDescriptor(final CallSiteDescriptor desc) {
         synchronized(publicDescs) {
-            final WeakReference<CallSiteDescriptor> ref = publicDescs.get(desc);
+            final Reference<CallSiteDescriptor> ref = publicDescs.get(desc);
             if(ref != null) {
                 final CallSiteDescriptor canonical = ref.get();
                 if(canonical != null) {
                     return canonical;
                 }
             }
-            publicDescs.put(desc, new WeakReference<>(desc));
+            publicDescs.put(desc, createReference(desc));
         }
         return desc;
     }
 
+    /**
+     * Override this to use a different kind of references for the cache
+     * @param desc desc
+     * @return reference
+     */
+    protected static Reference<CallSiteDescriptor> createReference(final CallSiteDescriptor desc) {
+        return new WeakReference<>(desc);
+    }
+
     private static CallSiteDescriptor createPublicCallSiteDescriptor(String[] tokenizedName, MethodType methodType) {
         final int l = tokenizedName.length;
         if(l > 0 && tokenizedName[0] == "dyn") {
--- a/src/jdk/nashorn/internal/codegen/Attr.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/Attr.java	Tue Apr 01 16:12:38 2014 +0200
@@ -1413,7 +1413,7 @@
         return leaveBinaryArithmetic(binaryNode);
     }
 
-    private Node leaveCmp(final BinaryNode binaryNode) {
+    private BinaryNode leaveCmp(final BinaryNode binaryNode) {
         //infect untyped comp with opportunistic type from other
         final Expression lhs = binaryNode.lhs();
         final Expression rhs = binaryNode.rhs();
@@ -1423,7 +1423,7 @@
         final Type widest = Type.widest(lhs.getType(), rhs.getType());
         ensureSymbol(lhs, widest);
         ensureSymbol(rhs, widest);
-        return end(ensureSymbol(binaryNode, Type.BOOLEAN));
+        return (BinaryNode)end(ensureSymbol(binaryNode, Type.BOOLEAN));
     }
 
     private boolean enterBinaryArithmetic(final BinaryNode binaryNode) {
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Apr 01 16:12:38 2014 +0200
@@ -1991,6 +1991,56 @@
         return node instanceof LiteralNode<?> && ((LiteralNode<?>) node).isNull();
     }
 
+
+    private boolean undefinedCheck(final RuntimeNode runtimeNode, final List<Expression> args) {
+        final Request request = runtimeNode.getRequest();
+
+        if (!Request.isUndefinedCheck(request)) {
+            return false;
+        }
+
+        final Expression lhs = args.get(0);
+        final Expression rhs = args.get(1);
+
+        if (!rhs.getSymbol().isScope()) {
+            return false; //disallow undefined as local var or parameter
+        }
+
+        //make sure that undefined has not been overridden or scoped as a local var
+        //between us and global
+        final CompilationEnvironment  env = compiler.getCompilationEnvironment();
+        RecompilableScriptFunctionData data = env.getScriptFunctionData(lc.getCurrentFunction().getId());
+        final RecompilableScriptFunctionData program = compiler.getCompilationEnvironment().getProgram();
+        assert data != null;
+        while (data != program) {
+            if (data.hasInternalSymbol("undefined")) {
+                return false;
+            }
+            data = data.getParent();
+        }
+
+        load(lhs);
+        if (lhs.getType().isPrimitive()) {
+            method.pop(); //throw away lhs, but it still needs to be evaluated for side effects, even if not in scope, as it can be optimistic
+            method.load(request == Request.IS_NOT_UNDEFINED);
+            method.store(runtimeNode.getSymbol());
+        } else {
+            final Label isUndefined  = new Label("ud_check_true");
+            final Label notUndefined = new Label("ud_check_false");
+            final Label end          = new Label("end");
+            method.loadUndefined(Type.OBJECT);
+            method.if_acmpeq(isUndefined);
+            method.label(notUndefined);
+            method.load(request == Request.IS_NOT_UNDEFINED);
+            method._goto(end);
+            method.label(isUndefined);
+            method.load(request == Request.IS_UNDEFINED);
+            method.label(end);
+            method.store(runtimeNode.getSymbol());
+        }
+        return true;
+    }
+
     private boolean nullCheck(final RuntimeNode runtimeNode, final List<Expression> args) {
         final Request request = runtimeNode.getRequest();
 
@@ -2078,7 +2128,7 @@
             return false;
         }
 
-        assert args.size() == 2;
+        assert args.size() == 2 : node;
         final Type returnType = node.getType();
 
         new OptimisticOperation() {
@@ -2135,7 +2185,7 @@
          *
          * TODO - remove this - Access Specializer will always know after Attr/Lower
          */
-        final List<Expression> args = runtimeNode.getArgs();
+        final List<Expression> args = new ArrayList<>(runtimeNode.getArgs());
         if (runtimeNode.isPrimitive() && !runtimeNode.isFinal() && isReducible(runtimeNode.getRequest())) {
             final Expression lhs = args.get(0);
 
@@ -2186,14 +2236,25 @@
            return false;
         }
 
-        if (!runtimeNode.isFinal() && specializationCheck(runtimeNode.getRequest(), runtimeNode, args)) {
+        if (undefinedCheck(runtimeNode, args)) {
+            return false;
+        }
+
+        final RuntimeNode newRuntimeNode;
+        if (Request.isUndefinedCheck(runtimeNode.getRequest())) {
+            newRuntimeNode = runtimeNode.setRequest(runtimeNode.getRequest() == Request.IS_UNDEFINED ? Request.EQ_STRICT : Request.NE_STRICT);
+        } else {
+            newRuntimeNode = runtimeNode;
+        }
+
+        if (!newRuntimeNode.isFinal() && specializationCheck(newRuntimeNode.getRequest(), newRuntimeNode, args)) {
            return false;
         }
 
         new OptimisticOperation() {
             @Override
             void loadStack() {
-                for (final Expression arg : args) {
+                for (final Expression arg : newRuntimeNode.getArgs()) {
                     load(arg, Type.OBJECT);
                 }
             }
@@ -2201,17 +2262,17 @@
             void consumeStack() {
                 method.invokestatic(
                         CompilerConstants.className(ScriptRuntime.class),
-                        runtimeNode.getRequest().toString(),
+                        newRuntimeNode.getRequest().toString(),
                         new FunctionSignature(
                             false,
                             false,
-                            runtimeNode.getType(),
-                            args.size()).toString());
+                            newRuntimeNode.getType(),
+                            newRuntimeNode.getArgs().size()).toString());
             }
-        }.emit(runtimeNode);
-
-        method.convert(runtimeNode.getType());
-        method.store(runtimeNode.getSymbol());
+        }.emit(newRuntimeNode);
+
+        method.convert(newRuntimeNode.getType());
+        method.store(newRuntimeNode.getSymbol());
 
         return false;
     }
--- a/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/CompilationEnvironment.java	Tue Apr 01 16:12:38 2014 +0200
@@ -477,6 +477,20 @@
         return optimistic;
     }
 
+    RecompilableScriptFunctionData getProgram() {
+        if (compiledFunction == null) {
+            return null;
+        }
+        RecompilableScriptFunctionData program = compiledFunction;
+        while (true) {
+            final RecompilableScriptFunctionData parent = program.getParent();
+            if (parent == null) {
+                return program;
+            }
+            program = parent;
+        }
+    }
+
     RecompilableScriptFunctionData getScriptFunctionData(final int functionId) {
         return compiledFunction == null ? null : compiledFunction.getScriptFunctionData(functionId);
     }
--- a/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/CompilerConstants.java	Tue Apr 01 16:12:38 2014 +0200
@@ -30,6 +30,7 @@
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.util.Iterator;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
--- a/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/FinalizeTypes.java	Tue Apr 01 16:12:38 2014 +0200
@@ -28,7 +28,6 @@
 import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
 import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-
 import jdk.nashorn.internal.ir.BinaryNode;
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.Expression;
@@ -38,6 +37,8 @@
 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
 import jdk.nashorn.internal.ir.LexicalContext;
 import jdk.nashorn.internal.ir.Node;
+import jdk.nashorn.internal.ir.RuntimeNode;
+import jdk.nashorn.internal.ir.RuntimeNode.Request;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.ir.UnaryNode;
 import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
@@ -83,6 +84,35 @@
             setModify(lc, modify == null ? null : discard(modify));
     }
 
+    private static Node createIsUndefined(final Expression parent, final Expression lhs, final Expression rhs, final Request request) {
+        if ("undefined".equals(rhs.getSymbol().getName())) {
+            return new RuntimeNode(parent, request, lhs, rhs);
+        }
+        return parent;
+    }
+
+    @Override
+    public Node leaveEQ_STRICT(final BinaryNode binaryNode) {
+        return createIsUndefined(binaryNode, binaryNode.lhs(), binaryNode.rhs(), Request.IS_UNDEFINED);
+    }
+
+    @Override
+    public Node leaveNE_STRICT(final BinaryNode binaryNode) {
+        return createIsUndefined(binaryNode, binaryNode.lhs(), binaryNode.rhs(), Request.IS_NOT_UNDEFINED);
+    }
+
+    @Override
+    public Node leaveRuntimeNode(final RuntimeNode runtimeNode) {
+        switch (runtimeNode.getRequest()) {
+        case EQ_STRICT:
+            return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), Request.IS_UNDEFINED);
+        case NE_STRICT:
+            return createIsUndefined(runtimeNode, runtimeNode.getArgs().get(0), runtimeNode.getArgs().get(1), Request.IS_NOT_UNDEFINED);
+        default:
+            return runtimeNode;
+        }
+    }
+
     @Override
     public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
         assert binaryNode.getSymbol() != null;
@@ -194,7 +224,7 @@
 
     private static Expression discard(final Expression expr) {
         if (expr.getSymbol() != null) {
-            UnaryNode discard = new UnaryNode(Token.recast(expr.getToken(), TokenType.DISCARD), expr);
+            final UnaryNode discard = new UnaryNode(Token.recast(expr.getToken(), TokenType.DISCARD), expr);
             //discard never has a symbol in the discard node - then it would be a nop
             assert !expr.isTerminal();
             return discard;
--- a/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/codegen/FindScopeDepths.java	Tue Apr 01 16:12:38 2014 +0200
@@ -33,6 +33,7 @@
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+
 import jdk.nashorn.internal.ir.Block;
 import jdk.nashorn.internal.ir.Expression;
 import jdk.nashorn.internal.ir.FunctionNode;
@@ -41,6 +42,7 @@
 import jdk.nashorn.internal.ir.Node;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
+import jdk.nashorn.internal.runtime.DebugLogger;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
 
@@ -57,6 +59,9 @@
     private final CompilationEnvironment env;
     private final Map<Integer, Map<Integer, RecompilableScriptFunctionData>> fnIdToNestedFunctions = new HashMap<>();
     private final Map<Integer, Map<String, Integer>> externalSymbolDepths = new HashMap<>();
+    private final Map<Integer, Set<String>> internalSymbols = new HashMap<>();
+
+    private final static DebugLogger LOG = new DebugLogger("scopedepths");
 
     FindScopeDepths(final Compiler compiler) {
         super(new LexicalContext());
@@ -123,7 +128,7 @@
         return null;
     }
 
-    private static Block findGlobalBlock(final LexicalContext lc, final FunctionNode fn, final Block block) {
+    private static Block findGlobalBlock(final LexicalContext lc, final Block block) {
         final Iterator<Block> iter = lc.getBlocks(block);
         Block globalBlock = null;
         while (iter.hasNext()) {
@@ -174,7 +179,8 @@
                 allocatorMap,
                 nestedFunctions,
                 compiler.getSourceURL(),
-                externalSymbolDepths.get(fnId)
+                externalSymbolDepths.get(fnId),
+                internalSymbols.get(fnId)
                 );
 
         if (lc.getOutermostFunction() != newFunctionNode) {
@@ -223,15 +229,15 @@
 
         final Map<String, Integer> internals = new HashMap<>();
 
+        final Block globalBlock = findGlobalBlock(lc, block);
+        final Block bodyBlock   = findBodyBlock(lc, fn, block);
+
+        assert globalBlock != null;
+        assert bodyBlock   != null;
+
         for (final Symbol symbol : symbols) {
             Iterator<Block> iter;
 
-            final Block globalBlock = findGlobalBlock(lc, fn, block);
-            final Block bodyBlock   = findBodyBlock(lc, fn, block);
-
-            assert globalBlock != null;
-            assert bodyBlock   != null;
-
             final int internalDepth = findInternalDepth(lc, fn, block, symbol);
             final boolean internal = internalDepth >= 0;
             if (internal) {
@@ -258,9 +264,21 @@
             }
         }
 
+        addInternalSymbols(fn, internals.keySet());
+
+        if (LOG.isEnabled()) {
+            LOG.info(fn.getName() + " internals=" + internals + " externals=" + externalSymbolDepths.get(fn.getId()));
+        }
+
         return true;
     }
 
+    private void addInternalSymbols(final FunctionNode functionNode, final Set<String> symbols) {
+        final int fnId = functionNode.getId();
+        assert internalSymbols.get(fnId) == null || internalSymbols.get(fnId).equals(symbols); //e.g. cloned finally block
+        internalSymbols.put(fnId, symbols);
+    }
+
     private void addExternalSymbol(final FunctionNode functionNode, final Symbol symbol, final int depthAtStart) {
         final int fnId = functionNode.getId();
         Map<String, Integer> depths = externalSymbolDepths.get(fnId);
@@ -268,7 +286,6 @@
             depths = new HashMap<>();
             externalSymbolDepths.put(fnId, depths);
         }
-        //System.err.println("PUT " + functionNode.getName() + " " + symbol + " " +depthAtStart);
         depths.put(symbol.getName(), depthAtStart);
     }
 }
--- a/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/ir/RuntimeNode.java	Tue Apr 01 16:12:38 2014 +0200
@@ -29,11 +29,11 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.annotations.Immutable;
 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
 import jdk.nashorn.internal.parser.TokenType;
-
 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
 
 /**
@@ -80,8 +80,10 @@
         NE_STRICT(TokenType.NE_STRICT, Type.BOOLEAN, 2, true),
         /** != operator with at least one object */
         NE(TokenType.NE, Type.BOOLEAN, 2, true),
-        /** Verify type */
-        TYPE_GUARD(TokenType.VOID, Type.INT, 2);
+        /** is undefined */
+        IS_UNDEFINED(TokenType.EQ_STRICT, Type.BOOLEAN, 2),
+        /** is not undefined */
+        IS_NOT_UNDEFINED(TokenType.NE_STRICT, Type.BOOLEAN, 2);
 
         /** token type */
         private final TokenType tokenType;
@@ -193,6 +195,17 @@
         }
 
         /**
+         * Is this an undefined check?
+         *
+         * @param request request
+         *
+         * @return true if undefined check
+         */
+        public static boolean isUndefinedCheck(final Request request) {
+            return request == IS_UNDEFINED || request == IS_NOT_UNDEFINED;
+        }
+
+        /**
          * Is this an EQ or EQ_STRICT?
          *
          * @param request a request
@@ -300,6 +313,8 @@
             case LT:
             case GE:
             case GT:
+            case IS_UNDEFINED:
+            case IS_NOT_UNDEFINED:
                 return true;
             default:
                 return false;
@@ -404,6 +419,19 @@
     }
 
     /**
+     * Reset the request for this runtime node
+     * @param request request
+     * @return new runtime node or same if same request
+     */
+    public RuntimeNode setRequest(final Request request) {
+       if (this.request == request) {
+           return this;
+       }
+       return new RuntimeNode(this, request, isFinal, args, programPoint);
+   }
+
+
+    /**
      * Is this node final - i.e. it can never be replaced with other nodes again
      * @return true if final
      */
@@ -473,7 +501,12 @@
         return Collections.unmodifiableList(args);
     }
 
-    private RuntimeNode setArgs(final List<Expression> args) {
+    /**
+     * Set the arguments of this runtime node
+     * @param args new arguments
+     * @return new runtime node, or identical if no change
+     */
+    public RuntimeNode setArgs(final List<Expression> args) {
         if (this.args == args) {
             return this;
         }
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Apr 01 11:19:32 2014 +0200
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java	Tue Apr 01 16:12:38 2014 +0200
@@ -123,6 +123,8 @@
 
     private final Map<String, Integer> externalScopeDepths;
 
+    private final Set<String> internalSymbols;
+
     private static final int GET_SET_PREFIX_LENGTH = "*et ".length();
 
     /**
@@ -135,6 +137,7 @@
      * @param nestedFunctions     nested function map
      * @param sourceURL           source URL
      * @param externalScopeDepths external scope depths
+     * @param internalSymbols     internal symbols to method, defined in its scope
      */
     public RecompilableScriptFunctionData(
         final FunctionNode functionNode,
@@ -143,7 +146,8 @@
         final PropertyMap allocatorMap,
         final Map<Integer, RecompilableScriptFunctionData> nestedFunctions,
         final String sourceURL,
-        final Map<String, Integer> externalScopeDepths) {
+        final Map<String, Integer> externalScopeDepths,
+        final Set<String> internalSymbols) {
 
         super(functionName(functionNode),
               Math.min(functionNode.getParameters().size(), MAX_ARITY),
@@ -161,8 +165,9 @@
         this.sourceURL           = sourceURL;
         this.allocatorClassName  = allocatorClassName;
         this.allocatorMap        = allocatorMap;
-        this.nestedFunctions     = nestedFunctions;//deepTraverse(nestedFunctions);
+        this.nestedFunctions     = nestedFunctions;
         this.externalScopeDepths = externalScopeDepths;
+        this.internalSymbols     = internalSymbols;
 
         for (final RecompilableScriptFunctionData nfn : nestedFunctions.values()) {
             assert nfn.getParent() == null;
@@ -171,6 +176,19 @@
     }
 
     /**
+     * Check if a symbol is internally defined in a function. For example
+     * if "undefined" is internally defined in the outermost program function,
+     * it has not been reassigned or overridden and can be optimized
+     *
+     * @param symbolName symbol name
+     * @return true if symbol is internal to this ScriptFunction
+     */
+
+    public boolean hasInternalSymbol(final String symbolName) {
+        return internalSymbols.contains(symbolName);
+    }
+
+    /**
      * Return the external symbol table
      * @param symbolName symbol name
      * @return the external symbol table with proto depths
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8038945.js	Tue Apr 01 16:12:38 2014 +0200
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8038945.js : test various undefined strict intrinsics and that they
+ * aren't erroneously applied when undefined is in any scope but global
+ * 
+ * @test
+ * @run
+ */
+
+//:program internals={print=0, f1=0, f2=0, f3=0, f4=0, undefined=0, f5=0} externals=null
+
+//f1 internals={} externals={undefined=0}
+function f1(x) {
+    return x === undefined;
+}
+
+//f2 internals={} externals=null
+function f2(x, undefined) {
+    return x === undefined;
+}
+
+//f3 internals={x=0} externals=null
+function f3(x) {
+    //f3$f3_2 internals={} externals={x=0}
+    function f3_2(undefined) {
+	return x === undefined;
+    }
+    return f3_2(17);
+}
+
+//f4 internals={x=0} externals=null
+function f4(x) {
+    //f4$f4_2 internals={} externals={x=0}
+    function f4_2() {
+	var undefined = 17;
+	return x === undefined;
+    }
+    return f4_2();
+}
+
+//f5 internals={x=0, undefined=0} externals=null
+function f5(x) {
+    var undefined = 17;
+    //f5$f5_2 internals={} externals={x=0, undefined=0}
+    function f5_2() {
+	return x === undefined;
+    }
+    return f5_2();
+}
+
+print(" 1: " + f1(17) + " === false");
+print(" 2: " + f2(17) + " === false");
+print(" 3: " + f3(17) + " === true");
+print(" 4: " + f4(17) + " === true");
+print(" 5: " + f5(17) + " === true");
+
+//recompile
+print(" 6: " + f1("17") + " === false");
+print(" 7: " + f2("17") + " === false");
+print(" 8: " + f3("17") + " === false");
+print(" 9: " + f4("17") + " === false");
+print("10: " + f5("17") + " === false");
+
+//g1 internals={} externals={undefined=0}
+function g1(x) {
+    return x !== undefined;
+}
+
+//g2 internals={} externals=null
+function g2(x, undefined) {
+    return x !== undefined;
+}
+
+//g3 internals={x=0} externals=null
+function g3(x) {
+    //g3$g3_2 internals={} externals={x=0}
+    function g3_2(undefined) {
+	return x !== undefined;
+    }
+    return g3_2(17);
+}
+
+//g4 internals={x=0} externals=null
+function g4(x) {
+    //f4$f4_2 internals={} externals={x=0}
+    function g4_2() {
+	var undefined = 17;
+	return x !== undefined;
+    }
+    return g4_2();
+}
+
+//g5 internals={x=0, undefined=0} externals=null
+function g5(x) {
+    var undefined = 17;
+    //g5$g5_2 internals={} externals={x=0, undefined=0}
+    function g5_2() {
+	return x !== undefined;
+    }
+    return g5_2();
+}
+
+print("11: " + g1(17) + " === true");
+print("12: " + g2(17) + " === true");
+print("13: " + g3(17) + " === false");
+print("14: " + g4(17) + " === false");
+print("15: " + g5(17) + " === false");
+
+//recompile
+print("16: " + g1("17") + " === true");
+print("17: " + g2("17") + " === true");
+print("18: " + g3("17") + " === true");
+print("19: " + g4("17") + " === true");
+print("20: " + g5("17") + " === true");
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8038945.js.EXPECTED	Tue Apr 01 16:12:38 2014 +0200
@@ -0,0 +1,20 @@
+ 1: false === false
+ 2: false === false
+ 3: true === true
+ 4: true === true
+ 5: true === true
+ 6: false === false
+ 7: false === false
+ 8: false === false
+ 9: false === false
+10: false === false
+11: true === true
+12: true === true
+13: false === false
+14: false === false
+15: false === false
+16: true === true
+17: true === true
+18: true === true
+19: true === true
+20: true === true