From 1f2ccb2e01ea0aeb5f214289565eac578f7b2f3b Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 20 Sep 2021 09:57:10 +0200 Subject: [PATCH 1/2] Avoid unnecessary arrayCopy in Kit.readStream, when initialBufferCapacity matches stream length. --- src/org/mozilla/javascript/Kit.java | 11 ++++ .../org/mozilla/javascript/tests/KitTest.java | 58 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 testsrc/org/mozilla/javascript/tests/KitTest.java diff --git a/src/org/mozilla/javascript/Kit.java b/src/org/mozilla/javascript/Kit.java index 11a5d93a02..efa5241edf 100644 --- a/src/org/mozilla/javascript/Kit.java +++ b/src/org/mozilla/javascript/Kit.java @@ -350,9 +350,20 @@ public static byte[] readStream(InputStream is, int initialBufferCapacity) if (n < 0) { break; } cursor += n; if (cursor == buffer.length) { + int readahead = -1; + if (cursor == initialBufferCapacity) { + readahead = is.read(); + if (readahead < 0) { // Check for EOS + return buffer; + } + } byte[] tmp = new byte[buffer.length * 2]; System.arraycopy(buffer, 0, tmp, 0, cursor); buffer = tmp; + if (readahead != -1) { + buffer[cursor++] = (byte)readahead; + readahead = -1; + } } } if (cursor != buffer.length) { diff --git a/testsrc/org/mozilla/javascript/tests/KitTest.java b/testsrc/org/mozilla/javascript/tests/KitTest.java new file mode 100644 index 0000000000..40dd3b89be --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/KitTest.java @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import org.junit.Test; +import org.mozilla.javascript.Kit; + +/** + * Test for {@link Kit} + * + * @author Roland Praml, FOCONIS AG + */ +public class KitTest { + + private byte[] contentArr = new byte[123]; + + public KitTest() { + for (int i = 0; i < 123; i++) { + contentArr[i] = (byte) i; + } + } + + private InputStream emptyStream() { + return new ByteArrayInputStream(new byte[0]); + } + + private InputStream contentStream() { + + return new ByteArrayInputStream(contentArr); + } + + /** + * Test various code paths in Kit.readStream. Especially if bufferCapacity matches stream size. + */ + @Test + public void testReadSteam() throws IOException { + + byte[] buf = Kit.readStream(emptyStream(), 4096); // test, too big + assertEquals(0, buf.length); + + buf = Kit.readStream(contentStream(), 122); // too small + assertArrayEquals(contentArr, buf); + buf = Kit.readStream(contentStream(), 123); // exact match + assertArrayEquals(contentArr, buf); + buf = Kit.readStream(contentStream(), 124); // too big + assertArrayEquals(contentArr, buf); + buf = Kit.readStream(contentStream(), 60); // much too small + assertArrayEquals(contentArr, buf); + } +} From 85eb05a2eb92a84a3040441145eaaf63d371fa09 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 20 Sep 2021 15:37:18 +0200 Subject: [PATCH 2/2] spotless --- src/org/mozilla/javascript/Kit.java | 182 +++++++++++----------------- 1 file changed, 73 insertions(+), 109 deletions(-) diff --git a/src/org/mozilla/javascript/Kit.java b/src/org/mozilla/javascript/Kit.java index efa5241edf..a2daae1a76 100644 --- a/src/org/mozilla/javascript/Kit.java +++ b/src/org/mozilla/javascript/Kit.java @@ -12,19 +12,14 @@ import java.io.Reader; import java.util.Map; -/** - * Collection of utilities - */ - -public class Kit -{ - public static Class classOrNull(String className) - { +/** Collection of utilities */ +public class Kit { + public static Class classOrNull(String className) { try { return Class.forName(className); - } catch (ClassNotFoundException ex) { - } catch (SecurityException ex) { - } catch (LinkageError ex) { + } catch (ClassNotFoundException ex) { + } catch (SecurityException ex) { + } catch (LinkageError ex) { } catch (IllegalArgumentException e) { // Can be thrown if name has characters that a class name // can not contain @@ -32,12 +27,8 @@ public static Class classOrNull(String className) return null; } - /** - * Attempt to load the class of the given name. Note that the type parameter - * isn't checked. - */ - public static Class classOrNull(ClassLoader loader, String className) - { + /** Attempt to load the class of the given name. Note that the type parameter isn't checked. */ + public static Class classOrNull(ClassLoader loader, String className) { try { return loader.loadClass(className); } catch (ClassNotFoundException ex) { @@ -50,23 +41,19 @@ public static Class classOrNull(ClassLoader loader, String className) return null; } - static Object newInstanceOrNull(Class cl) - { + static Object newInstanceOrNull(Class cl) { try { return cl.newInstance(); } catch (SecurityException x) { - } catch (LinkageError ex) { + } catch (LinkageError ex) { } catch (InstantiationException x) { } catch (IllegalAccessException x) { } return null; } - /** - * Check that testClass is accessible from the given loader. - */ - static boolean testIfCanLoadRhinoClasses(ClassLoader loader) - { + /** Check that testClass is accessible from the given loader. */ + static boolean testIfCanLoadRhinoClasses(ClassLoader loader) { Class testClass = ScriptRuntime.ContextFactoryClass; Class x = Kit.classOrNull(loader, testClass.getName()); if (x != testClass) { @@ -80,17 +67,18 @@ static boolean testIfCanLoadRhinoClasses(ClassLoader loader) } /** - * If character c is a hexadecimal digit, return - * accumulator * 16 plus corresponding - * number. Otherise return -1. + * If character c is a hexadecimal digit, return accumulator * 16 plus + * corresponding number. Otherise return -1. */ - public static int xDigitToInt(int c, int accumulator) - { - check: { + public static int xDigitToInt(int c, int accumulator) { + check: + { // Use 0..9 < A..Z < a..z if (c <= '9') { c -= '0'; - if (0 <= c) { break check; } + if (0 <= c) { + break check; + } } else if (c <= 'F') { if ('A' <= c) { c -= ('A' - 10); @@ -108,12 +96,12 @@ public static int xDigitToInt(int c, int accumulator) } /** - * Add listener to bag of listeners. - * The function does not modify bag and return a new collection - * containing listener and all listeners from bag. - * Bag without listeners always represented as the null value. - *

- * Usage example: + * Add listener to bag of listeners. The function does not modify bag and + * return a new collection containing listener and all listeners from bag. Bag + * without listeners always represented as the null value. + * + *

Usage example: + * *

      *     private volatile Object changeListeners;
      *
@@ -150,22 +138,20 @@ public static int xDigitToInt(int c, int accumulator)
      *
      * @param listener Listener to add to bag
      * @param bag Current collection of listeners.
-     * @return A new bag containing all listeners from bag and
-     *          listener.
+     * @return A new bag containing all listeners from bag and listener.
      * @see #removeListener(Object bag, Object listener)
      * @see #getListener(Object bag, int index)
      */
-    public static Object addListener(Object bag, Object listener)
-    {
+    public static Object addListener(Object bag, Object listener) {
         if (listener == null) throw new IllegalArgumentException();
         if (listener instanceof Object[]) throw new IllegalArgumentException();
 
         if (bag == null) {
             bag = listener;
         } else if (!(bag instanceof Object[])) {
-            bag = new Object[] { bag, listener };
+            bag = new Object[] {bag, listener};
         } else {
-            Object[] array = (Object[])bag;
+            Object[] array = (Object[]) bag;
             int L = array.length;
             // bag has at least 2 elements if it is array
             if (L < 2) throw new IllegalArgumentException();
@@ -179,30 +165,26 @@ public static Object addListener(Object bag, Object listener)
     }
 
     /**
-     * Remove listener from bag of listeners.
-     * The function does not modify bag and return a new collection
-     * containing all listeners from bag except listener.
-     * If bag does not contain listener, the function returns
-     * bag.
-     * 

- * For usage example, see {@link #addListener(Object bag, Object listener)}. + * Remove listener from bag of listeners. The function does not modify bag + * and return a new collection containing all listeners from bag except listener. + * If bag does not contain listener, the function returns bag. + * + *

For usage example, see {@link #addListener(Object bag, Object listener)}. * * @param listener Listener to remove from bag * @param bag Current collection of listeners. - * @return A new bag containing all listeners from bag except - * listener. + * @return A new bag containing all listeners from bag except listener. * @see #addListener(Object bag, Object listener) * @see #getListener(Object bag, int index) */ - public static Object removeListener(Object bag, Object listener) - { + public static Object removeListener(Object bag, Object listener) { if (listener == null) throw new IllegalArgumentException(); if (listener instanceof Object[]) throw new IllegalArgumentException(); if (bag == listener) { bag = null; } else if (bag instanceof Object[]) { - Object[] array = (Object[])bag; + Object[] array = (Object[]) bag; int L = array.length; // bag has at least 2 elements if it is array if (L < 2) throw new IllegalArgumentException(); @@ -231,10 +213,10 @@ public static Object removeListener(Object bag, Object listener) } /** - * Get listener at index position in bag or null if - * index equals to number of listeners in bag. - *

- * For usage example, see {@link #addListener(Object bag, Object listener)}. + * Get listener at index position in bag or null if index equals to number + * of listeners in bag. + * + *

For usage example, see {@link #addListener(Object bag, Object listener)}. * * @param bag Current collection of listeners. * @param index Index of the listener to access. @@ -242,14 +224,11 @@ public static Object removeListener(Object bag, Object listener) * @see #addListener(Object bag, Object listener) * @see #removeListener(Object bag, Object listener) */ - public static Object getListener(Object bag, int index) - { + public static Object getListener(Object bag, int index) { if (index == 0) { - if (bag == null) - return null; - if (!(bag instanceof Object[])) - return bag; - Object[] array = (Object[])bag; + if (bag == null) return null; + if (!(bag instanceof Object[])) return bag; + Object[] array = (Object[]) bag; // bag has at least 2 elements if it is array if (array.length < 2) throw new IllegalArgumentException(); return array[0]; @@ -258,22 +237,20 @@ public static Object getListener(Object bag, int index) if (bag == null) throw new IllegalArgumentException(); return null; } - Object[] array = (Object[])bag; + Object[] array = (Object[]) bag; // the array access will check for index on its own return array[1]; } else { // bag has to array - Object[] array = (Object[])bag; + Object[] array = (Object[]) bag; int L = array.length; if (L < 2) throw new IllegalArgumentException(); - if (index == L) - return null; + if (index == L) return null; return array[index]; } } - static Object initHash(Map h, Object key, Object initialValue) - { + static Object initHash(Map h, Object key, Object initialValue) { synchronized (h) { Object current = h.get(key); if (current == null) { @@ -285,30 +262,25 @@ static Object initHash(Map h, Object key, Object initialValue) return initialValue; } - private final static class ComplexKey - { + private static final class ComplexKey { private Object key1; private Object key2; private int hash; - ComplexKey(Object key1, Object key2) - { + ComplexKey(Object key1, Object key2) { this.key1 = key1; this.key2 = key2; } @Override - public boolean equals(Object anotherObj) - { - if (!(anotherObj instanceof ComplexKey)) - return false; - ComplexKey another = (ComplexKey)anotherObj; + public boolean equals(Object anotherObj) { + if (!(anotherObj instanceof ComplexKey)) return false; + ComplexKey another = (ComplexKey) anotherObj; return key1.equals(another.key1) && key2.equals(another.key2); } @Override - public int hashCode() - { + public int hashCode() { if (hash == 0) { hash = key1.hashCode() ^ key2.hashCode(); } @@ -316,15 +288,13 @@ public int hashCode() } } - public static Object makeHashKeyFromPair(Object key1, Object key2) - { + public static Object makeHashKeyFromPair(Object key1, Object key2) { if (key1 == null) throw new IllegalArgumentException(); if (key2 == null) throw new IllegalArgumentException(); return new ComplexKey(key1, key2); } - public static String readReader(Reader reader) throws IOException - { + public static String readReader(Reader reader) throws IOException { try (BufferedReader in = new BufferedReader(reader)) { char[] cbuf = new char[1024]; StringBuilder sb = new StringBuilder(1024); @@ -336,18 +306,18 @@ public static String readReader(Reader reader) throws IOException } } - public static byte[] readStream(InputStream is, int initialBufferCapacity) - throws IOException - { + public static byte[] readStream(InputStream is, int initialBufferCapacity) throws IOException { if (initialBufferCapacity <= 0) { throw new IllegalArgumentException( - "Bad initialBufferCapacity: "+initialBufferCapacity); + "Bad initialBufferCapacity: " + initialBufferCapacity); } byte[] buffer = new byte[initialBufferCapacity]; int cursor = 0; - for (;;) { + for (; ; ) { int n = is.read(buffer, cursor, buffer.length - cursor); - if (n < 0) { break; } + if (n < 0) { + break; + } cursor += n; if (cursor == buffer.length) { int readahead = -1; @@ -361,7 +331,7 @@ public static byte[] readStream(InputStream is, int initialBufferCapacity) System.arraycopy(buffer, 0, tmp, 0, cursor); buffer = tmp; if (readahead != -1) { - buffer[cursor++] = (byte)readahead; + buffer[cursor++] = (byte) readahead; readahead = -1; } } @@ -375,14 +345,11 @@ public static byte[] readStream(InputStream is, int initialBufferCapacity) } /** - * Throws RuntimeException to indicate failed assertion. - * The function never returns and its return type is RuntimeException - * only to be able to write throw Kit.codeBug() if plain - * Kit.codeBug() triggers unreachable code error. + * Throws RuntimeException to indicate failed assertion. The function never returns and its + * return type is RuntimeException only to be able to write throw Kit.codeBug() if + * plain Kit.codeBug() triggers unreachable code error. */ - public static RuntimeException codeBug() - throws RuntimeException - { + public static RuntimeException codeBug() throws RuntimeException { RuntimeException ex = new IllegalStateException("FAILED ASSERTION"); // Print stack trace ASAP ex.printStackTrace(System.err); @@ -390,14 +357,11 @@ public static RuntimeException codeBug() } /** - * Throws RuntimeException to indicate failed assertion. - * The function never returns and its return type is RuntimeException - * only to be able to write throw Kit.codeBug() if plain - * Kit.codeBug() triggers unreachable code error. + * Throws RuntimeException to indicate failed assertion. The function never returns and its + * return type is RuntimeException only to be able to write throw Kit.codeBug() if + * plain Kit.codeBug() triggers unreachable code error. */ - public static RuntimeException codeBug(String msg) - throws RuntimeException - { + public static RuntimeException codeBug(String msg) throws RuntimeException { msg = "FAILED ASSERTION: " + msg; RuntimeException ex = new IllegalStateException(msg); // Print stack trace ASAP