8040078: Avoid repeated reading of source for cached loads

Reviewed-by: jlaskey, lagergren
This commit is contained in:
Hannes Wallnöfer 2014-04-25 16:34:17 +02:00
parent 8fb1f303f7
commit 60a0f257df
14 changed files with 544 additions and 132 deletions

View File

@ -27,16 +27,14 @@ package jdk.nashorn.api.scripting;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
@ -124,21 +122,21 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
}
// load engine.js and return content as a char[]
// load engine.js
@SuppressWarnings("resource")
private static char[] loadEngineJSSource() {
private static Source loadEngineJSSource() {
final String script = "resources/engine.js";
try {
final InputStream is = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
return AccessController.doPrivileged(
new PrivilegedExceptionAction<Source>() {
@Override
public InputStream run() throws Exception {
public Source run() throws IOException {
final URL url = NashornScriptEngine.class.getResource(script);
return url.openStream();
return sourceFor(NashornException.ENGINE_SCRIPT_SOURCE_NAME, url);
}
});
return Source.readFully(new InputStreamReader(is));
} catch (final PrivilegedActionException | IOException e) {
}
);
} catch (final PrivilegedActionException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
@ -147,7 +145,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
// Source object for engine.js
private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource());
private static final Source ENGINE_SCRIPT_SRC = loadEngineJSSource();
NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) {
this(factory, DEFAULT_OPTIONS, appLoader);
@ -282,19 +280,14 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
try {
if (reader instanceof URLReader) {
final URL url = ((URLReader)reader).getURL();
final Charset cs = ((URLReader)reader).getCharset();
return new Source(url.toString(), url, cs);
}
return new Source(getScriptName(ctxt), Source.readFully(reader));
} catch (final IOException e) {
return sourceFor(getScriptName(ctxt), reader);
} catch (IOException e) {
throw new ScriptException(e);
}
}
private static Source makeSource(final String src, final ScriptContext ctxt) {
return new Source(getScriptName(ctxt), src);
return sourceFor(getScriptName(ctxt), src);
}
private static String getScriptName(final ScriptContext ctxt) {

View File

@ -25,6 +25,8 @@
package jdk.nashorn.internal.ir.debug;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
@ -88,7 +90,7 @@ public final class JSONWriter extends NodeVisitor<LexicalContext> {
* @return JSON string representation of AST of the supplied code
*/
public static String parse(final ScriptEnvironment env, final String code, final String name, final boolean includeLoc) {
final Parser parser = new Parser(env, new Source(name, code), new Context.ThrowErrorManager(), env._strict);
final Parser parser = new Parser(env, sourceFor(name, code), new Context.ThrowErrorManager(), env._strict);
final JSONWriter jsonWriter = new JSONWriter(includeLoc);
try {
final FunctionNode functionNode = parser.parse(CompilerConstants.RUN_SCRIPT.symbolName());

View File

@ -27,6 +27,7 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.util.List;
@ -257,7 +258,7 @@ public final class NativeFunction {
}
private static void checkFunctionParameters(final String params) {
final Source src = new Source("<function>", params);
final Source src = sourceFor("<function>", params);
final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
try {
parser.parseFormalParameterList();
@ -267,7 +268,7 @@ public final class NativeFunction {
}
private static void checkFunctionBody(final String funcBody) {
final Source src = new Source("<function>", funcBody);
final Source src = sourceFor("<function>", funcBody);
final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
try {
parser.parseFunctionBody();

View File

@ -32,6 +32,7 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.io.File;
import java.io.IOException;
@ -501,7 +502,7 @@ public final class Context {
*/
public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
final String file = (location == UNDEFINED || location == null) ? "<eval>" : location.toString();
final Source source = new Source(file, string);
final Source source = sourceFor(file, string);
final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
final Global global = Context.getGlobal();
@ -568,7 +569,7 @@ public final class Context {
public Source run() {
try {
final URL resURL = Context.class.getResource(resource);
return (resURL != null)? new Source(srcStr, resURL) : null;
return (resURL != null)? sourceFor(srcStr, resURL) : null;
} catch (final IOException exp) {
return null;
}
@ -600,7 +601,7 @@ public final class Context {
final String srcStr = (String)src;
if (srcStr.startsWith(LOAD_CLASSPATH)) {
URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
source = (url != null)? new Source(url.toString(), url) : null;
source = (url != null)? sourceFor(url.toString(), url) : null;
} else {
final File file = new File(srcStr);
if (srcStr.indexOf(':') != -1) {
@ -613,31 +614,31 @@ public final class Context {
} catch (final MalformedURLException e) {
url = file.toURI().toURL();
}
source = new Source(url.toString(), url);
source = sourceFor(url.toString(), url);
}
} else if (file.isFile()) {
source = new Source(srcStr, file);
source = sourceFor(srcStr, file);
}
}
} else if (src instanceof File && ((File)src).isFile()) {
final File file = (File)src;
source = new Source(file.getName(), file);
source = sourceFor(file.getName(), file);
} else if (src instanceof URL) {
final URL url = (URL)src;
source = new Source(url.toString(), url);
source = sourceFor(url.toString(), url);
} else if (src instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject)src;
if (sobj.has("script") && sobj.has("name")) {
final String script = JSType.toString(sobj.get("script"));
final String name = JSType.toString(sobj.get("name"));
source = new Source(name, script);
source = sourceFor(name, script);
}
} else if (src instanceof Map) {
final Map<?,?> map = (Map<?,?>)src;
if (map.containsKey("script") && map.containsKey("name")) {
final String script = JSType.toString(map.get("script"));
final String name = JSType.toString(map.get("name"));
source = new Source(name, script);
source = sourceFor(name, script);
}
}

View File

@ -39,6 +39,8 @@ import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
/**
* Utilities used by "JSON" object implementation.
*/
@ -77,9 +79,7 @@ public final class JSONFunctions {
*/
public static Object parse(final Object text, final Object reviver) {
final String str = JSType.toString(text);
final JSONParser parser = new JSONParser(
new Source("<json>", str),
new Context.ThrowErrorManager());
final JSONParser parser = new JSONParser(sourceFor("<json>", str), new Context.ThrowErrorManager());
Node node;

View File

@ -27,13 +27,16 @@ package jdk.nashorn.internal.runtime;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@ -43,13 +46,19 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;
import java.util.WeakHashMap;
import jdk.nashorn.api.scripting.URLReader;
import jdk.nashorn.internal.parser.Token;
/**
* Source objects track the origin of JavaScript entities.
*
*/
public final class Source {
private static final DebugLogger DEBUG = new DebugLogger("source");
private static final int BUF_SIZE = 8 * 1024;
private static final Cache CACHE = new Cache();
/**
* Descriptive name of the source as supplied by the user. Used for error
* reporting to the user. For example, SyntaxError will use this to print message.
@ -64,11 +73,8 @@ public final class Source {
*/
private final String base;
/** Cached source content. */
private final char[] content;
/** Length of source content. */
private final int length;
/** Source content */
private final Data data;
/** Cached hash code */
private int hash;
@ -76,40 +82,297 @@ public final class Source {
/** Message digest */
private byte[] digest;
/** Source URL if available */
private final URL url;
// Do *not* make this public, ever! Trusts the URL and content.
private Source(final String name, final String base, final Data data) {
this.name = name;
this.base = base;
this.data = data;
}
private static final int BUFSIZE = 8 * 1024;
private static synchronized Source sourceFor(final String name, final String base, final URLData data) throws IOException {
try {
final Source newSource = new Source(name, base, data);
final Source existingSource = CACHE.get(newSource);
if (existingSource != null) {
// Force any access errors
data.checkPermissionAndClose();
return existingSource;
} else {
// All sources in cache must be fully loaded
data.load();
CACHE.put(newSource, newSource);
return newSource;
}
} catch (RuntimeException e) {
final Throwable cause = e.getCause();
if (cause instanceof IOException) {
throw (IOException) cause;
}
throw e;
}
}
// Do *not* make this public ever! Trusts the URL and content. So has to be called
// from other public constructors. Note that this can not be some init method as
// we initialize final fields from here.
private Source(final String name, final String base, final char[] content, final URL url) {
this.name = name;
this.base = base;
this.content = content;
this.length = content.length;
this.url = url;
private static class Cache extends WeakHashMap<Source, WeakReference<Source>> {
public Source get(final Source key) {
final WeakReference<Source> ref = super.get(key);
return ref == null ? null : ref.get();
}
public void put(final Source key, final Source value) {
assert !(value.data instanceof RawData);
put(key, new WeakReference<>(value));
}
}
// Wrapper to manage lazy loading
private static interface Data {
URL url();
int length();
long lastModified();
char[] array();
}
private static class RawData implements Data {
private final char[] array;
private int hash;
private RawData(final char[] array) {
this.array = Objects.requireNonNull(array);
}
private RawData(final String source) {
this.array = Objects.requireNonNull(source).toCharArray();
}
private RawData(final Reader reader) throws IOException {
this(readFully(reader));
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = hash = Arrays.hashCode(array);
}
return h;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof RawData) {
return Arrays.equals(array, ((RawData)obj).array);
}
return false;
}
@Override
public String toString() {
return new String(array());
}
@Override
public URL url() {
return null;
}
@Override
public int length() {
return array.length;
}
@Override
public long lastModified() {
return 0;
}
@Override
public char[] array() {
return array;
}
}
private static class URLData implements Data {
private final URL url;
protected final Charset cs;
private int hash;
protected char[] array;
protected int length;
protected long lastModified;
private URLData(final URL url, final Charset cs) {
this.url = Objects.requireNonNull(url);
this.cs = cs;
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = hash = url.hashCode();
}
return h;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof URLData)) {
return false;
}
URLData otherData = (URLData) other;
if (url.equals(otherData.url)) {
// Make sure both have meta data loaded
try {
if (isDeferred()) {
// Data in cache is always loaded, and we only compare to cached data.
assert !otherData.isDeferred();
loadMeta();
} else if (otherData.isDeferred()) {
otherData.loadMeta();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
// Compare meta data
return this.length == otherData.length && this.lastModified == otherData.lastModified;
}
return false;
}
@Override
public String toString() {
return new String(array());
}
@Override
public URL url() {
return url;
}
@Override
public int length() {
return length;
}
@Override
public long lastModified() {
return lastModified;
}
@Override
public char[] array() {
assert !isDeferred();
return array;
}
boolean isDeferred() {
return array == null;
}
protected void checkPermissionAndClose() throws IOException {
try (InputStream in = url.openStream()) {}
debug("permission checked for ", url);
}
protected void load() throws IOException {
if (array == null) {
final URLConnection c = url.openConnection();
try (InputStream in = c.getInputStream()) {
array = cs == null ? readFully(in) : readFully(in, cs);
length = array.length;
lastModified = c.getLastModified();
debug("loaded content for ", url);
}
}
}
protected void loadMeta() throws IOException {
if (length == 0 && lastModified == 0) {
final URLConnection c = url.openConnection();
length = c.getContentLength();
lastModified = c.getLastModified();
debug("loaded metadata for ", url);
}
}
}
private static class FileData extends URLData {
private final File file;
private FileData(final File file, final Charset cs) {
super(getURLFromFile(file), cs);
this.file = file;
}
@Override
protected void checkPermissionAndClose() throws IOException {
if (!file.canRead()) {
throw new FileNotFoundException(file + " (Permission Denied)");
}
debug("permission checked for ", file);
}
@Override
protected void loadMeta() {
if (length == 0 && lastModified == 0) {
length = (int) file.length();
lastModified = file.lastModified();
debug("loaded metadata for ", file);
}
}
@Override
protected void load() throws IOException {
if (array == null) {
array = cs == null ? readFully(file) : readFully(file, cs);
length = array.length;
lastModified = file.lastModified();
debug("loaded content for ", file);
}
}
}
private static void debug(final Object... msg) {
DEBUG.info(msg);
}
private char[] data() {
return data.array();
}
/**
* Constructor
* Returns an instance
*
* @param name source name
* @param content contents as char array
*/
public Source(final String name, final char[] content) {
this(name, baseName(name, null), content, null);
public static Source sourceFor(final String name, final char[] content) {
return new Source(name, baseName(name), new RawData(content));
}
/**
* Constructor
* Returns an instance
*
* @param name source name
* @param content contents as string
*/
public Source(final String name, final String content) {
this(name, content.toCharArray());
public static Source sourceFor(final String name, final String content) {
return new Source(name, baseName(name), new RawData(content));
}
/**
@ -120,8 +383,8 @@ public final class Source {
*
* @throws IOException if source cannot be loaded
*/
public Source(final String name, final URL url) throws IOException {
this(name, baseURL(url, null), readFully(url), url);
public static Source sourceFor(final String name, final URL url) throws IOException {
return sourceFor(name, url, null);
}
/**
@ -133,8 +396,8 @@ public final class Source {
*
* @throws IOException if source cannot be loaded
*/
public Source(final String name, final URL url, final Charset cs) throws IOException {
this(name, baseURL(url, null), readFully(url, cs), url);
public static Source sourceFor(final String name, final URL url, final Charset cs) throws IOException {
return sourceFor(name, baseURL(url), new URLData(url, cs));
}
/**
@ -145,8 +408,8 @@ public final class Source {
*
* @throws IOException if source cannot be loaded
*/
public Source(final String name, final File file) throws IOException {
this(name, dirName(file, null), readFully(file), getURLFromFile(file));
public static Source sourceFor(final String name, final File file) throws IOException {
return sourceFor(name, file, null);
}
/**
@ -158,8 +421,25 @@ public final class Source {
*
* @throws IOException if source cannot be loaded
*/
public Source(final String name, final File file, final Charset cs) throws IOException {
this(name, dirName(file, null), readFully(file, cs), getURLFromFile(file));
public static Source sourceFor(final String name, final File file, final Charset cs) throws IOException {
final File absFile = file.getAbsoluteFile();
return sourceFor(name, dirName(absFile, null), new FileData(file, cs));
}
/**
* Returns an instance
*
* @param name source name
* @param reader reader from which source can be loaded
* @throws IOException if source cannot be loaded
*/
public static Source sourceFor(final String name, final Reader reader) throws IOException {
// Extract URL from URLReader to defer loading and reuse cached data if available.
if (reader instanceof URLReader) {
final URLReader urlReader = (URLReader) reader;
return sourceFor(name, urlReader.getURL(), urlReader.getCharset());
}
return new Source(name, baseName(name), new RawData(reader));
}
@Override
@ -167,21 +447,18 @@ public final class Source {
if (this == obj) {
return true;
}
if (!(obj instanceof Source)) {
return false;
}
final Source src = (Source)obj;
// Only compare content as a last resort measure
return length == src.length && Objects.equals(url, src.url) && Objects.equals(name, src.name) && Arrays.equals(content, src.content);
final Source other = (Source) obj;
return Objects.equals(name, other.name) && data.equals(other.data);
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
h = hash = Arrays.hashCode(content) ^ Objects.hashCode(name);
h = hash = data.hashCode() ^ Objects.hashCode(name);
}
return h;
}
@ -191,7 +468,7 @@ public final class Source {
* @return Source content.
*/
public String getString() {
return new String(content, 0, length);
return data.toString();
}
/**
@ -202,6 +479,14 @@ public final class Source {
return name;
}
/**
* Get the last modified time of this script.
* @return Last modified time.
*/
public long getLastModified() {
return data.lastModified();
}
/**
* Get the "directory" part of the file or "base" of the URL.
* @return base of file or URL.
@ -217,7 +502,7 @@ public final class Source {
* @return Source content portion.
*/
public String getString(final int start, final int len) {
return new String(content, start, len);
return new String(data(), start, len);
}
/**
@ -228,7 +513,7 @@ public final class Source {
public String getString(final long token) {
final int start = Token.descPosition(token);
final int len = Token.descLength(token);
return new String(content, start, len);
return new String(data(), start, len);
}
/**
@ -238,7 +523,7 @@ public final class Source {
* @return URL source or null
*/
public URL getURL() {
return url;
return data.url();
}
/**
@ -247,8 +532,9 @@ public final class Source {
* @return Index of first character of line.
*/
private int findBOLN(final int position) {
final char[] data = data();
for (int i = position - 1; i > 0; i--) {
final char ch = content[i];
final char ch = data[i];
if (ch == '\n' || ch == '\r') {
return i + 1;
@ -264,8 +550,10 @@ public final class Source {
* @return Index of last character of line.
*/
private int findEOLN(final int position) {
for (int i = position; i < length; i++) {
final char ch = content[i];
final char[] data = data();
final int length = data.length;
for (int i = position; i < length; i++) {
final char ch = data[i];
if (ch == '\n' || ch == '\r') {
return i - 1;
@ -285,11 +573,12 @@ public final class Source {
* @return Line number.
*/
public int getLine(final int position) {
final char[] data = data();
// Line count starts at 1.
int line = 1;
for (int i = 0; i < position; i++) {
final char ch = content[i];
final char ch = data[i];
// Works for both \n and \r\n.
if (ch == '\n') {
line++;
@ -320,7 +609,7 @@ public final class Source {
// Find end of this line.
final int last = findEOLN(position);
return new String(content, first, last - first + 1);
return new String(data(), first, last - first + 1);
}
/**
@ -328,7 +617,7 @@ public final class Source {
* @return content
*/
public char[] getContent() {
return content.clone();
return data().clone();
}
/**
@ -336,19 +625,18 @@ public final class Source {
* @return length
*/
public int getLength() {
return length;
return data.length();
}
/**
* Read all of the source until end of file. Return it as char array
*
* @param reader reader opened to source stream
* @param reader reader opened to source stream
* @return source as content
*
* @throws IOException if source could not be read
*/
public static char[] readFully(final Reader reader) throws IOException {
final char[] arr = new char[BUFSIZE];
final char[] arr = new char[BUF_SIZE];
final StringBuilder sb = new StringBuilder();
try {
@ -366,9 +654,8 @@ public final class Source {
/**
* Read all of the source until end of file. Return it as char array
*
* @param file source file
* @param file source file
* @return source as content
*
* @throws IOException if source could not be read
*/
public static char[] readFully(final File file) throws IOException {
@ -381,10 +668,9 @@ public final class Source {
/**
* Read all of the source until end of file. Return it as char array
*
* @param file source file
* @param file source file
* @param cs Charset used to convert bytes to chars
* @return source as content
*
* @throws IOException if source could not be read
*/
public static char[] readFully(final File file, final Charset cs) throws IOException {
@ -393,7 +679,7 @@ public final class Source {
}
final byte[] buf = Files.readAllBytes(file.toPath());
return (cs != null)? new String(buf, cs).toCharArray() : byteToCharArray(buf);
return (cs != null) ? new String(buf, cs).toCharArray() : byteToCharArray(buf);
}
/**
@ -401,7 +687,6 @@ public final class Source {
*
* @param url URL to read content from
* @return source as content
*
* @throws IOException if source could not be read
*/
public static char[] readFully(final URL url) throws IOException {
@ -414,7 +699,6 @@ public final class Source {
* @param url URL to read content from
* @param cs Charset used to convert bytes to chars
* @return source as content
*
* @throws IOException if source could not be read
*/
public static char[] readFully(final URL url, final Charset cs) throws IOException {
@ -428,7 +712,7 @@ public final class Source {
*/
public synchronized byte[] getDigest() {
if (digest == null) {
final char[] content = data();
final byte[] bytes = new byte[content.length * 2];
for (int i = 0; i < content.length; i++) {
@ -444,8 +728,8 @@ public final class Source {
if (base != null) {
md.update(base.getBytes(StandardCharsets.UTF_8));
}
if (url != null) {
md.update(url.toString().getBytes(StandardCharsets.UTF_8));
if (getURL() != null) {
md.update(getURL().toString().getBytes(StandardCharsets.UTF_8));
}
digest = md.digest(bytes);
} catch (NoSuchAlgorithmException e) {
@ -461,50 +745,46 @@ public final class Source {
* @return base URL for url
*/
public static String baseURL(final URL url) {
return baseURL(url, null);
}
private static String baseURL(final URL url, final String defaultValue) {
if (url.getProtocol().equals("file")) {
try {
final Path path = Paths.get(url.toURI());
final Path parent = path.getParent();
return (parent != null) ? (parent + File.separator) : defaultValue;
return (parent != null) ? (parent + File.separator) : null;
} catch (final SecurityException | URISyntaxException | IOError e) {
return defaultValue;
return null;
}
}
// FIXME: is there a better way to find 'base' URL of a given URL?
String path = url.getPath();
if (path.isEmpty()) {
return defaultValue;
return null;
}
path = path.substring(0, path.lastIndexOf('/') + 1);
final int port = url.getPort();
try {
return new URL(url.getProtocol(), url.getHost(), port, path).toString();
} catch (final MalformedURLException e) {
return defaultValue;
return null;
}
}
private static String dirName(final File file, final String defaultValue) {
private static String dirName(final File file, final String DEFAULT_BASE_NAME) {
final String res = file.getParent();
return (res != null)? (res + File.separator) : defaultValue;
return (res != null) ? (res + File.separator) : DEFAULT_BASE_NAME;
}
// fake directory like name
private static String baseName(final String name, final String defaultValue) {
private static String baseName(final String name) {
int idx = name.lastIndexOf('/');
if (idx == -1) {
idx = name.lastIndexOf('\\');
}
return (idx != -1)? name.substring(0, idx + 1) : defaultValue;
return (idx != -1) ? name.substring(0, idx + 1) : null;
}
private static char[] readFully(final InputStream is, final Charset cs) throws IOException {
return (cs != null)? new String(readBytes(is), cs).toCharArray() : readFully(is);
return (cs != null) ? new String(readBytes(is), cs).toCharArray() : readFully(is);
}
private static char[] readFully(final InputStream is) throws IOException {
@ -515,19 +795,19 @@ public final class Source {
Charset cs = StandardCharsets.UTF_8;
int start = 0;
// BOM detection.
if (bytes.length > 1 && bytes[0] == (byte)0xFE && bytes[1] == (byte)0xFF) {
if (bytes.length > 1 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF) {
start = 2;
cs = StandardCharsets.UTF_16BE;
} else if (bytes.length > 1 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE) {
} else if (bytes.length > 1 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE) {
start = 2;
cs = StandardCharsets.UTF_16LE;
} else if (bytes.length > 2 && bytes[0] == (byte)0xEF && bytes[1] == (byte)0xBB && bytes[2] == (byte)0xBF) {
} else if (bytes.length > 2 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) {
start = 3;
cs = StandardCharsets.UTF_8;
} else if (bytes.length > 3 && bytes[0] == (byte)0xFF && bytes[1] == (byte)0xFE && bytes[2] == 0 && bytes[3] == 0) {
} else if (bytes.length > 3 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE && bytes[2] == 0 && bytes[3] == 0) {
start = 4;
cs = Charset.forName("UTF-32LE");
} else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte)0xFE && bytes[3] == (byte)0xFF) {
} else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF) {
start = 4;
cs = Charset.forName("UTF-32BE");
}
@ -536,7 +816,7 @@ public final class Source {
}
static byte[] readBytes(final InputStream is) throws IOException {
final byte[] arr = new byte[BUFSIZE];
final byte[] arr = new byte[BUF_SIZE];
try {
try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) {
int numBytes;

View File

@ -25,6 +25,8 @@
package jdk.nashorn.tools;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
@ -50,7 +52,6 @@ import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
@ -244,7 +245,7 @@ public class Shell {
// For each file on the command line.
for (final String fileName : files) {
final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse();
final FunctionNode functionNode = new Parser(env, sourceFor(fileName, new File(fileName)), errors).parse();
if (errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;
@ -302,7 +303,7 @@ public class Shell {
}
final File file = new File(fileName);
final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
final ScriptFunction script = context.compileScript(sourceFor(fileName, file), global);
if (script == null || errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;
}
@ -405,7 +406,7 @@ public class Shell {
// initialize with "shell.js" script
try {
final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js"));
final Source source = sourceFor("<shell.js>", Shell.class.getResource("resources/shell.js"));
context.eval(global, source.getString(), global, "<shell.js>", false);
} catch (final Exception e) {
err.println(e);

View File

@ -113,7 +113,7 @@ function findFunction(node) {
var getContextMethod = Context.class.getMethod("getContext")
var getEnvMethod = Context.class.getMethod("getEnv")
var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class)
var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
@ -121,7 +121,7 @@ var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
// representing it.
function compile(source) {
var source = SourceConstructor.newInstance("<no name>", source);
var source = sourceForMethod.invoke(null, "<no name>", source);
var env = getEnvMethod.invoke(getContextMethod.invoke(null))

View File

@ -25,6 +25,9 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import static jdk.nashorn.internal.runtime.Source.readFully;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
@ -32,7 +35,6 @@ import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
import org.testng.Assert;
@ -151,7 +153,7 @@ public class CompilerTest {
final boolean globalChanged = (oldGlobal != global);
try {
final char[] buffer = Source.readFully(file);
final char[] buffer = readFully(file);
boolean excluded = false;
if (filter != null) {
@ -170,7 +172,7 @@ public class CompilerTest {
if (globalChanged) {
Context.setGlobal(global);
}
final Source source = new Source(file.getAbsolutePath(), buffer);
final Source source = sourceFor(file.getAbsolutePath(), buffer);
final ScriptFunction script = context.compileScript(source, global);
if (script == null || context.getErrorManager().getNumberOfErrors() > 0) {
log("Compile failed: " + file.getAbsolutePath());

View File

@ -25,6 +25,9 @@
package jdk.nashorn.internal.parser;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import static jdk.nashorn.internal.runtime.Source.readFully;
import java.io.File;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
@ -131,7 +134,7 @@ public class ParserTest {
}
try {
final char[] buffer = Source.readFully(file);
final char[] buffer = readFully(file);
boolean excluded = false;
if (filter != null) {
final String content = new String(buffer);
@ -153,7 +156,7 @@ public class ParserTest {
}
};
errors.setLimit(0);
final Source source = new Source(file.getAbsolutePath(), buffer);
final Source source = sourceFor(file.getAbsolutePath(), buffer);
new Parser(context.getEnv(), source, errors).parse();
if (errors.getNumberOfErrors() > 0) {
log("Parse failed: " + file.getAbsolutePath());

View File

@ -25,6 +25,7 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
@ -107,7 +108,7 @@ public class ContextTest {
}
private Object eval(final Context cx, final String name, final String code) {
final Source source = new Source(name, code);
final Source source = sourceFor(name, code);
final ScriptObject global = Context.getGlobal();
final ScriptFunction func = cx.compileScript(source, global);
return func != null ? ScriptRuntime.apply(func, global) : null;

View File

@ -0,0 +1,128 @@
/*
* 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime;
import jdk.nashorn.api.scripting.URLReader;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.util.Arrays;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
/**
* Tests different Source representations.
*/
public class SourceTest {
final private static String SOURCE_NAME = "source.js";
final private static String SOURCE_STRING = "var x = 1;";
final private static char[] SOURCE_CHARS = SOURCE_STRING.toCharArray();
final private static String RESOURCE_PATH = "resources/load_test.js";
final private static File SOURCE_FILE = new File("build/test/classes/jdk/nashorn/internal/runtime/" + RESOURCE_PATH);
final private static URL SOURCE_URL = SourceTest.class.getResource(RESOURCE_PATH);
@Test
public void testStringSource() {
testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_STRING));
testSources(sourceFor(SOURCE_NAME, SOURCE_STRING), sourceFor(SOURCE_NAME, SOURCE_CHARS));
}
@Test
public void testCharArraySource() {
testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_CHARS));
testSources(sourceFor(SOURCE_NAME, SOURCE_CHARS), sourceFor(SOURCE_NAME, SOURCE_STRING));
}
@Test
public void testURLSource() {
try {
testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, SOURCE_URL));
testSources(sourceFor(SOURCE_NAME, SOURCE_URL), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)));
} catch (final IOException e) {
fail(e.toString());
}
}
@Test
public void testURLReaderSource() {
try {
System.err.println(SourceTest.class.getResource(""));
testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)));
testSources(sourceFor(SOURCE_NAME, new URLReader(SOURCE_URL)), sourceFor(SOURCE_NAME, SOURCE_URL));
} catch (final IOException e) {
fail(e.toString());
}
}
@Test
public void testReaderSource() {
try {
testSources(sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH)), sourceFor(SOURCE_NAME, getReader(RESOURCE_PATH)));
} catch (final IOException e) {
fail(e.toString());
}
}
@Test
public void testFileSource() {
try {
testSources(sourceFor(SOURCE_NAME, SOURCE_FILE), sourceFor(SOURCE_NAME, SOURCE_FILE));
} catch (final IOException e) {
fail(e.toString());
}
}
private Reader getReader(final String path) {
return new InputStreamReader(SourceTest.class.getResourceAsStream(path));
}
private void testSources(final Source source1, final Source source2) {
final char[] chars1 = source1.getContent();
final char[] chars2 = source2.getContent();
final String str1 = source1.getString();
final String str2 = source2.getString();
assertTrue(Arrays.equals(chars1, chars2));
assertEquals(str1, str2);
assertEquals(source1.hashCode(), source2.hashCode());
assertTrue(source1.equals(source2));
// Test for immutability
Arrays.fill(source1.getContent(), (char)0);
Arrays.fill(source2.getContent(), (char)1);
assertTrue(Arrays.equals(source1.getContent(), str1.toCharArray()));
assertTrue(Arrays.equals(source1.getContent(), chars1));
assertTrue(Arrays.equals(source1.getContent(), source2.getContent()));
}
}

View File

@ -25,6 +25,7 @@
package jdk.nashorn.internal.test.framework;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
import static jdk.nashorn.tools.Shell.COMPILATION_ERROR;
import static jdk.nashorn.tools.Shell.RUNTIME_ERROR;
import static jdk.nashorn.tools.Shell.SUCCESS;
@ -39,7 +40,6 @@ import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.options.Options;
/**
@ -125,7 +125,7 @@ public final class SharedContextEvaluator implements ScriptEvaluator {
continue;
}
final File file = new File(fileName);
ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
ScriptFunction script = context.compileScript(sourceFor(fileName, file.toURI().toURL()), global);
if (script == null || errors.getNumberOfErrors() != 0) {
return COMPILATION_ERROR;

View File

@ -46,7 +46,7 @@ public final class SourceHelper {
}
public static String readFully(final URL url) throws IOException {
return new Source(url.toString(), url).getString();
return Source.sourceFor(url.toString(), url).getString();
}
public static String readFully(final Reader reader) throws IOException {