8040078: Avoid repeated reading of source for cached loads

Fri, 25 Apr 2014 16:34:17 +0200

author
hannesw
date
Fri, 25 Apr 2014 16:34:17 +0200
changeset 845
cdf42b4b8226
parent 844
8f06a63adf4e
child 846
9ad26ed8cc97

8040078: Avoid repeated reading of source for cached loads
Reviewed-by: jlaskey, lagergren

src/jdk/nashorn/api/scripting/NashornScriptEngine.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/ir/debug/JSONWriter.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/NativeFunction.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Context.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/JSONFunctions.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Source.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/tools/Shell.java file | annotate | diff | comparison | revisions
test/script/trusted/JDK-8006529.js file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/codegen/CompilerTest.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/parser/ParserTest.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/runtime/ContextTest.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/runtime/SourceTest.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/test/models/SourceHelper.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Fri May 02 19:15:59 2014 +0530
     1.2 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Fri Apr 25 16:34:17 2014 +0200
     1.3 @@ -27,16 +27,14 @@
     1.4  
     1.5  import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
     1.6  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     1.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     1.8  
     1.9  import java.io.IOException;
    1.10 -import java.io.InputStream;
    1.11 -import java.io.InputStreamReader;
    1.12  import java.io.Reader;
    1.13  import java.lang.invoke.MethodHandles;
    1.14  import java.lang.reflect.Method;
    1.15  import java.lang.reflect.Modifier;
    1.16  import java.net.URL;
    1.17 -import java.nio.charset.Charset;
    1.18  import java.security.AccessControlContext;
    1.19  import java.security.AccessController;
    1.20  import java.security.Permissions;
    1.21 @@ -124,21 +122,21 @@
    1.22          }
    1.23      }
    1.24  
    1.25 -    // load engine.js and return content as a char[]
    1.26 +    // load engine.js
    1.27      @SuppressWarnings("resource")
    1.28 -    private static char[] loadEngineJSSource() {
    1.29 +    private static Source loadEngineJSSource() {
    1.30          final String script = "resources/engine.js";
    1.31          try {
    1.32 -            final InputStream is = AccessController.doPrivileged(
    1.33 -                    new PrivilegedExceptionAction<InputStream>() {
    1.34 +            return AccessController.doPrivileged(
    1.35 +                    new PrivilegedExceptionAction<Source>() {
    1.36                          @Override
    1.37 -                        public InputStream run() throws Exception {
    1.38 +                        public Source run() throws IOException {
    1.39                              final URL url = NashornScriptEngine.class.getResource(script);
    1.40 -                            return url.openStream();
    1.41 +                            return sourceFor(NashornException.ENGINE_SCRIPT_SOURCE_NAME, url);
    1.42                          }
    1.43 -                    });
    1.44 -            return Source.readFully(new InputStreamReader(is));
    1.45 -        } catch (final PrivilegedActionException | IOException e) {
    1.46 +                    }
    1.47 +            );
    1.48 +        } catch (final PrivilegedActionException e) {
    1.49              if (Context.DEBUG) {
    1.50                  e.printStackTrace();
    1.51              }
    1.52 @@ -147,7 +145,7 @@
    1.53      }
    1.54  
    1.55      // Source object for engine.js
    1.56 -    private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource());
    1.57 +    private static final Source ENGINE_SCRIPT_SRC = loadEngineJSSource();
    1.58  
    1.59      NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) {
    1.60          this(factory, DEFAULT_OPTIONS, appLoader);
    1.61 @@ -282,19 +280,14 @@
    1.62  
    1.63      private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
    1.64          try {
    1.65 -            if (reader instanceof URLReader) {
    1.66 -                final URL url = ((URLReader)reader).getURL();
    1.67 -                final Charset cs = ((URLReader)reader).getCharset();
    1.68 -                return new Source(url.toString(), url, cs);
    1.69 -            }
    1.70 -            return new Source(getScriptName(ctxt), Source.readFully(reader));
    1.71 -        } catch (final IOException e) {
    1.72 +            return sourceFor(getScriptName(ctxt), reader);
    1.73 +        } catch (IOException e) {
    1.74              throw new ScriptException(e);
    1.75          }
    1.76      }
    1.77  
    1.78      private static Source makeSource(final String src, final ScriptContext ctxt) {
    1.79 -        return new Source(getScriptName(ctxt), src);
    1.80 +        return sourceFor(getScriptName(ctxt), src);
    1.81      }
    1.82  
    1.83      private static String getScriptName(final ScriptContext ctxt) {
     2.1 --- a/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Fri May 02 19:15:59 2014 +0530
     2.2 +++ b/src/jdk/nashorn/internal/ir/debug/JSONWriter.java	Fri Apr 25 16:34:17 2014 +0200
     2.3 @@ -25,6 +25,8 @@
     2.4  
     2.5  package jdk.nashorn.internal.ir.debug;
     2.6  
     2.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     2.8 +
     2.9  import java.util.Arrays;
    2.10  import java.util.List;
    2.11  import java.util.ArrayList;
    2.12 @@ -88,7 +90,7 @@
    2.13       * @return JSON string representation of AST of the supplied code
    2.14       */
    2.15      public static String parse(final ScriptEnvironment env, final String code, final String name, final boolean includeLoc) {
    2.16 -        final Parser       parser     = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
    2.17 +        final Parser       parser     = new Parser(env, sourceFor(name, code), new Context.ThrowErrorManager(), env._strict);
    2.18          final JSONWriter   jsonWriter = new JSONWriter(includeLoc);
    2.19          try {
    2.20              final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());
     3.1 --- a/src/jdk/nashorn/internal/objects/NativeFunction.java	Fri May 02 19:15:59 2014 +0530
     3.2 +++ b/src/jdk/nashorn/internal/objects/NativeFunction.java	Fri Apr 25 16:34:17 2014 +0200
     3.3 @@ -27,6 +27,7 @@
     3.4  
     3.5  import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
     3.6  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     3.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     3.8  
     3.9  import java.util.List;
    3.10  
    3.11 @@ -257,7 +258,7 @@
    3.12      }
    3.13  
    3.14      private static void checkFunctionParameters(final String params) {
    3.15 -        final Source src = new Source("<function>", params);
    3.16 +        final Source src = sourceFor("<function>", params);
    3.17          final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
    3.18          try {
    3.19              parser.parseFormalParameterList();
    3.20 @@ -267,7 +268,7 @@
    3.21      }
    3.22  
    3.23      private static void checkFunctionBody(final String funcBody) {
    3.24 -        final Source src = new Source("<function>", funcBody);
    3.25 +        final Source src = sourceFor("<function>", funcBody);
    3.26          final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
    3.27          try {
    3.28              parser.parseFunctionBody();
     4.1 --- a/src/jdk/nashorn/internal/runtime/Context.java	Fri May 02 19:15:59 2014 +0530
     4.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java	Fri Apr 25 16:34:17 2014 +0200
     4.3 @@ -32,6 +32,7 @@
     4.4  import static jdk.nashorn.internal.lookup.Lookup.MH;
     4.5  import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
     4.6  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     4.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     4.8  
     4.9  import java.io.File;
    4.10  import java.io.IOException;
    4.11 @@ -502,7 +503,7 @@
    4.12       */
    4.13      public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
    4.14          final String  file       = (location == UNDEFINED || location == null) ? "<eval>" : location.toString();
    4.15 -        final Source  source     = new Source(file, string);
    4.16 +        final Source  source     = sourceFor(file, string);
    4.17          final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
    4.18          final Global  global = Context.getGlobal();
    4.19  
    4.20 @@ -569,7 +570,7 @@
    4.21                          public Source run() {
    4.22                              try {
    4.23                                  final URL resURL = Context.class.getResource(resource);
    4.24 -                                return (resURL != null)? new Source(srcStr, resURL) : null;
    4.25 +                                return (resURL != null)? sourceFor(srcStr, resURL) : null;
    4.26                              } catch (final IOException exp) {
    4.27                                  return null;
    4.28                              }
    4.29 @@ -601,7 +602,7 @@
    4.30              final String srcStr = (String)src;
    4.31              if (srcStr.startsWith(LOAD_CLASSPATH)) {
    4.32                  URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
    4.33 -                source = (url != null)? new Source(url.toString(), url) : null;
    4.34 +                source = (url != null)? sourceFor(url.toString(), url) : null;
    4.35              } else {
    4.36                  final File file = new File(srcStr);
    4.37                  if (srcStr.indexOf(':') != -1) {
    4.38 @@ -614,31 +615,31 @@
    4.39                          } catch (final MalformedURLException e) {
    4.40                              url = file.toURI().toURL();
    4.41                          }
    4.42 -                        source = new Source(url.toString(), url);
    4.43 +                        source = sourceFor(url.toString(), url);
    4.44                      }
    4.45                  } else if (file.isFile()) {
    4.46 -                    source = new Source(srcStr, file);
    4.47 +                    source = sourceFor(srcStr, file);
    4.48                  }
    4.49              }
    4.50          } else if (src instanceof File && ((File)src).isFile()) {
    4.51              final File file = (File)src;
    4.52 -            source = new Source(file.getName(), file);
    4.53 +            source = sourceFor(file.getName(), file);
    4.54          } else if (src instanceof URL) {
    4.55              final URL url = (URL)src;
    4.56 -            source = new Source(url.toString(), url);
    4.57 +            source = sourceFor(url.toString(), url);
    4.58          } else if (src instanceof ScriptObject) {
    4.59              final ScriptObject sobj = (ScriptObject)src;
    4.60              if (sobj.has("script") && sobj.has("name")) {
    4.61                  final String script = JSType.toString(sobj.get("script"));
    4.62                  final String name   = JSType.toString(sobj.get("name"));
    4.63 -                source = new Source(name, script);
    4.64 +                source = sourceFor(name, script);
    4.65              }
    4.66          } else if (src instanceof Map) {
    4.67              final Map<?,?> map = (Map<?,?>)src;
    4.68              if (map.containsKey("script") && map.containsKey("name")) {
    4.69                  final String script = JSType.toString(map.get("script"));
    4.70                  final String name   = JSType.toString(map.get("name"));
    4.71 -                source = new Source(name, script);
    4.72 +                source = sourceFor(name, script);
    4.73              }
    4.74          }
    4.75  
     5.1 --- a/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Fri May 02 19:15:59 2014 +0530
     5.2 +++ b/src/jdk/nashorn/internal/runtime/JSONFunctions.java	Fri Apr 25 16:34:17 2014 +0200
     5.3 @@ -39,6 +39,8 @@
     5.4  import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
     5.5  import jdk.nashorn.internal.runtime.linker.Bootstrap;
     5.6  
     5.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     5.8 +
     5.9  /**
    5.10   * Utilities used by "JSON" object implementation.
    5.11   */
    5.12 @@ -77,9 +79,7 @@
    5.13       */
    5.14      public static Object parse(final Object text, final Object reviver) {
    5.15          final String     str     = JSType.toString(text);
    5.16 -        final JSONParser parser  = new JSONParser(
    5.17 -                new Source("<json>", str),
    5.18 -                new Context.ThrowErrorManager());
    5.19 +        final JSONParser parser  = new JSONParser(sourceFor("<json>", str), new Context.ThrowErrorManager());
    5.20  
    5.21          Node node;
    5.22  
     6.1 --- a/src/jdk/nashorn/internal/runtime/Source.java	Fri May 02 19:15:59 2014 +0530
     6.2 +++ b/src/jdk/nashorn/internal/runtime/Source.java	Fri Apr 25 16:34:17 2014 +0200
     6.3 @@ -27,13 +27,16 @@
     6.4  
     6.5  import java.io.ByteArrayOutputStream;
     6.6  import java.io.File;
     6.7 +import java.io.FileNotFoundException;
     6.8  import java.io.IOError;
     6.9  import java.io.IOException;
    6.10  import java.io.InputStream;
    6.11  import java.io.Reader;
    6.12 +import java.lang.ref.WeakReference;
    6.13  import java.net.MalformedURLException;
    6.14  import java.net.URISyntaxException;
    6.15  import java.net.URL;
    6.16 +import java.net.URLConnection;
    6.17  import java.nio.charset.Charset;
    6.18  import java.nio.charset.StandardCharsets;
    6.19  import java.nio.file.Files;
    6.20 @@ -43,13 +46,19 @@
    6.21  import java.security.NoSuchAlgorithmException;
    6.22  import java.util.Arrays;
    6.23  import java.util.Objects;
    6.24 +import java.util.WeakHashMap;
    6.25 +import jdk.nashorn.api.scripting.URLReader;
    6.26  import jdk.nashorn.internal.parser.Token;
    6.27  
    6.28  /**
    6.29   * Source objects track the origin of JavaScript entities.
    6.30 - *
    6.31   */
    6.32  public final class Source {
    6.33 +
    6.34 +    private static final DebugLogger DEBUG = new DebugLogger("source");
    6.35 +    private static final int BUF_SIZE = 8 * 1024;
    6.36 +    private static final Cache CACHE = new Cache();
    6.37 +
    6.38      /**
    6.39       * Descriptive name of the source as supplied by the user. Used for error
    6.40       * reporting to the user. For example, SyntaxError will use this to print message.
    6.41 @@ -64,11 +73,8 @@
    6.42       */
    6.43      private final String base;
    6.44  
    6.45 -    /** Cached source content. */
    6.46 -    private final char[] content;
    6.47 -
    6.48 -    /** Length of source content. */
    6.49 -    private final int length;
    6.50 +    /** Source content */
    6.51 +    private final Data data;
    6.52  
    6.53      /** Cached hash code */
    6.54      private int hash;
    6.55 @@ -76,40 +82,297 @@
    6.56      /** Message digest */
    6.57      private byte[] digest;
    6.58  
    6.59 -    /** Source URL if available */
    6.60 -    private final URL url;
    6.61 +    // Do *not* make this public, ever! Trusts the URL and content.
    6.62 +    private Source(final String name, final String base, final Data data) {
    6.63 +        this.name = name;
    6.64 +        this.base = base;
    6.65 +        this.data = data;
    6.66 +    }
    6.67  
    6.68 -    private static final int BUFSIZE = 8 * 1024;
    6.69 +    private static synchronized Source sourceFor(final String name, final String base, final URLData data) throws IOException {
    6.70 +        try {
    6.71 +            final Source newSource = new Source(name, base, data);
    6.72 +            final Source existingSource = CACHE.get(newSource);
    6.73 +            if (existingSource != null) {
    6.74 +                // Force any access errors
    6.75 +                data.checkPermissionAndClose();
    6.76 +                return existingSource;
    6.77 +            } else {
    6.78 +                // All sources in cache must be fully loaded
    6.79 +                data.load();
    6.80 +                CACHE.put(newSource, newSource);
    6.81 +                return newSource;
    6.82 +            }
    6.83 +        } catch (RuntimeException e) {
    6.84 +            final Throwable cause = e.getCause();
    6.85 +            if (cause instanceof IOException) {
    6.86 +                throw (IOException) cause;
    6.87 +            }
    6.88 +            throw e;
    6.89 +        }
    6.90 +    }
    6.91  
    6.92 -    // Do *not* make this public ever! Trusts the URL and content. So has to be called
    6.93 -    // from other public constructors. Note that this can not be some init method as
    6.94 -    // we initialize final fields from here.
    6.95 -    private Source(final String name, final String base, final char[] content, final URL url) {
    6.96 -        this.name    = name;
    6.97 -        this.base    = base;
    6.98 -        this.content = content;
    6.99 -        this.length  = content.length;
   6.100 -        this.url     = url;
   6.101 +    private static class Cache extends WeakHashMap<Source, WeakReference<Source>> {
   6.102 +        public Source get(final Source key) {
   6.103 +            final WeakReference<Source> ref = super.get(key);
   6.104 +            return ref == null ? null : ref.get();
   6.105 +        }
   6.106 +
   6.107 +        public void put(final Source key, final Source value) {
   6.108 +            assert !(value.data instanceof RawData);
   6.109 +            put(key, new WeakReference<>(value));
   6.110 +        }
   6.111 +    }
   6.112 +
   6.113 +    // Wrapper to manage lazy loading
   6.114 +    private static interface Data {
   6.115 +
   6.116 +        URL url();
   6.117 +
   6.118 +        int length();
   6.119 +
   6.120 +        long lastModified();
   6.121 +
   6.122 +        char[] array();
   6.123 +    }
   6.124 +
   6.125 +    private static class RawData implements Data {
   6.126 +        private final char[] array;
   6.127 +        private int hash;
   6.128 +
   6.129 +        private RawData(final char[] array) {
   6.130 +            this.array = Objects.requireNonNull(array);
   6.131 +        }
   6.132 +
   6.133 +        private RawData(final String source) {
   6.134 +            this.array = Objects.requireNonNull(source).toCharArray();
   6.135 +        }
   6.136 +
   6.137 +        private RawData(final Reader reader) throws IOException {
   6.138 +            this(readFully(reader));
   6.139 +        }
   6.140 +
   6.141 +        @Override
   6.142 +        public int hashCode() {
   6.143 +            int h = hash;
   6.144 +            if (h == 0) {
   6.145 +                h = hash = Arrays.hashCode(array);
   6.146 +            }
   6.147 +            return h;
   6.148 +        }
   6.149 +
   6.150 +        @Override
   6.151 +        public boolean equals(Object obj) {
   6.152 +            if (this == obj) {
   6.153 +                return true;
   6.154 +            }
   6.155 +            if (obj instanceof RawData) {
   6.156 +                return Arrays.equals(array, ((RawData)obj).array);
   6.157 +            }
   6.158 +            return false;
   6.159 +        }
   6.160 +
   6.161 +        @Override
   6.162 +        public String toString() {
   6.163 +            return new String(array());
   6.164 +        }
   6.165 +
   6.166 +        @Override
   6.167 +        public URL url() {
   6.168 +            return null;
   6.169 +        }
   6.170 +
   6.171 +        @Override
   6.172 +        public int length() {
   6.173 +            return array.length;
   6.174 +        }
   6.175 +
   6.176 +        @Override
   6.177 +        public long lastModified() {
   6.178 +            return 0;
   6.179 +        }
   6.180 +
   6.181 +        @Override
   6.182 +        public char[] array() {
   6.183 +            return array;
   6.184 +        }
   6.185 +
   6.186 +
   6.187 +    }
   6.188 +
   6.189 +    private static class URLData implements Data {
   6.190 +        private final URL url;
   6.191 +        protected final Charset cs;
   6.192 +        private int hash;
   6.193 +        protected char[] array;
   6.194 +        protected int length;
   6.195 +        protected long lastModified;
   6.196 +
   6.197 +        private URLData(final URL url, final Charset cs) {
   6.198 +            this.url = Objects.requireNonNull(url);
   6.199 +            this.cs = cs;
   6.200 +        }
   6.201 +
   6.202 +        @Override
   6.203 +        public int hashCode() {
   6.204 +            int h = hash;
   6.205 +            if (h == 0) {
   6.206 +                h = hash = url.hashCode();
   6.207 +            }
   6.208 +            return h;
   6.209 +        }
   6.210 +
   6.211 +        @Override
   6.212 +        public boolean equals(Object other) {
   6.213 +            if (this == other) {
   6.214 +                return true;
   6.215 +            }
   6.216 +            if (!(other instanceof URLData)) {
   6.217 +                return false;
   6.218 +            }
   6.219 +
   6.220 +            URLData otherData = (URLData) other;
   6.221 +
   6.222 +            if (url.equals(otherData.url)) {
   6.223 +                // Make sure both have meta data loaded
   6.224 +                try {
   6.225 +                    if (isDeferred()) {
   6.226 +                        // Data in cache is always loaded, and we only compare to cached data.
   6.227 +                        assert !otherData.isDeferred();
   6.228 +                        loadMeta();
   6.229 +                    } else if (otherData.isDeferred()) {
   6.230 +                        otherData.loadMeta();
   6.231 +                    }
   6.232 +                } catch (IOException e) {
   6.233 +                    throw new RuntimeException(e);
   6.234 +                }
   6.235 +
   6.236 +                // Compare meta data
   6.237 +                return this.length == otherData.length && this.lastModified == otherData.lastModified;
   6.238 +            }
   6.239 +            return false;
   6.240 +        }
   6.241 +
   6.242 +        @Override
   6.243 +        public String toString() {
   6.244 +            return new String(array());
   6.245 +        }
   6.246 +
   6.247 +        @Override
   6.248 +        public URL url() {
   6.249 +            return url;
   6.250 +        }
   6.251 +
   6.252 +        @Override
   6.253 +        public int length() {
   6.254 +            return length;
   6.255 +        }
   6.256 +
   6.257 +        @Override
   6.258 +        public long lastModified() {
   6.259 +            return lastModified;
   6.260 +        }
   6.261 +
   6.262 +        @Override
   6.263 +        public char[] array() {
   6.264 +            assert !isDeferred();
   6.265 +            return array;
   6.266 +        }
   6.267 +
   6.268 +        boolean isDeferred() {
   6.269 +            return array == null;
   6.270 +        }
   6.271 +
   6.272 +        protected void checkPermissionAndClose() throws IOException {
   6.273 +            try (InputStream in = url.openStream()) {}
   6.274 +            debug("permission checked for ", url);
   6.275 +        }
   6.276 +
   6.277 +        protected void load() throws IOException {
   6.278 +            if (array == null) {
   6.279 +                final URLConnection c = url.openConnection();
   6.280 +                try (InputStream in = c.getInputStream()) {
   6.281 +                    array = cs == null ? readFully(in) : readFully(in, cs);
   6.282 +                    length = array.length;
   6.283 +                    lastModified = c.getLastModified();
   6.284 +                    debug("loaded content for ", url);
   6.285 +                }
   6.286 +            }
   6.287 +        }
   6.288 +
   6.289 +        protected void loadMeta() throws IOException {
   6.290 +            if (length == 0 && lastModified == 0) {
   6.291 +                final URLConnection c = url.openConnection();
   6.292 +                length = c.getContentLength();
   6.293 +                lastModified = c.getLastModified();
   6.294 +                debug("loaded metadata for ", url);
   6.295 +            }
   6.296 +        }
   6.297 +    }
   6.298 +
   6.299 +    private static class FileData extends URLData {
   6.300 +        private final File file;
   6.301 +
   6.302 +        private FileData(final File file, final Charset cs) {
   6.303 +            super(getURLFromFile(file), cs);
   6.304 +            this.file = file;
   6.305 +
   6.306 +        }
   6.307 +
   6.308 +        @Override
   6.309 +        protected void checkPermissionAndClose() throws IOException {
   6.310 +            if (!file.canRead()) {
   6.311 +                throw new FileNotFoundException(file + " (Permission Denied)");
   6.312 +            }
   6.313 +            debug("permission checked for ", file);
   6.314 +        }
   6.315 +
   6.316 +        @Override
   6.317 +        protected void loadMeta() {
   6.318 +            if (length == 0 && lastModified == 0) {
   6.319 +                length = (int) file.length();
   6.320 +                lastModified = file.lastModified();
   6.321 +                debug("loaded metadata for ", file);
   6.322 +            }
   6.323 +        }
   6.324 +
   6.325 +        @Override
   6.326 +        protected void load() throws IOException {
   6.327 +            if (array == null) {
   6.328 +                array = cs == null ? readFully(file) : readFully(file, cs);
   6.329 +                length = array.length;
   6.330 +                lastModified = file.lastModified();
   6.331 +                debug("loaded content for ", file);
   6.332 +            }
   6.333 +        }
   6.334 +    }
   6.335 +
   6.336 +    private static void debug(final Object... msg) {
   6.337 +        DEBUG.info(msg);
   6.338 +    }
   6.339 +
   6.340 +    private char[] data() {
   6.341 +        return data.array();
   6.342      }
   6.343  
   6.344      /**
   6.345 -     * Constructor
   6.346 +     * Returns an instance
   6.347       *
   6.348       * @param name    source name
   6.349       * @param content contents as char array
   6.350       */
   6.351 -    public Source(final String name, final char[] content) {
   6.352 -        this(name, baseName(name, null), content, null);
   6.353 +    public static Source sourceFor(final String name, final char[] content) {
   6.354 +        return new Source(name, baseName(name), new RawData(content));
   6.355      }
   6.356  
   6.357      /**
   6.358 -     * Constructor
   6.359 +     * Returns an instance
   6.360       *
   6.361       * @param name    source name
   6.362       * @param content contents as string
   6.363       */
   6.364 -    public Source(final String name, final String content) {
   6.365 -        this(name, content.toCharArray());
   6.366 +    public static Source sourceFor(final String name, final String content) {
   6.367 +        return new Source(name, baseName(name), new RawData(content));
   6.368      }
   6.369  
   6.370      /**
   6.371 @@ -120,8 +383,8 @@
   6.372       *
   6.373       * @throws IOException if source cannot be loaded
   6.374       */
   6.375 -    public Source(final String name, final URL url) throws IOException {
   6.376 -        this(name, baseURL(url, null), readFully(url), url);
   6.377 +    public static Source sourceFor(final String name, final URL url) throws IOException {
   6.378 +        return sourceFor(name, url, null);
   6.379      }
   6.380  
   6.381      /**
   6.382 @@ -133,8 +396,8 @@
   6.383       *
   6.384       * @throws IOException if source cannot be loaded
   6.385       */
   6.386 -    public Source(final String name, final URL url, final Charset cs) throws IOException {
   6.387 -        this(name, baseURL(url, null), readFully(url, cs), url);
   6.388 +    public static Source sourceFor(final String name, final URL url, final Charset cs) throws IOException {
   6.389 +        return sourceFor(name, baseURL(url), new URLData(url, cs));
   6.390      }
   6.391  
   6.392      /**
   6.393 @@ -145,8 +408,8 @@
   6.394       *
   6.395       * @throws IOException if source cannot be loaded
   6.396       */
   6.397 -    public Source(final String name, final File file) throws IOException {
   6.398 -        this(name, dirName(file, null), readFully(file), getURLFromFile(file));
   6.399 +    public static Source sourceFor(final String name, final File file) throws IOException {
   6.400 +        return sourceFor(name, file, null);
   6.401      }
   6.402  
   6.403      /**
   6.404 @@ -158,8 +421,25 @@
   6.405       *
   6.406       * @throws IOException if source cannot be loaded
   6.407       */
   6.408 -    public Source(final String name, final File file, final Charset cs) throws IOException {
   6.409 -        this(name, dirName(file, null), readFully(file, cs), getURLFromFile(file));
   6.410 +    public static Source sourceFor(final String name, final File file, final Charset cs) throws IOException {
   6.411 +        final File absFile = file.getAbsoluteFile();
   6.412 +        return sourceFor(name, dirName(absFile, null), new FileData(file, cs));
   6.413 +    }
   6.414 +
   6.415 +    /**
   6.416 +     * Returns an instance
   6.417 +     *
   6.418 +     * @param name source name
   6.419 +     * @param reader reader from which source can be loaded
   6.420 +     * @throws IOException if source cannot be loaded
   6.421 +     */
   6.422 +    public static Source sourceFor(final String name, final Reader reader) throws IOException {
   6.423 +        // Extract URL from URLReader to defer loading and reuse cached data if available.
   6.424 +        if (reader instanceof URLReader) {
   6.425 +            final URLReader urlReader = (URLReader) reader;
   6.426 +            return sourceFor(name, urlReader.getURL(), urlReader.getCharset());
   6.427 +        }
   6.428 +        return new Source(name, baseName(name), new RawData(reader));
   6.429      }
   6.430  
   6.431      @Override
   6.432 @@ -167,21 +447,18 @@
   6.433          if (this == obj) {
   6.434              return true;
   6.435          }
   6.436 -
   6.437          if (!(obj instanceof Source)) {
   6.438              return false;
   6.439          }
   6.440 -
   6.441 -        final Source src = (Source)obj;
   6.442 -        // Only compare content as a last resort measure
   6.443 -        return length == src.length && Objects.equals(url, src.url) && Objects.equals(name, src.name) && Arrays.equals(content, src.content);
   6.444 +        final Source other = (Source) obj;
   6.445 +        return Objects.equals(name, other.name) && data.equals(other.data);
   6.446      }
   6.447  
   6.448      @Override
   6.449      public int hashCode() {
   6.450          int h = hash;
   6.451          if (h == 0) {
   6.452 -            h = hash = Arrays.hashCode(content) ^ Objects.hashCode(name);
   6.453 +            h = hash = data.hashCode() ^ Objects.hashCode(name);
   6.454          }
   6.455          return h;
   6.456      }
   6.457 @@ -191,7 +468,7 @@
   6.458       * @return Source content.
   6.459       */
   6.460      public String getString() {
   6.461 -        return new String(content, 0, length);
   6.462 +        return data.toString();
   6.463      }
   6.464  
   6.465      /**
   6.466 @@ -203,6 +480,14 @@
   6.467      }
   6.468  
   6.469      /**
   6.470 +     * Get the last modified time of this script.
   6.471 +     * @return Last modified time.
   6.472 +     */
   6.473 +    public long getLastModified() {
   6.474 +        return data.lastModified();
   6.475 +    }
   6.476 +
   6.477 +    /**
   6.478       * Get the "directory" part of the file or "base" of the URL.
   6.479       * @return base of file or URL.
   6.480       */
   6.481 @@ -217,7 +502,7 @@
   6.482       * @return Source content portion.
   6.483       */
   6.484      public String getString(final int start, final int len) {
   6.485 -        return new String(content, start, len);
   6.486 +        return new String(data(), start, len);
   6.487      }
   6.488  
   6.489      /**
   6.490 @@ -228,7 +513,7 @@
   6.491      public String getString(final long token) {
   6.492          final int start = Token.descPosition(token);
   6.493          final int len = Token.descLength(token);
   6.494 -        return new String(content, start, len);
   6.495 +        return new String(data(), start, len);
   6.496      }
   6.497  
   6.498      /**
   6.499 @@ -238,7 +523,7 @@
   6.500       * @return URL source or null
   6.501       */
   6.502      public URL getURL() {
   6.503 -        return url;
   6.504 +        return data.url();
   6.505      }
   6.506  
   6.507      /**
   6.508 @@ -247,8 +532,9 @@
   6.509       * @return Index of first character of line.
   6.510       */
   6.511      private int findBOLN(final int position) {
   6.512 +        final char[] data = data();
   6.513          for (int i = position - 1; i > 0; i--) {
   6.514 -            final char ch = content[i];
   6.515 +            final char ch = data[i];
   6.516  
   6.517              if (ch == '\n' || ch == '\r') {
   6.518                  return i + 1;
   6.519 @@ -264,8 +550,10 @@
   6.520       * @return Index of last character of line.
   6.521       */
   6.522      private int findEOLN(final int position) {
   6.523 -         for (int i = position; i < length; i++) {
   6.524 -            final char ch = content[i];
   6.525 +        final char[] data = data();
   6.526 +        final int length = data.length;
   6.527 +        for (int i = position; i < length; i++) {
   6.528 +            final char ch = data[i];
   6.529  
   6.530              if (ch == '\n' || ch == '\r') {
   6.531                  return i - 1;
   6.532 @@ -285,11 +573,12 @@
   6.533       * @return Line number.
   6.534       */
   6.535      public int getLine(final int position) {
   6.536 +        final char[] data = data();
   6.537          // Line count starts at 1.
   6.538          int line = 1;
   6.539  
   6.540          for (int i = 0; i < position; i++) {
   6.541 -            final char ch = content[i];
   6.542 +            final char ch = data[i];
   6.543              // Works for both \n and \r\n.
   6.544              if (ch == '\n') {
   6.545                  line++;
   6.546 @@ -320,7 +609,7 @@
   6.547          // Find end of this line.
   6.548          final int last = findEOLN(position);
   6.549  
   6.550 -        return new String(content, first, last - first + 1);
   6.551 +        return new String(data(), first, last - first + 1);
   6.552      }
   6.553  
   6.554      /**
   6.555 @@ -328,7 +617,7 @@
   6.556       * @return content
   6.557       */
   6.558      public char[] getContent() {
   6.559 -        return content.clone();
   6.560 +        return data().clone();
   6.561      }
   6.562  
   6.563      /**
   6.564 @@ -336,19 +625,18 @@
   6.565       * @return length
   6.566       */
   6.567      public int getLength() {
   6.568 -        return length;
   6.569 +        return data.length();
   6.570      }
   6.571  
   6.572      /**
   6.573       * Read all of the source until end of file. Return it as char array
   6.574       *
   6.575 -     * @param reader  reader opened to source stream
   6.576 +     * @param reader reader opened to source stream
   6.577       * @return source as content
   6.578 -     *
   6.579       * @throws IOException if source could not be read
   6.580       */
   6.581      public static char[] readFully(final Reader reader) throws IOException {
   6.582 -        final char[]        arr = new char[BUFSIZE];
   6.583 +        final char[]        arr = new char[BUF_SIZE];
   6.584          final StringBuilder sb  = new StringBuilder();
   6.585  
   6.586          try {
   6.587 @@ -366,9 +654,8 @@
   6.588      /**
   6.589       * Read all of the source until end of file. Return it as char array
   6.590       *
   6.591 -     * @param file  source file
   6.592 +     * @param file source file
   6.593       * @return source as content
   6.594 -     *
   6.595       * @throws IOException if source could not be read
   6.596       */
   6.597      public static char[] readFully(final File file) throws IOException {
   6.598 @@ -381,10 +668,9 @@
   6.599      /**
   6.600       * Read all of the source until end of file. Return it as char array
   6.601       *
   6.602 -     * @param file  source file
   6.603 +     * @param file source file
   6.604       * @param cs Charset used to convert bytes to chars
   6.605       * @return source as content
   6.606 -     *
   6.607       * @throws IOException if source could not be read
   6.608       */
   6.609      public static char[] readFully(final File file, final Charset cs) throws IOException {
   6.610 @@ -393,7 +679,7 @@
   6.611          }
   6.612  
   6.613          final byte[] buf = Files.readAllBytes(file.toPath());
   6.614 -        return (cs != null)? new String(buf, cs).toCharArray() : byteToCharArray(buf);
   6.615 +        return (cs != null) ? new String(buf, cs).toCharArray() : byteToCharArray(buf);
   6.616      }
   6.617  
   6.618      /**
   6.619 @@ -401,7 +687,6 @@
   6.620       *
   6.621       * @param url URL to read content from
   6.622       * @return source as content
   6.623 -     *
   6.624       * @throws IOException if source could not be read
   6.625       */
   6.626      public static char[] readFully(final URL url) throws IOException {
   6.627 @@ -414,7 +699,6 @@
   6.628       * @param url URL to read content from
   6.629       * @param cs Charset used to convert bytes to chars
   6.630       * @return source as content
   6.631 -     *
   6.632       * @throws IOException if source could not be read
   6.633       */
   6.634      public static char[] readFully(final URL url, final Charset cs) throws IOException {
   6.635 @@ -428,7 +712,7 @@
   6.636       */
   6.637      public synchronized byte[] getDigest() {
   6.638          if (digest == null) {
   6.639 -
   6.640 +            final char[] content = data();
   6.641              final byte[] bytes = new byte[content.length * 2];
   6.642  
   6.643              for (int i = 0; i < content.length; i++) {
   6.644 @@ -444,8 +728,8 @@
   6.645                  if (base != null) {
   6.646                      md.update(base.getBytes(StandardCharsets.UTF_8));
   6.647                  }
   6.648 -                if (url != null) {
   6.649 -                    md.update(url.toString().getBytes(StandardCharsets.UTF_8));
   6.650 +                if (getURL() != null) {
   6.651 +                    md.update(getURL().toString().getBytes(StandardCharsets.UTF_8));
   6.652                  }
   6.653                  digest = md.digest(bytes);
   6.654              } catch (NoSuchAlgorithmException e) {
   6.655 @@ -461,50 +745,46 @@
   6.656       * @return base URL for url
   6.657       */
   6.658      public static String baseURL(final URL url) {
   6.659 -        return baseURL(url, null);
   6.660 -    }
   6.661 -
   6.662 -    private static String baseURL(final URL url, final String defaultValue) {
   6.663          if (url.getProtocol().equals("file")) {
   6.664              try {
   6.665                  final Path path = Paths.get(url.toURI());
   6.666                  final Path parent = path.getParent();
   6.667 -                return (parent != null) ? (parent + File.separator) : defaultValue;
   6.668 +                return (parent != null) ? (parent + File.separator) : null;
   6.669              } catch (final SecurityException | URISyntaxException | IOError e) {
   6.670 -                return defaultValue;
   6.671 +                return null;
   6.672              }
   6.673          }
   6.674  
   6.675          // FIXME: is there a better way to find 'base' URL of a given URL?
   6.676          String path = url.getPath();
   6.677          if (path.isEmpty()) {
   6.678 -            return defaultValue;
   6.679 +            return null;
   6.680          }
   6.681          path = path.substring(0, path.lastIndexOf('/') + 1);
   6.682          final int port = url.getPort();
   6.683          try {
   6.684              return new URL(url.getProtocol(), url.getHost(), port, path).toString();
   6.685          } catch (final MalformedURLException e) {
   6.686 -            return defaultValue;
   6.687 +            return null;
   6.688          }
   6.689      }
   6.690  
   6.691 -    private static String dirName(final File file, final String defaultValue) {
   6.692 +    private static String dirName(final File file, final String DEFAULT_BASE_NAME) {
   6.693          final String res = file.getParent();
   6.694 -        return (res != null)? (res + File.separator) : defaultValue;
   6.695 +        return (res != null) ? (res + File.separator) : DEFAULT_BASE_NAME;
   6.696      }
   6.697  
   6.698      // fake directory like name
   6.699 -    private static String baseName(final String name, final String defaultValue) {
   6.700 +    private static String baseName(final String name) {
   6.701          int idx = name.lastIndexOf('/');
   6.702          if (idx == -1) {
   6.703              idx = name.lastIndexOf('\\');
   6.704          }
   6.705 -        return (idx != -1)? name.substring(0, idx + 1) : defaultValue;
   6.706 +        return (idx != -1) ? name.substring(0, idx + 1) : null;
   6.707      }
   6.708  
   6.709      private static char[] readFully(final InputStream is, final Charset cs) throws IOException {
   6.710 -        return (cs != null)? new String(readBytes(is), cs).toCharArray() : readFully(is);
   6.711 +        return (cs != null) ? new String(readBytes(is), cs).toCharArray() : readFully(is);
   6.712      }
   6.713  
   6.714      private static char[] readFully(final InputStream is) throws IOException {
   6.715 @@ -515,19 +795,19 @@
   6.716          Charset cs = StandardCharsets.UTF_8;
   6.717          int start = 0;
   6.718          // BOM detection.
   6.719 -        if (bytes.length > 1 && bytes[0] == (byte)0xFE && bytes[1] == (byte)0xFF) {
   6.720 +        if (bytes.length > 1 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF) {
   6.721              start = 2;
   6.722              cs = StandardCharsets.UTF_16BE;
   6.723 -        } else if (bytes.length > 1 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE) {
   6.724 +        } else if (bytes.length > 1 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE) {
   6.725              start = 2;
   6.726              cs = StandardCharsets.UTF_16LE;
   6.727 -        } else if (bytes.length > 2 && bytes[0] == (byte)0xEF && bytes[1] == (byte)0xBB && bytes[2] == (byte)0xBF) {
   6.728 +        } else if (bytes.length > 2 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) {
   6.729              start = 3;
   6.730              cs = StandardCharsets.UTF_8;
   6.731 -        } else if (bytes.length > 3 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE && bytes[2] == 0 && bytes[3] == 0) {
   6.732 +        } else if (bytes.length > 3 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE && bytes[2] == 0 && bytes[3] == 0) {
   6.733              start = 4;
   6.734              cs = Charset.forName("UTF-32LE");
   6.735 -        } else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte)0xFE && bytes[3] == (byte)0xFF) {
   6.736 +        } else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF) {
   6.737              start = 4;
   6.738              cs = Charset.forName("UTF-32BE");
   6.739          }
   6.740 @@ -536,7 +816,7 @@
   6.741      }
   6.742  
   6.743      static byte[] readBytes(final InputStream is) throws IOException {
   6.744 -        final byte[] arr = new byte[BUFSIZE];
   6.745 +        final byte[] arr = new byte[BUF_SIZE];
   6.746          try {
   6.747              try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
   6.748                  int numBytes;
     7.1 --- a/src/jdk/nashorn/tools/Shell.java	Fri May 02 19:15:59 2014 +0530
     7.2 +++ b/src/jdk/nashorn/tools/Shell.java	Fri Apr 25 16:34:17 2014 +0200
     7.3 @@ -25,6 +25,8 @@
     7.4  
     7.5  package jdk.nashorn.tools;
     7.6  
     7.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     7.8 +
     7.9  import java.io.BufferedReader;
    7.10  import java.io.File;
    7.11  import java.io.FileReader;
    7.12 @@ -50,7 +52,6 @@
    7.13  import jdk.nashorn.internal.runtime.Property;
    7.14  import jdk.nashorn.internal.runtime.ScriptEnvironment;
    7.15  import jdk.nashorn.internal.runtime.ScriptFunction;
    7.16 -import jdk.nashorn.internal.runtime.ScriptObject;
    7.17  import jdk.nashorn.internal.runtime.ScriptRuntime;
    7.18  import jdk.nashorn.internal.runtime.Source;
    7.19  import jdk.nashorn.internal.runtime.options.Options;
    7.20 @@ -244,7 +245,7 @@
    7.21  
    7.22              // For each file on the command line.
    7.23              for (final String fileName : files) {
    7.24 -                final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse();
    7.25 +                final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors).parse();
    7.26  
    7.27                  if (errors.getNumberOfErrors() != 0) {
    7.28                      return COMPILATION_ERROR;
    7.29 @@ -302,7 +303,7 @@
    7.30                  }
    7.31  
    7.32                  final File file = new File(fileName);
    7.33 -                final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
    7.34 +                final ScriptFunction script = context.compileScript(sourceFor(fileName, file), global);
    7.35                  if (script == null || errors.getNumberOfErrors() != 0) {
    7.36                      return COMPILATION_ERROR;
    7.37                  }
    7.38 @@ -405,7 +406,7 @@
    7.39  
    7.40              // initialize with "shell.js" script
    7.41              try {
    7.42 -                final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js"));
    7.43 +                final Source source = sourceFor("<shell.js>", Shell.class.getResource("resources/shell.js"));
    7.44                  context.eval(global, source.getString(), global, "<shell.js>", false);
    7.45              } catch (final Exception e) {
    7.46                  err.println(e);
     8.1 --- a/test/script/trusted/JDK-8006529.js	Fri May 02 19:15:59 2014 +0530
     8.2 +++ b/test/script/trusted/JDK-8006529.js	Fri Apr 25 16:34:17 2014 +0200
     8.3 @@ -113,7 +113,7 @@
     8.4  var getContextMethod = Context.class.getMethod("getContext")
     8.5  var getEnvMethod = Context.class.getMethod("getEnv")
     8.6  
     8.7 -var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class)
     8.8 +var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
     8.9  var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
    8.10  var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
    8.11  
    8.12 @@ -121,7 +121,7 @@
    8.13  // source code, returns a jdk.nashorn.internal.ir.FunctionNode object 
    8.14  // representing it.
    8.15  function compile(source) {
    8.16 -    var source = SourceConstructor.newInstance("<no name>", source);
    8.17 +    var source = sourceForMethod.invoke(null, "<no name>", source);
    8.18  
    8.19      var env = getEnvMethod.invoke(getContextMethod.invoke(null))
    8.20  
     9.1 --- a/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Fri May 02 19:15:59 2014 +0530
     9.2 +++ b/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Fri Apr 25 16:34:17 2014 +0200
     9.3 @@ -25,6 +25,9 @@
     9.4  
     9.5  package jdk.nashorn.internal.codegen;
     9.6  
     9.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
     9.8 +import static jdk.nashorn.internal.runtime.Source.readFully;
     9.9 +
    9.10  import java.io.File;
    9.11  import java.io.PrintWriter;
    9.12  import java.io.StringWriter;
    9.13 @@ -32,7 +35,6 @@
    9.14  import jdk.nashorn.internal.runtime.Context;
    9.15  import jdk.nashorn.internal.runtime.ErrorManager;
    9.16  import jdk.nashorn.internal.runtime.ScriptFunction;
    9.17 -import jdk.nashorn.internal.runtime.ScriptObject;
    9.18  import jdk.nashorn.internal.runtime.Source;
    9.19  import jdk.nashorn.internal.runtime.options.Options;
    9.20  import org.testng.Assert;
    9.21 @@ -152,7 +154,7 @@
    9.22          final boolean globalChanged = (oldGlobal != global);
    9.23  
    9.24          try {
    9.25 -            final char[] buffer = Source.readFully(file);
    9.26 +            final char[] buffer = readFully(file);
    9.27              boolean excluded = false;
    9.28  
    9.29              if (filter != null) {
    9.30 @@ -171,7 +173,7 @@
    9.31              if (globalChanged) {
    9.32                  Context.setGlobal(global);
    9.33              }
    9.34 -            final Source source = new Source(file.getAbsolutePath(), buffer);
    9.35 +            final Source source = sourceFor(file.getAbsolutePath(), buffer);
    9.36              final ScriptFunction script = context.compileScript(source, global);
    9.37              if (script == null || context.getErrorManager().getNumberOfErrors() > 0) {
    9.38                  log("Compile failed: " + file.getAbsolutePath());
    10.1 --- a/test/src/jdk/nashorn/internal/parser/ParserTest.java	Fri May 02 19:15:59 2014 +0530
    10.2 +++ b/test/src/jdk/nashorn/internal/parser/ParserTest.java	Fri Apr 25 16:34:17 2014 +0200
    10.3 @@ -25,6 +25,9 @@
    10.4  
    10.5  package jdk.nashorn.internal.parser;
    10.6  
    10.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
    10.8 +import static jdk.nashorn.internal.runtime.Source.readFully;
    10.9 +
   10.10  import java.io.File;
   10.11  import jdk.nashorn.internal.runtime.Context;
   10.12  import jdk.nashorn.internal.runtime.ErrorManager;
   10.13 @@ -132,7 +135,7 @@
   10.14          }
   10.15  
   10.16          try {
   10.17 -            final char[] buffer = Source.readFully(file);
   10.18 +            final char[] buffer = readFully(file);
   10.19              boolean excluded = false;
   10.20              if (filter != null) {
   10.21                  final String content = new String(buffer);
   10.22 @@ -154,7 +157,7 @@
   10.23                  }
   10.24              };
   10.25              errors.setLimit(0);
   10.26 -            final Source   source   = new Source(file.getAbsolutePath(), buffer);
   10.27 +            final Source   source   = sourceFor(file.getAbsolutePath(), buffer);
   10.28              new Parser(context.getEnv(), source, errors).parse();
   10.29              if (errors.getNumberOfErrors() > 0) {
   10.30                  log("Parse failed: " + file.getAbsolutePath());
    11.1 --- a/test/src/jdk/nashorn/internal/runtime/ContextTest.java	Fri May 02 19:15:59 2014 +0530
    11.2 +++ b/test/src/jdk/nashorn/internal/runtime/ContextTest.java	Fri Apr 25 16:34:17 2014 +0200
    11.3 @@ -25,6 +25,7 @@
    11.4  
    11.5  package jdk.nashorn.internal.runtime;
    11.6  
    11.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
    11.8  import static org.testng.Assert.assertEquals;
    11.9  import static org.testng.Assert.assertTrue;
   11.10  
   11.11 @@ -107,7 +108,7 @@
   11.12      }
   11.13  
   11.14      private Object eval(final Context cx, final String name, final String code) {
   11.15 -        final Source source = new Source(name, code);
   11.16 +        final Source source = sourceFor(name, code);
   11.17          final ScriptObject global = Context.getGlobal();
   11.18          final ScriptFunction func = cx.compileScript(source, global);
   11.19          return func != null ? ScriptRuntime.apply(func, global) : null;
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/test/src/jdk/nashorn/internal/runtime/SourceTest.java	Fri Apr 25 16:34:17 2014 +0200
    12.3 @@ -0,0 +1,128 @@
    12.4 +/*
    12.5 + * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
    12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.7 + *
    12.8 + * This code is free software; you can redistribute it and/or modify it
    12.9 + * under the terms of the GNU General Public License version 2 only, as
   12.10 + * published by the Free Software Foundation.  Oracle designates this
   12.11 + * particular file as subject to the "Classpath" exception as provided
   12.12 + * by Oracle in the LICENSE file that accompanied this code.
   12.13 + *
   12.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   12.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   12.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   12.17 + * version 2 for more details (a copy is included in the LICENSE file that
   12.18 + * accompanied this code).
   12.19 + *
   12.20 + * You should have received a copy of the GNU General Public License version
   12.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   12.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   12.23 + *
   12.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   12.25 + * or visit www.oracle.com if you need additional information or have any
   12.26 + * questions.
   12.27 + */
   12.28 +
   12.29 +package jdk.nashorn.internal.runtime;
   12.30 +
   12.31 +import jdk.nashorn.api.scripting.URLReader;
   12.32 +import org.testng.annotations.Test;
   12.33 +
   12.34 +import java.io.File;
   12.35 +import java.io.IOException;
   12.36 +import java.io.InputStreamReader;
   12.37 +import java.io.Reader;
   12.38 +import java.net.URL;
   12.39 +import java.util.Arrays;
   12.40 +
   12.41 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
   12.42 +import static org.testng.Assert.assertEquals;
   12.43 +import static org.testng.Assert.assertTrue;
   12.44 +import static org.testng.Assert.fail;
   12.45 +
   12.46 +/**
   12.47 + * Tests different Source representations.
   12.48 + */
   12.49 +public class SourceTest {
   12.50 +
   12.51 +    final private static String SOURCE_NAME = "source.js";
   12.52 +    final private static String SOURCE_STRING = "var x = 1;";
   12.53 +    final private static char[] SOURCE_CHARS = SOURCE_STRING.toCharArray();
   12.54 +    final private static String RESOURCE_PATH = "resources/load_test.js";
   12.55 +    final private static File SOURCE_FILE = new File("build/test/classes/jdk/nashorn/internal/runtime/" + RESOURCE_PATH);
   12.56 +    final private static URL  SOURCE_URL = SourceTest.class.getResource(RESOURCE_PATH);
   12.57 +
   12.58 +
   12.59 +    @Test
   12.60 +    public void testStringSource() {
   12.61 +        testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_STRING));
   12.62 +        testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_CHARS));
   12.63 +    }
   12.64 +
   12.65 +    @Test
   12.66 +    public void testCharArraySource() {
   12.67 +        testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_CHARS));
   12.68 +        testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_STRING));
   12.69 +    }
   12.70 +
   12.71 +    @Test
   12.72 +    public void testURLSource() {
   12.73 +        try {
   12.74 +            testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, SOURCE_URL));
   12.75 +            testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)));
   12.76 +
   12.77 +        } catch (final IOException e) {
   12.78 +            fail(e.toString());
   12.79 +        }
   12.80 +    }
   12.81 +
   12.82 +    @Test
   12.83 +    public void testURLReaderSource() {
   12.84 +        try {
   12.85 +            System.err.println(SourceTest.class.getResource(""));
   12.86 +            testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)));
   12.87 +            testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, SOURCE_URL));
   12.88 +        } catch (final IOException e) {
   12.89 +            fail(e.toString());
   12.90 +        }
   12.91 +    }
   12.92 +
   12.93 +    @Test
   12.94 +    public void testReaderSource() {
   12.95 +        try {
   12.96 +            testSources(sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH)), sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH)));
   12.97 +        } catch (final IOException e) {
   12.98 +            fail(e.toString());
   12.99 +        }
  12.100 +    }
  12.101 +
  12.102 +    @Test
  12.103 +    public void testFileSource() {
  12.104 +        try {
  12.105 +            testSources(sourceFor(SOURCE_NAME, SOURCE_FILE), sourceFor(SOURCE_NAME, SOURCE_FILE));
  12.106 +        } catch (final IOException e) {
  12.107 +            fail(e.toString());
  12.108 +        }
  12.109 +    }
  12.110 +
  12.111 +    private Reader getReader(final String path) {
  12.112 +        return new InputStreamReader(SourceTest.class.getResourceAsStream(path));
  12.113 +    }
  12.114 +
  12.115 +    private void testSources(final Source source1, final Source source2) {
  12.116 +        final char[] chars1 = source1.getContent();
  12.117 +        final char[] chars2 = source2.getContent();
  12.118 +        final String str1 = source1.getString();
  12.119 +        final String str2 = source2.getString();
  12.120 +        assertTrue(Arrays.equals(chars1, chars2));
  12.121 +        assertEquals(str1, str2);
  12.122 +        assertEquals(source1.hashCode(), source2.hashCode());
  12.123 +        assertTrue(source1.equals(source2));
  12.124 +        // Test for immutability
  12.125 +        Arrays.fill(source1.getContent(), (char)0);
  12.126 +        Arrays.fill(source2.getContent(), (char)1);
  12.127 +        assertTrue(Arrays.equals(source1.getContent(), str1.toCharArray()));
  12.128 +        assertTrue(Arrays.equals(source1.getContent(), chars1));
  12.129 +        assertTrue(Arrays.equals(source1.getContent(), source2.getContent()));
  12.130 +    }
  12.131 +}
    13.1 --- a/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java	Fri May 02 19:15:59 2014 +0530
    13.2 +++ b/test/src/jdk/nashorn/internal/test/framework/SharedContextEvaluator.java	Fri Apr 25 16:34:17 2014 +0200
    13.3 @@ -25,6 +25,7 @@
    13.4  
    13.5  package jdk.nashorn.internal.test.framework;
    13.6  
    13.7 +import static jdk.nashorn.internal.runtime.Source.sourceFor;
    13.8  import static jdk.nashorn.tools.Shell.COMPILATION_ERROR;
    13.9  import static jdk.nashorn.tools.Shell.RUNTIME_ERROR;
   13.10  import static jdk.nashorn.tools.Shell.SUCCESS;
   13.11 @@ -39,7 +40,6 @@
   13.12  import jdk.nashorn.internal.runtime.ErrorManager;
   13.13  import jdk.nashorn.internal.runtime.ScriptFunction;
   13.14  import jdk.nashorn.internal.runtime.ScriptRuntime;
   13.15 -import jdk.nashorn.internal.runtime.Source;
   13.16  import jdk.nashorn.internal.runtime.options.Options;
   13.17  
   13.18  /**
   13.19 @@ -125,7 +125,7 @@
   13.20                      continue;
   13.21                  }
   13.22                  final File file = new File(fileName);
   13.23 -                ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
   13.24 +                ScriptFunction script = context.compileScript(sourceFor(fileName, file.toURI().toURL()), global);
   13.25  
   13.26                  if (script == null || errors.getNumberOfErrors() != 0) {
   13.27                      return COMPILATION_ERROR;
    14.1 --- a/test/src/jdk/nashorn/test/models/SourceHelper.java	Fri May 02 19:15:59 2014 +0530
    14.2 +++ b/test/src/jdk/nashorn/test/models/SourceHelper.java	Fri Apr 25 16:34:17 2014 +0200
    14.3 @@ -46,7 +46,7 @@
    14.4      }
    14.5  
    14.6      public static String readFully(final URL url) throws IOException {
    14.7 -        return new Source(url.toString(), url).getString();
    14.8 +        return Source.sourceFor(url.toString(), url).getString();
    14.9      }
   14.10  
   14.11      public static String readFully(final Reader reader) throws IOException {

mercurial