src/java.base/share/classes/jdk/internal/misc/Unsafe.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File
*** old/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Tue Mar  1 08:25:36 2016
--- new/src/java.base/share/classes/jdk/internal/misc/Unsafe.java	Tue Mar  1 08:25:36 2016

*** 38,47 **** --- 38,56 ---- /** * A collection of methods for performing low-level, unsafe operations. * Although the class and all methods are public, use of this class is * limited because only trusted code can obtain instances of it. * + * <em>Note:</em> It is the resposibility of the caller to make sure + * arguments are checked before methods of this class are + * called. While some rudimentary checks are performed on the input, + * the checks are best effort and when performance is an overriding + * priority, as when methods of this class are optimized by the + * runtime compiler, some or all checks (if any) may be elided. Hence, + * the caller must not rely on the checks and corresponding + * exceptions! + * * @author John R. Rose * @see #getUnsafe */ public final class Unsafe {
*** 356,383 **** --- 365,589 ---- * @see #getAddress(long) */ @HotSpotIntrinsicCandidate public native void putAddress(long address, long x); + + + /// helper methods for validating various types of objects/values + + /** + * Create an exception reflecting that some of the input was invalid + * + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @return an exception object + */ + private RuntimeException invalidInput() { + return new IllegalArgumentException(); + } + + /** + * Check if a value is 32-bit clean (32 MSB are all zero) + * + * @param value the 64-bit value to check + * + * @return true if the value is 32-bit clean + */ + private boolean is32BitClean(long value) { + return value >>> 32 == 0; + } + + /** + * Check the validity of a size (the equivalent of a size_t) + * + * @throws RuntimeException if the size is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkSize(long size) { + if (ADDRESS_SIZE == 4) { + // Note: this will also check for negative sizes + if (!is32BitClean(size)) { + throw invalidInput(); + } + } else if (size < 0) { + throw invalidInput(); + } + } + + /** + * Check the validity of a native address (the equivalent of void*) + * + * @throws RuntimeException if the address is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkNativeAddress(long address) { + if (ADDRESS_SIZE == 4) { + // Accept both zero and sign extended pointers. A valid + // pointer will, after the +1 below, either have produced + // the value 0x0 or 0x1. Masking off the low bit allows + // for testing against 0. + if ((((address >> 32) + 1) & ~1) != 0) { + throw invalidInput(); + } + } + } + + /** + * Check the validity of an offset, relative to a base object + * + * @param o the base object + * @param offset the offset to check + * + * @throws RuntimeException if the size is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkOffset(Object o, long offset) { + if (ADDRESS_SIZE == 4) { + // Note: this will also check for negative offsets + if (!is32BitClean(offset)) { + throw invalidInput(); + } + } else if (offset < 0) { + throw invalidInput(); + } + } + + /** + * Check the validity of a double-register pointer + * + * Note: This code deliberately does *not* check for NPE for (at + * least) three reasons: + * + * 1) NPE is not just NULL/0 - there is a range of values all + * resulting in an NPE, which is not trivial to check for + * + * 2) It is the responsibility of the callers of Unsafe methods + * to verify the input, so throwing an exception here is not really + * useful - passing in a NULL pointer is a critical error and the + * must not expect an exception to be thrown anyway. + * + * 3) the actual operations will detect NULL pointers anyway by + * means of traps and signals (like SIGSEGV). + * + * @param o Java heap object, or null + * @param offset indication of where the variable resides in a Java heap + * object, if any, else a memory address locating the variable + * statically + * + * @throws RuntimeException if the pointer is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkPointer(Object o, long offset) { + if (o == null) { + checkNativeAddress(offset); + } else { + checkOffset(o, offset); + } + } + + /** + * Check if a type is a primitive array type + * + * @param c the type to check + * + * @return true if the type is a primitive array type + */ + private void checkPrimitiveArray(Class<?> c) { + Class<?> componentType = c.getComponentType(); + if (componentType == null || !componentType.isPrimitive()) { + throw invalidInput(); + } + } + + /** + * Check that a pointer is a valid primitive array type pointer + * + * Note: pointers off-heap are considered to be primitive arrays + * + * @throws RuntimeException if the pointer is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void checkPrimitivePointer(Object o, long offset) { + checkPointer(o, offset); + + if (o != null) { + // If on heap, it it must be a primitive array + checkPrimitiveArray(o.getClass()); + } + } + + /// wrappers for malloc, realloc, free: /** * Allocates a new block of native memory, of the given size in bytes. The * contents of the memory are uninitialized; they will generally be * garbage. The resulting native pointer will never be zero, and will be * aligned for all value types. Dispose of this memory by calling {@link * #freeMemory}, or resize it with {@link #reallocateMemory}. * * @throws IllegalArgumentException if the size is negative or too large + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system * * @see #getByte(long) * @see #putByte(long, byte) */ ! public native long allocateMemory(long bytes); ! public long allocateMemory(long bytes) { + allocateMemoryChecks(bytes); + + if (bytes == 0) { + return 0; + } + + long p = allocateMemory0(bytes); + if (p == 0) { + throw new OutOfMemoryError(); + } + + return p; + } + + /** + * Validate the arguments to allocateMemory + * + * @throws RuntimeException if the arguments are invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void allocateMemoryChecks(long bytes) { + checkSize(bytes); + } /** * Resizes a new block of native memory, to the given size in bytes. The * contents of the new block past the size of the old block are * uninitialized; they will generally be garbage. The resulting native
*** 385,402 **** --- 591,644 ---- * resulting native pointer will be aligned for all value types. Dispose * of this memory by calling {@link #freeMemory}, or resize it with {@link * #reallocateMemory}. The address passed to this method may be null, in * which case an allocation will be performed. * * @throws IllegalArgumentException if the size is negative or too large + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if the size is negative or too large * for the native size_t type * * @throws OutOfMemoryError if the allocation is refused by the system * * @see #allocateMemory */ ! public native long reallocateMemory(long address, long bytes); ! public long reallocateMemory(long address, long bytes) { + reallocateMemoryChecks(address, bytes); + + if (bytes == 0) { + freeMemory(address); + return 0; + } + + long p = (address == 0) ? allocateMemory0(bytes) : reallocateMemory0(address, bytes); + if (p == 0) { + throw new OutOfMemoryError(); + } + + return p; + } + + /** + * Validate the arguments to reallocateMemory + * + * @throws RuntimeException if the arguments are invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void reallocateMemoryChecks(long address, long bytes) { + checkPointer(null, address); + checkSize(bytes); + } /** * Sets all bytes in a given block of memory to a fixed value * (usually zero). *
*** 409,421 **** --- 651,682 ---- * by the address and length parameters. If the effective address and * length are all even modulo 8, the stores take place in 'long' units. * If the effective address and length are (resp.) even modulo 4 or 2, * the stores take place in units of 'int' or 'short'. * + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ ! public native void setMemory(Object o, long offset, long bytes, byte value); ! public void setMemory(Object o, long offset, long bytes, byte value) { + setMemoryChecks(o, offset, bytes, value); + + if (bytes == 0) { + return; + } + + setMemory0(o, offset, bytes, value); + } /** * Sets all bytes in a given block of memory to a fixed value * (usually zero). This provides a <em>single-register</em> addressing mode, * as discussed in {@link #getInt(Object,long)}.
*** 425,434 **** --- 686,708 ---- public void setMemory(long address, long bytes, byte value) { setMemory(null, address, bytes, value); } /** + * Validate the arguments to setMemory + * + * @throws RuntimeException if the arguments are invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void setMemoryChecks(Object o, long offset, long bytes, byte value) { + checkPrimitivePointer(o, offset); + checkSize(bytes); + } + + /** * Sets all bytes in a given block of memory to a copy of another * block. * * <p>This method determines each block's base address by means of two parameters, * and so it provides (in effect) a <em>double-register</em> addressing mode,
*** 439,454 **** --- 713,747 ---- * by the address and length parameters. If the effective addresses and * length are all even modulo 8, the transfer takes place in 'long' units. * If the effective addresses and length are (resp.) even modulo 4 or 2, * the transfer takes place in units of 'int' or 'short'. * + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 1.7 */ @HotSpotIntrinsicCandidate public native void copyMemory(Object srcBase, long srcOffset, + public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + long bytes) { + copyMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes); + + if (bytes == 0) { + return; + } + + copyMemory0(srcBase, srcOffset, destBase, destOffset, bytes); + } + /** * Sets all bytes in a given block of memory to a copy of another * block. This provides a <em>single-register</em> addressing mode, * as discussed in {@link #getInt(Object,long)}. *
*** 456,522 **** --- 749,822 ---- */ public void copyMemory(long srcAddress, long destAddress, long bytes) { copyMemory(null, srcAddress, null, destAddress, bytes); } private boolean isPrimitiveArray(Class<?> c) { Class<?> componentType = c.getComponentType(); return componentType != null && componentType.isPrimitive(); } private native void copySwapMemory0(Object srcBase, long srcOffset, + /** + * Validate the arguments to copyMemory + * + * @throws RuntimeException if any of the arguments is invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void copyMemoryChecks(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize); + long bytes) { + checkSize(bytes); + checkPrimitivePointer(srcBase, srcOffset); + checkPrimitivePointer(destBase, destOffset); + } /** * Copies all elements from one block of memory to another block, * *unconditionally* byte swapping the elements on the fly. * * <p>This method determines each block's base address by means of two parameters, * and so it provides (in effect) a <em>double-register</em> addressing mode, * as discussed in {@link #getInt(Object,long)}. When the object reference is null, * the offset supplies an absolute base address. * + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @since 9 */ public void copySwapMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize) { if (bytes < 0) { throw new IllegalArgumentException(); } if (elemSize != 2 && elemSize != 4 && elemSize != 8) { throw new IllegalArgumentException(); } if (bytes % elemSize != 0) { throw new IllegalArgumentException(); } if ((srcBase == null && srcOffset == 0) || (destBase == null && destOffset == 0)) { throw new NullPointerException(); } + copySwapMemoryChecks(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); // Must be off-heap, or primitive heap arrays if (srcBase != null && (srcOffset < 0 || !isPrimitiveArray(srcBase.getClass()))) { throw new IllegalArgumentException(); } if (destBase != null && (destOffset < 0 || !isPrimitiveArray(destBase.getClass()))) { throw new IllegalArgumentException(); + if (bytes == 0) { + return; } // Sanity check size and offsets on 32-bit platforms. Most // significant 32 bits must be zero. if (ADDRESS_SIZE == 4 && (bytes >>> 32 != 0 || srcOffset >>> 32 != 0 || destOffset >>> 32 != 0)) { throw new IllegalArgumentException(); + copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); } if (bytes == 0) { return; + private void copySwapMemoryChecks(Object srcBase, long srcOffset, + Object destBase, long destOffset, + long bytes, long elemSize) { + checkSize(bytes); + + if (elemSize != 2 && elemSize != 4 && elemSize != 8) { + throw invalidInput(); + } + if (bytes % elemSize != 0) { + throw invalidInput(); } ! copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize); ! checkPrimitivePointer(srcBase, srcOffset); + checkPrimitivePointer(destBase, destOffset); } /** * Copies all elements from one block of memory to another block, byte swapping the * elements on the fly.
*** 533,545 **** --- 833,876 ---- /** * Disposes of a block of native memory, as obtained from {@link * #allocateMemory} or {@link #reallocateMemory}. The address passed to * this method may be null, in which case no action is taken. * + * <em>Note:</em> It is the resposibility of the caller to make + * sure arguments are checked before the methods are called. While + * some rudimentary checks are performed on the input, the checks + * are best effort and when performance is an overriding priority, + * as when methods of this class are optimized by the runtime + * compiler, some or all checks (if any) may be elided. Hence, the + * caller must not rely on the checks and corresponding + * exceptions! + * + * @throws RuntimeException if any of the arguments is invalid + * * @see #allocateMemory */ ! public native void freeMemory(long address); ! public void freeMemory(long address) { + freeMemoryChecks(address); + + if (address == 0) { + return; + } + + freeMemory0(address); + } + + /** + * Validate the arguments to freeMemory + * + * @throws RuntimeException if the arguments are invalid + * (<em>Note:</em> after optimization, invalid inputs may + * go undetected, which will lead to unpredictable + * behavior) + */ + private void freeMemoryChecks(long address) { + checkPointer(null, address); + } /// random queries /** * This constant differs from all results that will ever be returned from
*** 564,574 **** --- 895,911 ---- * the field locations in a form usable by {@link #getInt(Object,long)}. * Therefore, code which will be ported to such JVMs on 64-bit platforms * must preserve all bits of static field offsets. * @see #getInt(Object, long) */ ! public native long objectFieldOffset(Field f); ! public long objectFieldOffset(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return objectFieldOffset0(f); + } /** * Reports the location of a given static field, in conjunction with {@link * #staticFieldBase}. * <p>Do not expect to perform any sort of arithmetic on this offset;
*** 583,593 **** --- 920,936 ---- * a few bits to encode an offset within a non-array object, * However, for consistency with other methods in this class, * this method reports its result as a long value. * @see #getInt(Object, long) */ ! public native long staticFieldOffset(Field f); ! public long staticFieldOffset(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return staticFieldOffset0(f); + } /** * Reports the location of a given static field, in conjunction with {@link * #staticFieldOffset}. * <p>Fetch the base "Object", if any, with which static fields of the
*** 595,620 **** --- 938,981 ---- * long)}. This value may be null. This value may refer to an object * which is a "cookie", not guaranteed to be a real Object, and it should * not be used in any way except as argument to the get and put routines in * this class. */ ! public native Object staticFieldBase(Field f); ! public Object staticFieldBase(Field f) { + if (f == null) { + throw new NullPointerException(); + } + + return staticFieldBase0(f); + } /** * Detects if the given class may need to be initialized. This is often * needed in conjunction with obtaining the static field base of a * class. * @return false only if a call to {@code ensureClassInitialized} would have no effect */ ! public native boolean shouldBeInitialized(Class<?> c); ! public boolean shouldBeInitialized(Class<?> c) { + if (c == null) { + throw new NullPointerException(); + } + + return shouldBeInitialized0(c); + } /** * Ensures the given class has been initialized. This is often * needed in conjunction with obtaining the static field base of a * class. */ ! public native void ensureClassInitialized(Class<?> c); ! public void ensureClassInitialized(Class<?> c) { + if (c == null) { + throw new NullPointerException(); + } + + ensureClassInitialized0(c); + } /** * Reports the offset of the first element in the storage allocation of a * given array class. If {@link #arrayIndexScale} returns a non-zero value * for the same class, you may use that scale factor, together with this
*** 622,632 **** --- 983,1000 ---- * given class. * * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ ! public native int arrayBaseOffset(Class<?> arrayClass); ! public int arrayBaseOffset(Class<?> arrayClass) { + if (arrayClass == null) { + throw new NullPointerException(); + } + + return arrayBaseOffset0(arrayClass); + } + /** The value of {@code arrayBaseOffset(boolean[].class)} */ public static final int ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);
*** 671,681 **** --- 1039,1056 ---- * * @see #arrayBaseOffset * @see #getInt(Object, long) * @see #putInt(Object, long, int) */ ! public native int arrayIndexScale(Class<?> arrayClass); ! public int arrayIndexScale(Class<?> arrayClass) { + if (arrayClass == null) { + throw new NullPointerException(); + } + + return arrayIndexScale0(arrayClass); + } + /** The value of {@code arrayIndexScale(boolean[].class)} */ public static final int ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
*** 715,728 **** --- 1090,1105 ---- * Reports the size in bytes of a native pointer, as stored via {@link * #putAddress}. This value will be either 4 or 8. Note that the sizes of * other primitive types (as stored in native memory blocks) is determined * fully by their information content. */ ! public native int addressSize(); ! public int addressSize() { + return ADDRESS_SIZE; + } /** The value of {@code addressSize()} */ ! public static final int ADDRESS_SIZE = theUnsafe.addressSize0(); /** * Reports the size in bytes of a native memory page (whatever that is). * This value will always be a power of two. */
*** 733,743 **** --- 1110,1133 ---- /** * Tells the VM to define a class, without security checks. By default, the * class loader and protection domain come from the caller's class. */ - public native Class<?> defineClass(String name, byte[] b, int off, int len, + ClassLoader loader, + ProtectionDomain protectionDomain) { + if (b == null) { + throw new NullPointerException(); + } + if (len < 0) { + throw new ArrayIndexOutOfBoundsException(); + } + + return defineClass0(name, b, off, len, loader, protectionDomain); + } + + public native Class<?> defineClass0(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); /** * Defines a class but does not make it known to the class loader or system dictionary.
*** 753,763 **** --- 1143,1159 ---- * </ul> * @param hostClass context for linkage, access control, protection domain, and class loader * @param data bytes of a class file * @param cpPatches where non-null entries exist, they replace corresponding CP entries in data */ ! public native Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches); ! public Class<?> defineAnonymousClass(Class<?> hostClass, byte[] data, Object[] cpPatches) { + if (hostClass == null || data == null) { + throw new NullPointerException(); + } + + return defineAnonymousClass0(hostClass, data, cpPatches); + } /** * Allocates an instance but does not run any constructor. * Initializes the class if it has not yet been. */
*** 1288,1298 **** --- 1684,1700 ---- * must be 1 to 3. * * @return the number of samples actually retrieved; or -1 * if the load average is unobtainable. */ ! public native int getLoadAverage(double[] loadavg, int nelems); ! public int getLoadAverage(double[] loadavg, int nelems) { + if (nelems < 0 || nelems > 3 || nelems > loadavg.length) { + throw new ArrayIndexOutOfBoundsException(); + } + + return getLoadAverage0(loadavg, nelems); + } // The following contain CAS-based Java implementations used on // platforms not supporting native instructions /**
*** 1716,1728 **** --- 2118,2127 ---- public final void putCharUnaligned(Object o, long offset, char x, boolean bigEndian) { putCharUnaligned(o, offset, convEndian(bigEndian, x)); } // JVM interface methods private native boolean unalignedAccess0(); private native boolean isBigEndian0(); // BE is true iff the native endianness of this platform is big. private static final boolean BE = theUnsafe.isBigEndian0(); // unalignedAccess is true iff this platform can perform unaligned accesses. private static final boolean unalignedAccess = theUnsafe.unalignedAccess0();
*** 1818,1823 **** --- 2217,2244 ---- // Maybe byte-reverse an integer private static char convEndian(boolean big, char n) { return big == BE ? n : Character.reverseBytes(n); } private static short convEndian(boolean big, short n) { return big == BE ? n : Short.reverseBytes(n) ; } private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; } private static long convEndian(boolean big, long n) { return big == BE ? n : Long.reverseBytes(n) ; } + + + + private native long allocateMemory0(long bytes); + private native long reallocateMemory0(long address, long bytes); + private native void freeMemory0(long address); + private native void setMemory0(Object o, long offset, long bytes, byte value); + @HotSpotIntrinsicCandidate + private native void copyMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + private native void copySwapMemory0(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes, long elemSize); + private native long objectFieldOffset0(Field f); + private native long staticFieldOffset0(Field f); + private native Object staticFieldBase0(Field f); + private native boolean shouldBeInitialized0(Class<?> c); + private native void ensureClassInitialized0(Class<?> c); + private native int arrayBaseOffset0(Class<?> arrayClass); + private native int arrayIndexScale0(Class<?> arrayClass); + private native int addressSize0(); + private native Class<?> defineAnonymousClass0(Class<?> hostClass, byte[] data, Object[] cpPatches); + private native int getLoadAverage0(double[] loadavg, int nelems); + private native boolean unalignedAccess0(); + private native boolean isBigEndian0(); }

src/java.base/share/classes/jdk/internal/misc/Unsafe.java
Index Unified diffs Context diffs Sdiffs Patch New Old Previous File Next File