diff --git a/AGENTS.md b/AGENTS.md index cade775..1b09621 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,7 +49,7 @@ Node, which is how the tests run. - `src/natives/` JS implementations of JDK native methods, by package; `java2d*.ts` port libawt's Java2D loops and must match HotSpot's pixels; `font*.ts` are the font natives (scaler, fontconfig, layout) over one TrueType font; - `crypto.ts` the SHA and BigInteger methods HotSpot replaces with intrinsics + `crypto.ts` the SHA, AES and BigInteger methods HotSpot replaces with intrinsics - `src/sockets.ts` sockets (`Sock`, POSIX readiness) and the in-VM loopback backend; `src/net-node.ts` host sockets, DNS and interfaces for Node - `src/node.ts` Node entry, over `build/jdk` or, built as `dist/node.js`, the diff --git a/src/jit.ts b/src/jit.ts index 702a4f8..b9f604c 100644 --- a/src/jit.ts +++ b/src/jit.ts @@ -137,6 +137,11 @@ const STRING_BUILDER = "java/lang/StringBuilder"; const LONG_MIN = -(1n << 63n), LONG_MAX = (1n << 63n) - 1n; +const VH = "java/lang/invoke/VarHandle"; +const VH_AD = "java/lang/invoke/VarHandle$AccessDescriptor"; +const VF = "java/lang/invoke/VarForm"; +const VH_CHECK = "checkAccessModeThenIsDirect(Ljava/lang/invoke/VarHandle$AccessDescriptor;)Z"; + const runtime = { JObject, JArray, Frame, NativeFrame, JavaThrow, PendingThrow, newArrayData, hooks, @@ -149,6 +154,27 @@ const runtime = { return memberTarget(t, t.vm.getField(form, "java/lang/invoke/LambdaForm", "vmentry")); }, memberTarget, + // Fills a VarHandle call site's cache [handle, implementation] when the + // guard would link straight to the implementation: it decides from the + // handle's final and stable fields and the site's AccessDescriptor, so the + // same handle always takes the same path. Indirect handles, and exact ones + // whose type check has not passed yet, are left uncached. + vhTarget(t: JThread, cell: any[], h: JObject | null, ad: JObject) { + const vm = t.vm; + // IndirectVarHandle overrides the check and is never direct + if (h === null || h.cls.selectMethod(VH_CHECK)?.cls.name !== VH) return; + if (vm.getField(h, VH, "exact")) { + const exact = (vm.getField(h, VH, "methodTypeTable") as JArray | null)?.a[vm.getField(ad, VH_AD, "type")]; + if (!exact || exact !== vm.getField(ad, VH_AD, "symbolicMethodTypeExact")) return; + } + const vform = vm.getField(h, VH, "vform") as JObject; + const types = vm.getField(vform, VF, "methodType_table") as JArray; + if (types.a[vm.getField(ad, VH_AD, "type")] !== vm.getField(ad, VH_AD, "symbolicMethodTypeErased")) return; + const mn = (vm.getField(vform, VF, "memberName_table") as JArray | null)?.a[vm.getField(ad, VH_AD, "mode")] as JObject | null; + if (!mn) return; + cell[1] = memberTarget(t, mn); + cell[0] = h; + }, virtualTarget(t: JThread, mn: JObject, recv: JObject | null, pc: number): JMethod { const m = memberTarget(t, mn); if (recv === null) npeAt(pc); @@ -1196,7 +1222,15 @@ function genPolymorphic(g: Gen, pc: number, next: number, d: number, target: JMe let dd = d; let code = ""; if (link.m.argSlots === n + 1) { code = `s${dd}=${K(link.appendix)};`; dd++; } - return code + genInvoke(g, 0xb8, pc, next, dd, link.m, g.top.m.cls, d, false); + code += genInvoke(g, 0xb8, pc, next, dd, link.m, g.top.m.cls, d, false); + if (link.m.cls.name !== "java/lang/invoke/VarHandleGuards" || !link.appendix) return code; + // the handle seen last here: call its implementation directly + const cell = K([null, null]); + const push = target.ret === "V" ? "" : `s${base}=t.ret;`; + return `if(${args[0]}!==null&&${args[0]}===${cell}[0]){f.pc=${pc};tm=${cell}[1];` + + `if(tm.jit!==null&&tm.cls.state===${INITIALIZED}&&(cf=rt.enter(t,tm,null))!==null){` + + `r=tm.jit(t,cf${args.map((x) => "," + x).join("")});if(r!==0){t.unpool(cf);${exit(pc, base, EXIT_SUSPEND)}}${push}}` + + `else{${call(pc, d)}}}else{rt.vhTarget(t,${cell},${args[0]},${K(link.appendix)});${code}}`; } } const push = target.ret === "V" ? "" : `s${base}=t.ret;`; diff --git a/src/natives/crypto.ts b/src/natives/crypto.ts index 55db96e..f1008dd 100644 --- a/src/natives/crypto.ts +++ b/src/natives/crypto.ts @@ -1,7 +1,7 @@ -// The methods HotSpot replaces with intrinsics for SHA digests and BigInteger -// arithmetic. As bytecode they run on Java longs, which are JS BigInts here, -// so a 64-bit step allocates; these do the same work on numbers, or on one -// BigInt for a whole multiplication. +// The methods HotSpot replaces with intrinsics for SHA digests, AES blocks +// and BigInteger arithmetic. As bytecode they run on Java longs, which are JS +// BigInts here, so a 64-bit step allocates; these do the same work on +// numbers, or on one BigInt for a whole multiplication. import type { JArray, JObject } from "../runtime.ts"; import type { JThread } from "../thread.ts"; @@ -12,6 +12,7 @@ const SHA1 = "sun/security/provider/SHA"; const SHA2 = "sun/security/provider/SHA2"; const SHA5 = "sun/security/provider/SHA5"; const BI = "java/math/BigInteger"; +const AES = "com/sun/crypto/provider/AESCrypt"; // the `state` field's slot in each digest class const stateSlots = new WeakMap>(); @@ -213,7 +214,56 @@ function montgomery(a: bigint, b: bigint, nArr: Int32Array, len: number, inv: bi return product; } +// AESCrypt's round tables and the slots of its key schedule fields +interface Aes { T1: Int32Array; T2: Int32Array; T3: Int32Array; T4: Int32Array; S: Int8Array; K: number; limit: number } +const aesTables = new WeakMap(); +function aes(t: JThread, self: JObject): Aes { + let a = aesTables.get(t.vm); + if (!a) { + const vm = t.vm, c = self.cls; + const table = (name: string) => (vm.getStatic(c, name) as JArray).a as any; + a = { + T1: table("T1"), T2: table("T2"), T3: table("T3"), T4: table("T4"), S: table("S"), + K: vm.slotOf(AES, "K"), limit: vm.slotOf(AES, "limit"), + }; + aesTables.set(vm, a); + } + return a; +} + +// the last round's four output bytes for one column +function aesLast(out: Int8Array, o: number, S: Int8Array, a: number, b: number, c: number, d: number, k: number) { + out[o] = S[a >>> 24] ^ (k >>> 24); + out[o + 1] = S[(b >>> 16) & 255] ^ (k >>> 16); + out[o + 2] = S[(c >>> 8) & 255] ^ (k >>> 8); + out[o + 3] = S[d & 255] ^ k; +} + +// AESCrypt.implEncryptBlock. Like HotSpot's intrinsic it does no bounds checks: +// its callers (FeedbackCipher.encrypt and the modes) make them. +function aesEncrypt(t: JThread, self: JObject, inp: Int8Array, i: number, out: Int8Array, o: number) { + const { T1, T2, T3, T4, S, K: kSlot, limit: limitSlot } = aes(t, self); + const K = (self.f[kSlot] as JArray).a as Int32Array, limit = self.f[limitSlot] as number; + let t0 = word(inp, i) ^ K[0], t1 = word(inp, i + 4) ^ K[1], t2 = word(inp, i + 8) ^ K[2], t3 = word(inp, i + 12) ^ K[3]; + let k = 4; + while (k < limit) { + const a0 = T1[t0 >>> 24] ^ T2[(t1 >>> 16) & 255] ^ T3[(t2 >>> 8) & 255] ^ T4[t3 & 255] ^ K[k]; + const a1 = T1[t1 >>> 24] ^ T2[(t2 >>> 16) & 255] ^ T3[(t3 >>> 8) & 255] ^ T4[t0 & 255] ^ K[k + 1]; + const a2 = T1[t2 >>> 24] ^ T2[(t3 >>> 16) & 255] ^ T3[(t0 >>> 8) & 255] ^ T4[t1 & 255] ^ K[k + 2]; + t3 = T1[t3 >>> 24] ^ T2[(t0 >>> 16) & 255] ^ T3[(t1 >>> 8) & 255] ^ T4[t2 & 255] ^ K[k + 3]; + t0 = a0; t1 = a1; t2 = a2; + k += 4; + } + aesLast(out, o, S, t0, t1, t2, t3, K[k]); + aesLast(out, o + 4, S, t1, t2, t3, t0, K[k + 1]); + aesLast(out, o + 8, S, t2, t3, t0, t1, K[k + 2]); + aesLast(out, o + 12, S, t3, t0, t1, t2, K[k + 3]); +} + export const intrinsics: Natives = { + [`${AES}.implEncryptBlock([BI[BI)V`]: (t, [self, inp, i, out, o]) => + aesEncrypt(t, self, (inp as JArray).a as Int8Array, i, (out as JArray).a as Int8Array, o), + [`${SHA1}.implCompress0([BI)V`]: (t, [self, buf, ofs]) => sha1(state(t, self, SHA1).a as Int32Array, (buf as JArray).a as Int8Array, ofs), [`${SHA2}.implCompress0([BI)V`]: (t, [self, buf, ofs]) => diff --git a/src/natives/misc.ts b/src/natives/misc.ts index ff75f65..f200053 100644 --- a/src/natives/misc.ts +++ b/src/natives/misc.ts @@ -20,33 +20,38 @@ export function indexScale(c: JClass): number { return p ? WIDTH[p] : REF_SCALE; } +// A primitive array's DataView, kept in x (unused otherwise for arrays, and +// not copied by clone). function view(a: JArray): DataView { - const ta = a.a as Int8Array; - return new DataView(ta.buffer, ta.byteOffset, ta.byteLength); + const v = a.x as DataView | undefined; + if (v !== undefined) return v; + const d = a.a as Int8Array; + return (a.x = new DataView(d.buffer, d.byteOffset, d.byteLength)); } -function dvRead(dv: DataView, p: number, k: string): any { +// native order is little-endian +function dvRead(dv: DataView, p: number, k: string, le = true): any { switch (k) { case "Z": return dv.getInt8(p) !== 0 ? 1 : 0; case "B": return dv.getInt8(p); - case "S": return dv.getInt16(p, true); - case "C": return dv.getUint16(p, true); - case "I": return dv.getInt32(p, true); - case "J": return dv.getBigInt64(p, true); - case "F": return dv.getFloat32(p, true); - case "D": return dv.getFloat64(p, true); + case "S": return dv.getInt16(p, le); + case "C": return dv.getUint16(p, le); + case "I": return dv.getInt32(p, le); + case "J": return dv.getBigInt64(p, le); + case "F": return dv.getFloat32(p, le); + case "D": return dv.getFloat64(p, le); } throw new Error(`bad kind ${k}`); } -function dvWrite(dv: DataView, p: number, k: string, v: any) { +function dvWrite(dv: DataView, p: number, k: string, v: any, le = true) { switch (k) { case "Z": case "B": dv.setInt8(p, v); return; - case "S": case "C": dv.setInt16(p, v, true); return; - case "I": dv.setInt32(p, v, true); return; - case "J": dv.setBigInt64(p, v, true); return; - case "F": dv.setFloat32(p, v, true); return; - case "D": dv.setFloat64(p, v, true); return; + case "S": case "C": dv.setInt16(p, v, le); return; + case "I": dv.setInt32(p, v, le); return; + case "J": dv.setBigInt64(p, v, le); return; + case "F": dv.setFloat32(p, v, le); return; + case "D": dv.setFloat64(p, v, le); return; } throw new Error(`bad kind ${k}`); } @@ -135,6 +140,39 @@ export const intrinsics: Natives = { [`${U}compareAndExchangeShort(Ljava/lang/Object;JSS)S`]: (t, [, o, off, e, v]) => cas(t, o, off, "S", e, v), }; +// Unaligned access, intrinsics in HotSpot too: one native-order read or write +// instead of the Java version's byte-by-byte assembly. +// a primitive array access that the element-indexed path cannot do +function unaligned(o: JObject | null, off: number, k: string): o is JArray { + if (!(o instanceof JArray)) return false; + const elem = o.cls.component!.prim; + return !!elem && !(sameKind(elem, k) && (off - ARRAY_BASE_OFFSET) % WIDTH[k] === 0); +} +// Unsafe.convEndian, through a shared scratch view: a VM runs its Java +// threads on one JS thread and this never yields, so calls cannot interleave. +const swapView = new DataView(new ArrayBuffer(8)); +function convEndian(k: string, v: any, bigEndian: boolean): any { + if (!bigEndian) return v; + dvWrite(swapView, 0, k, v); + return dvRead(swapView, 0, k, false); +} +for (const [name, k] of [["Long", "J"], ["Int", "I"], ["Short", "S"], ["Char", "C"]]) { + const get = (t: JThread, o: JObject | null, offset: bigint, bigEndian: boolean) => { + const off = Number(offset); + if (unaligned(o, off, k)) return dvRead(view(o), off - ARRAY_BASE_OFFSET, k, !bigEndian); + return convEndian(k, unsafeGet(t, o, offset, k), bigEndian); + }; + const put = (t: JThread, o: JObject | null, offset: bigint, v: any, bigEndian: boolean) => { + const off = Number(offset); + if (unaligned(o, off, k)) dvWrite(view(o), off - ARRAY_BASE_OFFSET, k, v, !bigEndian); + else unsafePut(t, o, offset, k, convEndian(k, v, bigEndian)); + }; + intrinsics[`${U}get${name}Unaligned(Ljava/lang/Object;J)${k}`] = (t, [, o, off]) => get(t, o, off, false); + intrinsics[`${U}get${name}Unaligned(Ljava/lang/Object;JZ)${k}`] = (t, [, o, off, be]) => get(t, o, off, be !== 0); + intrinsics[`${U}put${name}Unaligned(Ljava/lang/Object;J${k})V`] = (t, [, o, off, v]) => put(t, o, off, v, false); + intrinsics[`${U}put${name}Unaligned(Ljava/lang/Object;J${k}Z)V`] = (t, [, o, off, v, be]) => put(t, o, off, v, be !== 0); +} + function* park(t: JThread, absolute: boolean, time: bigint): NativeGen { if (t.parkPermit) { t.parkPermit = false; diff --git a/test/java/CryptoMath.java b/test/java/CryptoMath.java index 9041b63..0794734 100644 --- a/test/java/CryptoMath.java +++ b/test/java/CryptoMath.java @@ -4,10 +4,13 @@ import java.security.SecureRandom; import java.security.interfaces.RSAPrivateCrtKey; import java.util.HexFormat; +import java.util.Arrays; +import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -// SHA digests and BigInteger arithmetic, at sizes that reach their intrinsics. +// SHA digests, AES and BigInteger arithmetic, at sizes that reach their intrinsics. public class CryptoMath { static final HexFormat HEX = HexFormat.of(); @@ -28,6 +31,25 @@ public static void main(String[] args) throws Exception { mac.init(new SecretKeySpec(data, 0, 40, "HmacSHA512")); System.out.println("hmac " + HEX.formatHex(mac.doFinal(data))); + for (int keyLen : new int[] {16, 24, 32}) { + SecretKeySpec key = new SecretKeySpec(data, keyLen, keyLen, "AES"); + IvParameterSpec iv = new IvParameterSpec(data, 100, 16); + for (String mode : new String[] {"AES/ECB/NoPadding", "AES/CBC/PKCS5Padding", "AES/CTR/NoPadding"}) { + Cipher c = Cipher.getInstance(mode); + c.init(Cipher.ENCRYPT_MODE, key, mode.contains("ECB") ? null : iv); + byte[] enc = c.doFinal(data, 5, 160); + c.init(Cipher.DECRYPT_MODE, key, mode.contains("ECB") ? null : iv); + boolean back = Arrays.equals(c.doFinal(enc), Arrays.copyOfRange(data, 5, 165)); + System.out.println(mode + " " + keyLen + " " + HEX.formatHex(MessageDigest.getInstance("SHA-256").digest(enc), 0, 8) + " " + back); + } + // in place, in and out overlapping + Cipher c = Cipher.getInstance("AES/ECB/NoPadding"); + c.init(Cipher.ENCRYPT_MODE, key); + byte[] buf = Arrays.copyOf(data, 64); + c.doFinal(buf, 0, 48, buf, 16); + System.out.println("in place " + keyLen + " " + HEX.formatHex(buf, 16, 40)); + } + SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG"); rnd.setSeed(42); for (int bits : new int[] {31, 64, 100, 512, 1024, 2048, 3000, 4100}) { diff --git a/test/java/Unaligned.java b/test/java/Unaligned.java new file mode 100644 index 0000000..bcf84bd --- /dev/null +++ b/test/java/Unaligned.java @@ -0,0 +1,70 @@ +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.HexFormat; + +// Multi-byte reads and writes at every alignment, in both byte orders, through +// byte array views, heap and direct buffers, and views of other element types, +// and native-order reads across the elements of other primitive arrays. +public class Unaligned { + public static void main(String[] args) { + HexFormat hex = HexFormat.of(); + for (ByteOrder order : new ByteOrder[] {ByteOrder.BIG_ENDIAN, ByteOrder.LITTLE_ENDIAN}) { + VarHandle ints = MethodHandles.byteArrayViewVarHandle(int[].class, order); + VarHandle longs = MethodHandles.byteArrayViewVarHandle(long[].class, order); + VarHandle shorts = MethodHandles.byteArrayViewVarHandle(short[].class, order); + VarHandle chars = MethodHandles.byteArrayViewVarHandle(char[].class, order); + for (int at = 0; at < 8; at++) { + byte[] b = new byte[24]; + ints.set(b, at, 0x81828384); + longs.set(b, at + 4, 0x0102030405060708L); + shorts.set(b, at + 12, (short) -2); + chars.set(b, at + 14, 'ꯍ'); + System.out.println(order + " " + at + " " + hex.formatHex(b) + " " + Integer.toHexString((int) ints.get(b, at)) + + " " + Long.toHexString((long) longs.get(b, at + 4)) + " " + (short) shorts.get(b, at + 12) + + " " + Integer.toHexString((char) chars.get(b, at + 14)) + " " + Long.toHexString((long) longs.get(b, at + 1))); + } + for (ByteBuffer buf : new ByteBuffer[] {ByteBuffer.allocate(32), ByteBuffer.allocateDirect(32)}) { + buf.order(order); + for (int at = 0; at < 5; at++) { + buf.putLong(at, -0x123456789abcdefL).putInt(at + 9, 0x7f00ff01).putShort(at + 14, (short) 0x8001).putChar(at + 17, 'Z') + .putDouble(at + 20, Math.PI).putFloat(at + 28 - at, 1.5f); + System.out.println((buf.isDirect() ? "direct " : "heap ") + order + " " + at + " " + Long.toHexString(buf.getLong(at)) + + " " + buf.getInt(at + 9) + " " + buf.getShort(at + 14) + " " + buf.getChar(at + 17) + " " + buf.getDouble(at + 20) + + " " + buf.getFloat(28) + " " + buf.getInt(at + 1)); + } + } + // a clone written through a view after the original was + byte[] orig = new byte[8]; + ints.set(orig, 1, 0x11223344); + byte[] copy = orig.clone(); + ints.set(copy, 1, 0x55667788); + System.out.println(order + " clone " + hex.formatHex(orig) + " " + hex.formatHex(copy)); + // an int buffer over a byte buffer, read through a long view + ByteBuffer bb = ByteBuffer.allocate(40).order(order); + bb.asIntBuffer().put(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + System.out.println(order + " view " + Long.toHexString(bb.asLongBuffer().get(2)) + " " + bb.position(3).slice().order(order).getInt(0)); + } + // Arrays.mismatch reads longs across the elements of int, char and short + // arrays, from any starting element + int[] ia = new int[40], ib = new int[40]; + char[] ca = new char[70], cb = new char[70]; + short[] sa = new short[70], sb = new short[70]; + StringBuilder out = new StringBuilder("mismatch"); + for (int diff = 0; diff < 40; diff += 3) { + ib[diff] = ia[diff] + 1; + cb[diff + 25] = 'x'; + sb[diff + 25] = -1; + for (int from = 0; from < 5; from++) { + out.append(' ').append(java.util.Arrays.mismatch(ia, from, 40, ib, from, 40)) + .append(',').append(java.util.Arrays.mismatch(ca, from, 70, cb, from + 1, 70)) + .append(',').append(java.util.Arrays.mismatch(sa, from, 70, sb, from, 70)); + } + ib[diff] = ia[diff]; + cb[diff + 25] = 0; + sb[diff + 25] = 0; + } + System.out.println(out); + } +} diff --git a/test/java/VarHandles.java b/test/java/VarHandles.java new file mode 100644 index 0000000..ad6a9f3 --- /dev/null +++ b/test/java/VarHandles.java @@ -0,0 +1,88 @@ +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.invoke.WrongMethodTypeException; +import java.nio.ByteOrder; + +// VarHandle calls hot enough to be compiled: one call site fed different +// handles, field, static, array and byte-view handles, CAS and getAndAdd, +// exact handles that must reject a mismatched call, and failing accesses. +public class VarHandles { + int count; + long total; + static int shared; + + static final VarHandle COUNT, TOTAL, SHARED, INTS, BE, LE, LONGS; + static { + try { + var l = MethodHandles.lookup(); + COUNT = l.findVarHandle(VarHandles.class, "count", int.class); + TOTAL = l.findVarHandle(VarHandles.class, "total", long.class); + SHARED = l.findStaticVarHandle(VarHandles.class, "shared", int.class); + INTS = MethodHandles.arrayElementVarHandle(int[].class); + BE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN); + LE = MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN); + LONGS = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN); + } catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + // one call site, whichever handle it is given + static void put(VarHandle h, byte[] b, int at, int v) { + h.set(b, at, v); + } + + static int get(VarHandle h, byte[] b, int at) { + return (int) h.get(b, at); + } + + public static void main(String[] args) { + VarHandles o = new VarHandles(); + int[] ints = new int[8]; + byte[] b = new byte[16]; + long mix = 0; + for (int i = 0; i < 30000; i++) { + COUNT.set(o, (int) COUNT.get(o) + 1); + TOTAL.getAndAdd(o, (long) i); + SHARED.compareAndSet(i, i + 1); + INTS.set(ints, i & 7, i); + VarHandle h = (i & 1) == 0 ? BE : LE; + put(h, b, i % 13, i); + mix = mix * 31 + get(h, b, i % 13) + get((i & 2) == 0 ? LE : BE, b, 4); + } + System.out.println(o.count + " " + o.total + " " + shared + " " + java.util.Arrays.toString(ints) + " " + mix); + + VarHandle exact = COUNT.withInvokeExactBehavior(); + int wrong = 0, oob = 0; + for (int i = 0; i < 20000; i++) { + exact.set(o, i); + try { + Object boxed = exact.get(o); + if (boxed == null) throw new AssertionError(); + } catch (WrongMethodTypeException e) { + wrong++; + } + try { + put(BE, b, 13 + (i & 3), i); + } catch (IndexOutOfBoundsException e) { + oob++; + } + } + System.out.println(o.count + " wrong type " + wrong + " out of bounds " + oob); + + // exact handles at a shared site: an int view matches put's call, a long + // view does not, and a plain long view converts the int + VarHandle[] hs = {BE.withInvokeExactBehavior(), LONGS.withInvokeExactBehavior(), LONGS}; + wrong = 0; + mix = 0; + for (int i = 0; i < 30000; i++) { + try { + put(hs[i % 3], b, i & 7, i); + } catch (WrongMethodTypeException e) { + wrong++; + } + mix = mix * 31 + get(BE, b, 4); + } + System.out.println("exact wrong type " + wrong + " " + mix); + } +} diff --git a/test/jtreg/passing.txt b/test/jtreg/passing.txt index 89fc24d..9bd9567 100644 --- a/test/jtreg/passing.txt +++ b/test/jtreg/passing.txt @@ -1711,6 +1711,7 @@ java/security/SecureRandom/GetAlgorithm.java java/security/SecureRandom/GetInstanceTest.java java/security/SecureRandom/MultiThreadTest.java java/security/SecureRandom/NextBytesNull.java +java/security/SecureRandom/NoSync.java java/security/SecureRandom/Serialize.java java/security/SecureRandom/SerializedSeedTest.java java/security/SecureRandom/TestStrong.java