diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TBuffer.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TBuffer.java index bb82b068b7..f33b1c74b2 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TBuffer.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TBuffer.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.nio; public abstract class TBuffer { + TBuffer nextRef; int position; int limit; int mark = -1; diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBuffer.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBuffer.java index 13d758b0d5..340cf20af0 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBuffer.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBuffer.java @@ -17,9 +17,12 @@ import java.nio.BufferUnderflowException; import java.util.Objects; +import org.teavm.backend.c.runtime.Memory; import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.lang.TComparable; +import org.teavm.interop.Address; import org.teavm.jso.typedarrays.Int8Array; +import org.teavm.runtime.GC; public abstract class TByteBuffer extends TBuffer implements TComparable { TByteOrder order = TByteOrder.BIG_ENDIAN; @@ -36,6 +39,19 @@ public static TByteBuffer allocateDirect(int capacity) { result.limit = capacity; return result; } + if (PlatformDetector.isC()) { + var memory = Memory.malloc(capacity); + var result = new TByteBufferNativeImpl(null, 0, null, memory, capacity, false); + GC.registerDirectBuffer(Address.ofObject(result).toStructure()); + result.limit = capacity; + return result; + } + if (PlatformDetector.isWebAssembly()) { + var array = new byte[capacity]; + var result = new TByteBufferNativeImpl(array, 0, array, Address.ofData(array), array.length, false); + result.limit = capacity; + return result; + } return new TByteBufferImpl(capacity, true); } @@ -49,6 +65,12 @@ public static TByteBuffer allocate(int capacity) { result.limit = capacity; return result; } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly()) { + var array = new byte[capacity]; + var result = new TByteBufferNativeImpl(array, 0, array, Address.ofData(array), array.length, false); + result.limit = capacity; + return result; + } return new TByteBufferImpl(capacity, false); } @@ -61,6 +83,12 @@ public static TByteBuffer wrap(byte[] array, int offset, int length) { result.limit = offset + length; return result; } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly()) { + var result = new TByteBufferNativeImpl(array, 0, array, Address.ofData(array), array.length, false); + result.position = offset; + result.limit = offset + length; + return result; + } return new TByteBufferImpl(0, array.length, array, offset, offset + length, false, false); } @@ -254,9 +282,13 @@ public final TByteOrder order() { public final TByteBuffer order(TByteOrder bo) { order = bo; + onOrderChanged(); return this; } + void onOrderChanged() { + } + public abstract char getChar(); public abstract TByteBuffer putChar(char value); diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferNativeImpl.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferNativeImpl.java new file mode 100644 index 0000000000..3d8efd0156 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TByteBufferNativeImpl.java @@ -0,0 +1,548 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; +import org.teavm.runtime.Allocator; + +class TByteBufferNativeImpl extends TByteBuffer { + private byte[] array; + private int arrayOffset; + @TNativeBufferObjectMarker + private Object base; + private Address address; + private int capacity; + private boolean readOnly; + private boolean swap; + + TByteBufferNativeImpl(byte[] array, int arrayOffset, Object base, Address address, int capacity, boolean readOnly) { + this.array = array; + this.arrayOffset = arrayOffset; + this.base = base; + this.address = address; + this.capacity = capacity; + this.readOnly = readOnly; + updateSwap(); + } + + @Override + void onOrderChanged() { + updateSwap(); + } + + private void updateSwap() { + swap = order != TByteOrder.nativeOrder(); + } + + @Override + byte[] arrayImpl() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return array; + } + + @Override + int arrayOffsetImpl() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return arrayOffset; + } + + @Override + boolean hasArrayImpl() { + return array != null; + } + + @Override + int capacityImpl() { + return capacity; + } + + @Override + void getImpl(int index, byte[] dst, int offset, int length) { + copy(address.add(index), Address.ofData(dst).add(offset), length); + } + + @Override + void putImpl(int index, TByteBuffer src, int offset, int length) { + if (src instanceof TByteBufferNativeImpl) { + var srcImpl = (TByteBufferNativeImpl) src; + copy(srcImpl.address.add(offset), address.add(index), length); + } else if (src.hasArray()) { + copy(Address.ofData(src.array()).add(offset + src.arrayOffset() + offset), + address.add(index), length); + } else { + var addr = address.add(index); + while (length-- > 0) { + addr.putByte(src.get(offset++)); + addr = addr.add(1); + } + } + } + + @Override + void putImpl(byte[] src, int srcOffset, int destOffset, int length) { + copy(Address.ofData(src).add(srcOffset), address.add(destOffset), length); + } + + @Override + public TByteBuffer slice() { + var newData = address.add(position); + var result = new TByteBufferNativeImpl(array, arrayOffset + position, base, newData, remaining(), readOnly); + result.position = 0; + result.limit = result.capacity(); + result.order = TByteOrder.BIG_ENDIAN; + return result; + } + + @Override + public TByteBuffer duplicate() { + var result = new TByteBufferNativeImpl(array, arrayOffset + position, base, address, capacity, readOnly); + result.position = position; + result.limit = limit; + result.mark = mark; + result.order = TByteOrder.BIG_ENDIAN; + return result; + } + + @Override + public TByteBuffer asReadOnlyBuffer() { + var result = new TByteBufferNativeImpl(array, arrayOffset + position, base, address, capacity, true); + result.position = position; + result.limit = limit; + result.mark = mark; + result.order = TByteOrder.BIG_ENDIAN; + return result; + } + + @Override + public byte get() { + if (position >= limit) { + throw new TBufferUnderflowException(); + } + return address.add(position++).getByte(); + } + + @Override + public TByteBuffer put(byte b) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position >= limit) { + throw new TBufferOverflowException(); + } + address.add(position++).putByte(b); + return this; + } + + @Override + public byte get(int index) { + if (index < 0 || index >= limit) { + throw new IndexOutOfBoundsException(); + } + return address.add(index).getByte(); + } + + @Override + public TByteBuffer put(int index, byte b) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index < 0 || index >= limit) { + throw new IndexOutOfBoundsException(); + } + address.add(index).putByte(b); + return this; + } + + @Override + public TByteBuffer compact() { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + int sz = remaining(); + if (position > 0) { + copy(address.add(position), address, sz); + } + position = sz; + limit = capacity(); + mark = -1; + return this; + } + + @Override + public boolean isDirect() { + return base == null; + } + + @Override + public boolean isReadOnly() { + return readOnly; + } + + @Override + public char getChar() { + if (position + 1 >= limit) { + throw new TBufferUnderflowException(); + } + var result = address.add(position).getChar(); + position += 2; + return swap ? Character.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putChar(char value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 1 >= limit) { + throw new TBufferOverflowException(); + } + address.add(position).putChar(swap ? Character.reverseBytes(value) : value); + position += 2; + return this; + } + + @Override + public char getChar(int index) { + if (index < 0 || index + 1 >= limit) { + throw new IndexOutOfBoundsException(); + } + var result = address.add(index).getChar(); + return swap ? Character.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putChar(int index, char value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index < 0 || index + 1 >= limit) { + throw new IndexOutOfBoundsException(); + } + address.add(index).putChar(swap ? Character.reverseBytes(value) : value); + return this; + } + + @Override + public TCharBuffer asCharBuffer() { + int sz = remaining() / 2; + if (swap) { + return new TCharBufferSwapNative(0, sz, readOnly, base, address.add(position), sz); + } else { + return new TCharBufferNative(null, 0, 0, sz, readOnly, base, address.add(position), sz); + } + } + + @Override + public short getShort() { + if (position + 1 >= limit) { + throw new TBufferUnderflowException(); + } + var result = address.add(position).getShort(); + position += 2; + return swap ? Short.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putShort(short value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 1 >= limit) { + throw new TBufferOverflowException(); + } + address.add(position).putShort(swap ? Short.reverseBytes(value) : value); + position += 2; + return this; + } + + @Override + public short getShort(int index) { + if (index < 0 || index + 1 >= limit) { + throw new IndexOutOfBoundsException(); + } + var result = address.add(index).getShort(); + return swap ? Short.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putShort(int index, short value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index < 0 || index + 1 >= limit) { + throw new IndexOutOfBoundsException(); + } + address.add(index).putShort(swap ? Short.reverseBytes(value) : value); + return this; + } + + @Override + public TShortBuffer asShortBuffer() { + int sz = remaining() / 2; + if (swap) { + return new TShortBufferSwapNative(0, sz, readOnly, base, address.add(position), sz); + } else { + return new TShortBufferNative(null, 0, 0, sz, readOnly, base, address.add(position), sz); + } + } + + @Override + public int getInt() { + if (position + 3 >= limit) { + throw new TBufferUnderflowException(); + } + var result = address.add(position).getInt(); + position += 4; + return swap ? Integer.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putInt(int value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 3 >= limit) { + throw new TBufferOverflowException(); + } + address.add(position).putInt(swap ? Integer.reverseBytes(value) : value); + position += 4; + return this; + } + + @Override + public int getInt(int index) { + if (index < 0 || index + 3 >= limit) { + throw new IndexOutOfBoundsException(); + } + var result = address.add(index).getInt(); + return swap ? Integer.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putInt(int index, int value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index < 0 || index + 3 >= limit) { + throw new IndexOutOfBoundsException(); + } + address.add(index).putInt(swap ? Integer.reverseBytes(value) : value); + return this; + } + + @Override + public TIntBuffer asIntBuffer() { + int sz = remaining() / 4; + if (swap) { + return new TIntBufferSwapNative(0, sz, readOnly, base, address.add(position), sz); + } else { + return new TIntBufferNative(null, 0, 0, sz, readOnly, base, address.add(position), sz); + } + } + + @Override + public float getFloat() { + if (position + 3 >= limit) { + throw new TBufferUnderflowException(); + } + var address = this.address.add(position); + var result = swap + ? Float.intBitsToFloat(Integer.reverseBytes(address.getInt())) + : address.getFloat(); + position += 4; + return result; + } + + @Override + public TByteBuffer putFloat(float value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 3 >= limit) { + throw new TBufferOverflowException(); + } + var address = this.address.add(position); + if (swap) { + address.putInt(Integer.reverseBytes(Float.floatToIntBits(value))); + } else { + address.putFloat(value); + } + position += 4; + return this; + } + + @Override + public TByteBuffer putFloat(int index, float value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index + 3 >= limit) { + throw new TBufferOverflowException(); + } + var address = this.address.add(index); + if (swap) { + address.putInt(Integer.reverseBytes(Float.floatToIntBits(value))); + } else { + address.putFloat(value); + } + return this; + } + + @Override + public float getFloat(int index) { + if (index + 3 >= limit) { + throw new TBufferUnderflowException(); + } + var address = this.address.add(index); + return swap + ? Float.intBitsToFloat(Integer.reverseBytes(address.getInt())) + : address.getFloat(); + } + + @Override + public double getDouble() { + if (position + 7 >= limit) { + throw new TBufferUnderflowException(); + } + var address = this.address.add(position); + var result = swap + ? Double.longBitsToDouble(Long.reverseBytes(address.getLong())) + : address.getDouble(); + position += 8; + return result; + } + + @Override + public TByteBuffer putDouble(double value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 7 >= limit) { + throw new TBufferOverflowException(); + } + var address = this.address.add(position); + if (swap) { + address.putLong(Long.reverseBytes(Double.doubleToLongBits(value))); + } else { + address.putDouble(value); + } + position += 8; + return this; + } + + @Override + public double getDouble(int index) { + if (index + 7 >= limit) { + throw new TBufferUnderflowException(); + } + var address = this.address.add(index); + return swap + ? Double.longBitsToDouble(Long.reverseBytes(address.getLong())) + : address.getDouble(); + } + + @Override + public TByteBuffer putDouble(int index, double value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index + 7 >= limit) { + throw new TBufferOverflowException(); + } + var address = this.address.add(index); + if (swap) { + address.putLong(Long.reverseBytes(Double.doubleToLongBits(value))); + } else { + address.putDouble(value); + } + return this; + } + + @Override + public long getLong() { + if (position + 7 >= limit) { + throw new TBufferUnderflowException(); + } + var result = address.add(position).getLong(); + position += 8; + return swap ? Long.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putLong(long value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (position + 7 >= limit) { + throw new TBufferOverflowException(); + } + address.add(position).putLong(swap ? Long.reverseBytes(value) : value); + position += 8; + return this; + } + + @Override + public long getLong(int index) { + if (index < 0 || index + 7 >= limit) { + throw new IndexOutOfBoundsException("Index " + index + " is outside of range [0;" + (limit - 7) + ")"); + } + var result = address.add(index).getLong(); + position += 8; + return swap ? Long.reverseBytes(result) : result; + } + + @Override + public TByteBuffer putLong(int index, long value) { + if (readOnly) { + throw new TReadOnlyBufferException(); + } + if (index < 0 || index + 3 >= limit) { + throw new IndexOutOfBoundsException("Index " + index + " is outside of range [0;" + (limit - 3) + ")"); + } + address.add(index).putLong(swap ? Long.reverseBytes(value) : value); + return this; + } + + @Override + public TLongBuffer asLongBuffer() { + int sz = remaining() / 8; + throw new UnsupportedOperationException(); + } + + @Override + public TFloatBuffer asFloatBuffer() { + int sz = remaining() / 4; + throw new UnsupportedOperationException(); + } + + @Override + public TDoubleBuffer asDoubleBuffer() { + int sz = remaining() / 8; + throw new UnsupportedOperationException(); + } + + static void copy(Address from, Address to, int count) { + Allocator.moveMemoryBlock(from, to, count); + } + + static TByteOrder oppositeOrder(TByteOrder order) { + return order == TByteOrder.BIG_ENDIAN ? TByteOrder.LITTLE_ENDIAN : TByteOrder.BIG_ENDIAN; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBuffer.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBuffer.java index ea259af9ab..7abc3e59d2 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBuffer.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBuffer.java @@ -19,6 +19,7 @@ import java.util.Objects; import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.lang.TReadable; +import org.teavm.interop.Address; import org.teavm.jso.typedarrays.Uint16Array; public abstract class TCharBuffer extends TBuffer implements Comparable, Appendable, @@ -40,6 +41,10 @@ public static TCharBuffer allocate(int capacity) { var array = new char[capacity]; return new TCharBufferOverTypedArray(0, capacity, false, Uint16Array.fromJavaArray(array), array); } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly()) { + var array = new char[capacity]; + return new TCharBufferNative(array, 0, 0, capacity, false, array, Address.ofData(array), capacity); + } return new TCharBufferOverArray(capacity); } @@ -53,6 +58,13 @@ public static TCharBuffer wrap(char[] array, int offset, int length) { result.limit = offset + length; return result; } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly() ) { + var result = new TCharBufferNative(array, 0, 0, array.length, false, array, Address.ofData(array), + array.length); + result.position = offset; + result.limit = offset + length; + return result; + } return new TCharBufferOverArray(0, array.length, array, offset, offset + length, false); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNative.java new file mode 100644 index 0000000000..7917fc6665 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNative.java @@ -0,0 +1,89 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TCharBufferNative extends TCharBufferNativeBase { + TCharBufferNative(char[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(array, arrayOffset, position, limit, readOnly, base, address, capacity); + } + + @Override + TCharBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TCharBufferNative(array, arrayOffset, position, limit, readOnly, base, address.add(start * 2), + capacity); + } + + @Override + char getChar(int index) { + return address.add(index * 2).getChar(); + } + + @Override + void putChar(int index, char value) { + address.add(index * 2).putChar(value); + } + + @Override + void getImpl(int index, char[] dst, int offset, int length) { + TByteBufferNativeImpl.copy(address.add(index * 2), Address.ofData(dst).add(offset * 2), length * 2); + } + + @Override + void putImpl(int index, char[] src, int offset, int length) { + TByteBufferNativeImpl.copy(Address.ofData(src).add(offset * 2), address.add(index * 2), length * 2); + } + + @Override + void putImpl(int index, TCharBuffer src, int offset, int length) { + if (src instanceof TCharBufferNative) { + var srcImpl = (TCharBufferNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 2), address.add(index * 2), length * 2); + } else if (src instanceof TCharBufferSwapNative) { + var srcImpl = (TCharBufferSwapNative) src; + var addr = address.add(index * 2); + var srcAddr = srcImpl.address.add(offset * 2); + while (length-- > 0) { + addr.putChar(Character.reverseBytes(srcAddr.getChar())); + addr = addr.add(2); + srcAddr = srcAddr.add(2); + } + } else { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putChar(src.get(offset++)); + addr = addr.add(2); + } + } + } + + @Override + void putImpl(int index, String src, int offset, int length) { + index *= 2; + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putChar(src.charAt(offset++)); + addr = addr.add(2); + } + } + + @Override + public TByteOrder order() { + return TByteOrder.nativeOrder(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNativeBase.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNativeBase.java new file mode 100644 index 0000000000..e21900efec --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferNativeBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +abstract class TCharBufferNativeBase extends TCharBufferImpl { + char[] array; + int arrayOffset; + private boolean readOnly; + @TNativeBufferObjectMarker + protected final Object base; + Address address; + private int capacity; + + TCharBufferNativeBase(char[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(position, limit); + this.array = array; + this.arrayOffset = arrayOffset; + this.readOnly = readOnly; + this.base = base; + this.address = address; + this.capacity = capacity; + } + + @Override + int capacityImpl() { + return capacity; + } + + @Override + boolean isArrayPresent() { + return array != null; + } + + @Override + char[] getArray() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return array; + } + + @Override + int getArrayOffset() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return arrayOffset; + } + + @Override + boolean readOnly() { + return readOnly; + } + + @Override + public boolean isDirect() { + return base == null; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferSwapNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferSwapNative.java new file mode 100644 index 0000000000..cc76547c99 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TCharBufferSwapNative.java @@ -0,0 +1,95 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TCharBufferSwapNative extends TCharBufferNativeBase { + TCharBufferSwapNative(int position, int limit, boolean readOnly, Object base, Address address, int capacity) { + super(null, 0, position, limit, readOnly, base, address, capacity); + } + + @Override + TCharBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TCharBufferSwapNative(position, limit, readOnly, base, address.add(start * 2), capacity); + } + + @Override + char getChar(int index) { + return Character.reverseBytes(address.add(index * 2).getChar()); + } + + @Override + void putChar(int index, char value) { + address.add(index * 2).putChar(Character.reverseBytes(value)); + } + + @Override + void getImpl(int index, char[] dst, int offset, int length) { + var addr = address.add(index * 2); + while (length-- > 0) { + dst[offset++] = Character.reverseBytes(addr.getChar()); + addr = addr.add(2); + } + } + + @Override + void putImpl(int index, char[] src, int offset, int length) { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putChar(Character.reverseBytes(src[offset++])); + addr = addr.add(2); + } + } + + @Override + void putImpl(int index, TCharBuffer src, int offset, int length) { + if (src instanceof TCharBufferSwapNative) { + var srcImpl = (TCharBufferSwapNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 2), address.add(index * 2), length * 2); + } else if (src instanceof TCharBufferNative) { + var srcImpl = (TCharBufferNative) src; + var addr = address.add(index * 2); + var srcAddr = srcImpl.address.add(offset * 2); + while (length-- > 0) { + addr.putChar(Character.reverseBytes(srcAddr.getChar())); + addr = addr.add(2); + srcAddr = srcAddr.add(2); + } + } else { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putChar(Character.reverseBytes(src.get(offset++))); + addr = addr.add(2); + } + } + } + + @Override + void putImpl(int index, String src, int offset, int length) { + index *= 2; + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putChar(Character.reverseBytes(src.charAt(offset++))); + addr = addr.add(2); + } + } + + @Override + public TByteOrder order() { + return TByteBufferNativeImpl.oppositeOrder(TByteOrder.nativeOrder()); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBuffer.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBuffer.java index ce86bf261a..f80cfe644f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBuffer.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBuffer.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.nio; import org.teavm.classlib.PlatformDetector; +import org.teavm.interop.Address; import org.teavm.jso.typedarrays.Int32Array; public abstract class TIntBuffer extends TBuffer implements Comparable { @@ -32,6 +33,10 @@ public static TIntBuffer allocate(int capacity) { var array = new int[capacity]; return new TIntBufferOverTypedArray(0, capacity, false, Int32Array.fromJavaArray(array), array); } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly()) { + var array = new int[capacity]; + return new TIntBufferNative(array, 0, 0, capacity, false, array, Address.ofData(array), capacity); + } return new TIntBufferOverArray(capacity); } @@ -42,6 +47,13 @@ public static TIntBuffer wrap(int[] array, int offset, int length) { result.limit = offset + length; return result; } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly() ) { + var result = new TIntBufferNative(array, 0, 0, array.length, false, array, Address.ofData(array), + array.length); + result.position = offset; + result.limit = offset + length; + return result; + } return new TIntBufferOverArray(0, array.length, array, offset, offset + length, false); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNative.java new file mode 100644 index 0000000000..64980b5d37 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNative.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TIntBufferNative extends TIntBufferNativeBase { + TIntBufferNative(int[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(array, arrayOffset, position, limit, readOnly, base, address, capacity); + } + + @Override + TIntBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TIntBufferNative(array, arrayOffset, position, limit, readOnly, base, address.add(start * 4), + capacity); + } + + @Override + int getElement(int index) { + return address.add(index * 4).getInt(); + } + + @Override + void putElement(int index, int value) { + address.add(index * 4).putInt(value); + } + + @Override + void getImpl(int index, int[] dst, int offset, int length) { + TByteBufferNativeImpl.copy(address.add(index * 4), Address.ofData(dst).add(offset * 4), length * 4); + } + + @Override + void putImpl(int index, int[] src, int offset, int length) { + TByteBufferNativeImpl.copy(Address.ofData(src).add(offset * 4), address.add(index * 4), length * 4); + } + + @Override + void putImpl(int index, TIntBuffer src, int offset, int length) { + if (src instanceof TIntBufferNative) { + var srcImpl = (TIntBufferNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 4), address.add(index * 4), length * 4); + } else if (src instanceof TIntBufferSwapNative) { + var srcImpl = (TIntBufferSwapNative) src; + var addr = address.add(index * 4); + var srcAddr = srcImpl.address.add(offset * 4); + while (length-- > 0) { + addr.putInt(Integer.reverseBytes(srcAddr.getInt())); + addr = addr.add(4); + srcAddr = srcAddr.add(4); + } + } else { + var addr = address.add(index * 4); + while (length-- > 0) { + addr.putInt(src.get(offset++)); + addr = addr.add(4); + } + } + } + + @Override + public TByteOrder order() { + return TByteOrder.nativeOrder(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNativeBase.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNativeBase.java new file mode 100644 index 0000000000..9ebac0ace2 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferNativeBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +abstract class TIntBufferNativeBase extends TIntBufferImpl { + int[] array; + int arrayOffset; + private boolean readOnly; + @TNativeBufferObjectMarker + protected final Object base; + Address address; + private int capacity; + + TIntBufferNativeBase(int[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(position, limit); + this.array = array; + this.arrayOffset = arrayOffset; + this.readOnly = readOnly; + this.base = base; + this.address = address; + this.capacity = capacity; + } + + @Override + int capacityImpl() { + return capacity; + } + + @Override + boolean isArrayPresent() { + return array != null; + } + + @Override + int[] getArray() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return array; + } + + @Override + int getArrayOffset() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return arrayOffset; + } + + @Override + boolean readOnly() { + return readOnly; + } + + @Override + public boolean isDirect() { + return base == null; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferSwapNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferSwapNative.java new file mode 100644 index 0000000000..bab1af7312 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TIntBufferSwapNative.java @@ -0,0 +1,85 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TIntBufferSwapNative extends TIntBufferNativeBase { + TIntBufferSwapNative(int position, int limit, boolean readOnly, Object base, Address address, int capacity) { + super(null, 0, position, limit, readOnly, base, address, capacity); + } + + @Override + TIntBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TIntBufferSwapNative(position, limit, readOnly, base, address.add(start * 4), capacity); + } + + @Override + int getElement(int index) { + return Integer.reverseBytes(address.add(index * 4).getInt()); + } + + @Override + void putElement(int index, int value) { + address.add(index * 4).putInt(Integer.reverseBytes(value)); + } + + @Override + void getImpl(int index, int[] dst, int offset, int length) { + var addr = address.add(index * 4); + while (length-- > 0) { + dst[offset++] = Integer.reverseBytes(addr.getInt()); + addr = addr.add(4); + } + } + + @Override + void putImpl(int index, int[] src, int offset, int length) { + var addr = address.add(index * 4); + while (length-- > 0) { + addr.putInt(Integer.reverseBytes(src[offset++])); + addr = addr.add(4); + } + } + + @Override + void putImpl(int index, TIntBuffer src, int offset, int length) { + if (src instanceof TIntBufferSwapNative) { + var srcImpl = (TIntBufferSwapNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 4), address.add(index * 4), length * 4); + } else if (src instanceof TIntBufferNative) { + var srcImpl = (TIntBufferNative) src; + var addr = address.add(index * 4); + var srcAddr = srcImpl.address.add(offset * 4); + while (length-- > 0) { + addr.putInt(Integer.reverseBytes(srcAddr.getInt())); + addr = addr.add(4); + srcAddr = srcAddr.add(4); + } + } else { + var addr = address.add(index * 4); + while (length-- > 0) { + addr.putInt(Integer.reverseBytes(src.get(offset++))); + addr = addr.add(4); + } + } + } + + @Override + public TByteOrder order() { + return TByteBufferNativeImpl.oppositeOrder(TByteOrder.nativeOrder()); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TNativeBufferObjectMarker.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TNativeBufferObjectMarker.java new file mode 100644 index 0000000000..e2b5f2850a --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TNativeBufferObjectMarker.java @@ -0,0 +1,26 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.FIELD) +@interface TNativeBufferObjectMarker { +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBuffer.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBuffer.java index cda5329998..3fcd33e481 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBuffer.java +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBuffer.java @@ -16,6 +16,7 @@ package org.teavm.classlib.java.nio; import org.teavm.classlib.PlatformDetector; +import org.teavm.interop.Address; import org.teavm.jso.typedarrays.Int16Array; public abstract class TShortBuffer extends TBuffer implements Comparable { @@ -32,6 +33,10 @@ public static TShortBuffer allocate(int capacity) { var array = new short[capacity]; return new TShortBufferOverTypedArray(0, capacity, false, Int16Array.fromJavaArray(array), array); } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly()) { + var array = new short[capacity]; + return new TShortBufferNative(array, 0, 0, capacity, false, array, Address.ofData(array), capacity); + } return new TShortBufferOverArray(capacity); } @@ -42,6 +47,13 @@ public static TShortBuffer wrap(short[] array, int offset, int length) { result.limit = offset + length; return result; } + if (PlatformDetector.isC() || PlatformDetector.isWebAssembly() ) { + var result = new TShortBufferNative(array, 0, 0, array.length, false, array, Address.ofData(array), + array.length); + result.position = offset; + result.limit = offset + length; + return result; + } return new TShortBufferOverArray(0, array.length, array, offset, offset + length, false); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNative.java new file mode 100644 index 0000000000..f7328b5b82 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNative.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TShortBufferNative extends TShortBufferNativeBase { + TShortBufferNative(short[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(array, arrayOffset, position, limit, readOnly, base, address, capacity); + } + + @Override + TShortBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TShortBufferNative(array, arrayOffset, position, limit, readOnly, base, address.add(start * 2), + capacity); + } + + @Override + short getElement(int index) { + return address.add(index * 2).getShort(); + } + + @Override + void putElement(int index, short value) { + address.add(index * 2).putShort(value); + } + + @Override + void getImpl(int index, short[] dst, int offset, int length) { + TByteBufferNativeImpl.copy(address.add(index * 2), Address.ofData(dst).add(offset * 2), length * 2); + } + + @Override + void putImpl(int index, short[] src, int offset, int length) { + TByteBufferNativeImpl.copy(Address.ofData(src).add(offset * 2), address.add(index * 2), length * 2); + } + + @Override + void putImpl(int index, TShortBuffer src, int offset, int length) { + if (src instanceof TShortBufferNative) { + var srcImpl = (TShortBufferNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 2), address.add(index * 2), length * 2); + } else if (src instanceof TShortBufferSwapNative) { + var srcImpl = (TShortBufferSwapNative) src; + var addr = address.add(index * 2); + var srcAddr = srcImpl.address.add(offset * 2); + while (length-- > 0) { + addr.putShort(Short.reverseBytes(srcAddr.getShort())); + addr = addr.add(2); + srcAddr = srcAddr.add(2); + } + } else { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putShort(src.get(offset++)); + addr = addr.add(2); + } + } + } + + @Override + public TByteOrder order() { + return TByteOrder.nativeOrder(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNativeBase.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNativeBase.java new file mode 100644 index 0000000000..bce3e03a5f --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferNativeBase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +abstract class TShortBufferNativeBase extends TShortBufferImpl { + short[] array; + int arrayOffset; + private boolean readOnly; + @TNativeBufferObjectMarker + protected final Object base; + Address address; + private int capacity; + + TShortBufferNativeBase(short[] array, int arrayOffset, int position, int limit, boolean readOnly, + Object base, Address address, int capacity) { + super(position, limit); + this.array = array; + this.arrayOffset = arrayOffset; + this.readOnly = readOnly; + this.base = base; + this.address = address; + this.capacity = capacity; + } + + @Override + int capacityImpl() { + return capacity; + } + + @Override + boolean isArrayPresent() { + return array != null; + } + + @Override + short[] getArray() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return array; + } + + @Override + int getArrayOffset() { + if (array == null) { + throw new UnsupportedOperationException(); + } + return arrayOffset; + } + + @Override + boolean readOnly() { + return readOnly; + } + + @Override + public boolean isDirect() { + return base == null; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferSwapNative.java b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferSwapNative.java new file mode 100644 index 0000000000..51e9d47d23 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/java/nio/TShortBufferSwapNative.java @@ -0,0 +1,85 @@ +/* + * Copyright 2025 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.java.nio; + +import org.teavm.interop.Address; + +class TShortBufferSwapNative extends TShortBufferNativeBase { + TShortBufferSwapNative(int position, int limit, boolean readOnly, Object base, Address address, int capacity) { + super(null, 0, position, limit, readOnly, base, address, capacity); + } + + @Override + TShortBuffer duplicate(int start, int capacity, int position, int limit, boolean readOnly) { + return new TShortBufferSwapNative(position, limit, readOnly, base, address.add(start * 2), capacity); + } + + @Override + short getElement(int index) { + return Short.reverseBytes(address.add(index * 2).getShort()); + } + + @Override + void putElement(int index, short value) { + address.add(index * 2).putShort(Short.reverseBytes(value)); + } + + @Override + void getImpl(int index, short[] dst, int offset, int length) { + var addr = address.add(index * 2); + while (length-- > 0) { + dst[offset++] = Short.reverseBytes(addr.getShort()); + addr = addr.add(2); + } + } + + @Override + void putImpl(int index, short[] src, int offset, int length) { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putShort(Short.reverseBytes(src[offset++])); + addr = addr.add(2); + } + } + + @Override + void putImpl(int index, TShortBuffer src, int offset, int length) { + if (src instanceof TShortBufferSwapNative) { + var srcImpl = (TShortBufferSwapNative) src; + TByteBufferNativeImpl.copy(srcImpl.address.add(offset * 2), address.add(index * 2), length * 2); + } else if (src instanceof TShortBufferNative) { + var srcImpl = (TShortBufferNative) src; + var addr = address.add(index * 2); + var srcAddr = srcImpl.address.add(offset * 2); + while (length-- > 0) { + addr.putShort(Short.reverseBytes(srcAddr.getShort())); + addr = addr.add(2); + srcAddr = srcAddr.add(2); + } + } else { + var addr = address.add(index * 2); + while (length-- > 0) { + addr.putShort(Short.reverseBytes(src.get(offset++))); + addr = addr.add(2); + } + } + } + + @Override + public TByteOrder order() { + return TByteBufferNativeImpl.oppositeOrder(TByteOrder.nativeOrder()); + } +} diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 3c897b28ae..eda40184b8 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -555,7 +555,10 @@ private void generateVirtualTable(ValueType type) { if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { enumConstants = writeEnumConstants(cls, name); } else { - enumConstants = "NULL"; + enumConstants = getBufferFieldInitializer(cls); + if (enumConstants == null) { + enumConstants = "NULL"; + } } headerWriter.print("extern ").print(structName).print(" ").print(name).println(";"); @@ -601,6 +604,20 @@ private void generateVirtualTable(ValueType type) { codeWriter.outdent().println(";"); } + private String getBufferFieldInitializer(ClassReader cls) { + if (cls == null) { + return null; + } + for (var field : cls.getFields()) { + if (ClassGeneratorUtil.isBufferObjectField(field)) { + String structName = context.getNames().forClass(cls.getName()); + String fieldName = context.getNames().forMemberField(field.getReference()); + return "(void*) offsetof(" + structName + ", " + fieldName + ")"; + } + } + return null; + } + private int getInheritanceDepth(String className) { int depth = 0; while (true) { @@ -796,14 +813,7 @@ private void generateRuntimeClassInitializer(ValueType type, String enumConstant superinterfaces = sb.append(" }").toString(); } - switch (className) { - case "java.lang.ref.WeakReference": - flags |= RuntimeClass.VM_TYPE_WEAKREFERENCE << RuntimeClass.VM_TYPE_SHIFT; - break; - case "java.lang.ref.ReferenceQueue": - flags |= RuntimeClass.VM_TYPE_REFERENCEQUEUE << RuntimeClass.VM_TYPE_SHIFT; - break; - } + flags = ClassGeneratorUtil.contributeToFlags(cls, flags); if (cls != null) { simpleName = cls.getSimpleName(); diff --git a/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java b/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java index c69e08d472..eb3b975be2 100644 --- a/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java +++ b/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java @@ -15,6 +15,8 @@ */ package org.teavm.backend.lowlevel.generate; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReader; import org.teavm.model.ValueType; import org.teavm.runtime.RuntimeClass; @@ -53,4 +55,22 @@ private static int getPrimitiveFlag(ValueType.Primitive type) { throw new AssertionError(); } } + + public static int contributeToFlags(ClassReader cls, int flags) { + switch (cls.getName()) { + case "java.lang.ref.WeakReference": + flags |= RuntimeClass.VM_TYPE_WEAKREFERENCE << RuntimeClass.VM_TYPE_SHIFT; + break; + case "java.lang.ref.ReferenceQueue": + flags |= RuntimeClass.VM_TYPE_REFERENCEQUEUE << RuntimeClass.VM_TYPE_SHIFT; + break; + } + + return flags; + } + + + public static boolean isBufferObjectField(FieldReader cls) { + return cls.getAnnotations().get("java.nio.NativeBufferObjectMarker") != null; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 62d218840c..aa3557dd36 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import org.teavm.backend.lowlevel.generate.ClassGeneratorUtil; import org.teavm.backend.wasm.WasmFunctionRepository; import org.teavm.backend.wasm.binary.BinaryWriter; import org.teavm.backend.wasm.binary.DataArray; @@ -271,7 +272,7 @@ public int getFunctionPointer(WasmFunction function) { private DataValue createStructure(ClassBinaryData binaryData) { String parent = binaryData.cls.getParent(); - int parentPtr = !binaryData.isInferface && parent != null + int parentPtr = !binaryData.isInterface && parent != null ? getClassPointer(ValueType.object(binaryData.cls.getParent())) : 0; @@ -351,6 +352,11 @@ private DataValue createStructure(ClassBinaryData binaryData) { } } + flags = ClassGeneratorUtil.contributeToFlags(cls, flags); + if (binaryData.bufferObjectFieldOffset >= 0) { + header.setAddress(CLASS_ENUM_VALUES, binaryData.bufferObjectFieldOffset); + } + if (cls != null && binaryData.start >= 0 && cls.getMethod(new MethodDescriptor("", ValueType.VOID)) != null && classInitializerInfo.isDynamicInitializer(name)) { @@ -512,7 +518,7 @@ private void calculateLayout(ClassReader cls, ClassBinaryData data, DwarfClassGe data.alignment = 4; } - data.isInferface = cls.hasModifier(ElementModifier.INTERFACE); + data.isInterface = cls.hasModifier(ElementModifier.INTERFACE); data.cls = cls; for (FieldReader field : cls.getFields()) { @@ -537,6 +543,9 @@ private void calculateLayout(ClassReader cls, ClassBinaryData data, DwarfClassGe dwarfClass.registerField(field.getName(), field.getType(), offset); dwarfClassGenerator.getTypePtr(field.getType()); } + if (ClassGeneratorUtil.isBufferObjectField(field)) { + data.bufferObjectFieldOffset = offset; + } } if (data.alignment == 0) { data.alignment = desiredAlignment; @@ -718,7 +727,7 @@ public void writeDebug(DebugClassLayout debug) { var parent = data.cls != null && data.cls.getParent() != null ? indexes.get(ValueType.object(data.cls.getParent())) : -1; - if (data.isInferface) { + if (data.isInterface) { debug.writeInterface(className, data.start); } else { debug.startClass(className, parent, data.start, data.size); @@ -803,10 +812,11 @@ static class ClassBinaryData { int size; int alignment; int start; - boolean isInferface; + boolean isInterface; ObjectIntMap fieldLayout = new ObjectIntHashMap<>(); DataValue data; ClassReader cls; boolean function; + int bufferObjectFieldOffset = -1; } } diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index 5cf7027f7c..12ca0b2454 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -15,6 +15,8 @@ */ package org.teavm.runtime; +import org.teavm.backend.c.runtime.Memory; +import org.teavm.classlib.PlatformDetector; import org.teavm.interop.Address; import org.teavm.interop.Export; import org.teavm.interop.Import; @@ -46,6 +48,7 @@ private GC() { static RelocationBlock lastRelocationBlock; static boolean isFullGC = true; private static int youngGCCount; + private static RuntimeBuffer firstDirectBuffer; static native Address gcStorageAddress(); @@ -89,6 +92,7 @@ public static int getFreeMemory() { currentChunkPointer.value = currentChunk; freeChunks = 1; totalChunks = 1; + firstDirectBuffer = null; int regionCount = getRegionCount(); Allocator.fill(cardTable(), CARD_VALID, regionCount); @@ -98,6 +102,11 @@ private static int getRegionCount() { return (int) (availableBytes() / regionSize()) + 1; } + public static void registerDirectBuffer(RuntimeBuffer buffer) { + buffer.nextRef = firstDirectBuffer; + firstDirectBuffer = buffer; + } + public static RuntimeObject alloc(int size) { FreeChunk current = currentChunk; Address next = current.toAddress().add(size); @@ -210,6 +219,7 @@ private static void doCollectGarbage() { } mark(); processReferences(); + processDirectBuffers(); sweep(); defragment(); updateFreeMemory(); @@ -549,6 +559,45 @@ private static void makeInvalid(RuntimeObject object) { cardTableItem.putByte((byte) (cardTableItem.getByte() & ~CARD_VALID)); } + private static void processDirectBuffers() { + var buffer = firstDirectBuffer; + RuntimeBuffer prevBuffer = null; + while (buffer != null) { + var next = buffer.nextRef; + if ((buffer.classReference & RuntimeObject.GC_MARKED) == 0) { + freeBufferContent(buffer); + } else { + if (prevBuffer == null) { + firstDirectBuffer = buffer; + } else { + prevBuffer.nextRef = buffer; + } + prevBuffer = buffer; + } + buffer = next; + } + + if (prevBuffer == null) { + firstDirectBuffer = null; + } else { + prevBuffer.nextRef = null; + } + } + + private static void freeBufferContent(RuntimeBuffer buffer) { + var cls = RuntimeClass.getClass(buffer); + while (cls != null) { + if (cls.enumValues != null) { + var fieldOffset = cls.enumValues.toInt(); + var fieldAddress = buffer.toAddress().add(fieldOffset).add(Address.sizeOf()); + var dataAddress = fieldAddress.getAddress(); + free(dataAddress); + return; + } + cls = cls.parent; + } + } + private static void sweep() { MemoryTrace.sweepStarted(); @@ -1086,13 +1135,24 @@ private static void updatePointersInReferenceQueue(RuntimeReferenceQueue object) private static void updatePointersInFields(RuntimeClass cls, RuntimeObject object) { Address layout = cls.layout; + int bufferFieldOffset = -1; + if (cls.enumValues != null && (cls.flags & RuntimeClass.ENUM) == 0) { + bufferFieldOffset = cls.enumValues.toInt(); + } if (layout != null) { short fieldCount = layout.getShort(); while (fieldCount-- > 0) { layout = layout.add(2); int fieldOffset = layout.getShort(); Address referenceHolder = object.toAddress().add(fieldOffset); - referenceHolder.putAddress(updatePointer(referenceHolder.getAddress())); + var oldPointer = referenceHolder.getAddress(); + var newPointer = updatePointer(oldPointer); + referenceHolder.putAddress(newPointer); + if (fieldOffset == bufferFieldOffset && oldPointer != null) { + var diff = newPointer.diff(oldPointer); + referenceHolder.add(Address.sizeOf()); + referenceHolder.putAddress(referenceHolder.getAddress().add(diff)); + } } } } @@ -1482,4 +1542,10 @@ private static boolean isMarked(RuntimeObject object) { static class Region extends Structure { short start; } + + private static void free(Address address) { + if (PlatformDetector.isC()) { + Memory.free(address); + } + } } diff --git a/core/src/main/java/org/teavm/backend/javascript/runtime/ref/RuntimeWeakRef.java b/core/src/main/java/org/teavm/runtime/RuntimeBuffer.java similarity index 80% rename from core/src/main/java/org/teavm/backend/javascript/runtime/ref/RuntimeWeakRef.java rename to core/src/main/java/org/teavm/runtime/RuntimeBuffer.java index 86bf718cc9..ce49662e5e 100644 --- a/core/src/main/java/org/teavm/backend/javascript/runtime/ref/RuntimeWeakRef.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 konsoletyper. + * Copyright 2025 Alexey Andreev. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.backend.javascript.runtime.ref; - -public final class RuntimeWeakRef { +package org.teavm.runtime; +public class RuntimeBuffer extends RuntimeObject { + RuntimeBuffer nextRef; } diff --git a/interop/core/src/main/java/org/teavm/interop/Address.java b/interop/core/src/main/java/org/teavm/interop/Address.java index 01faa8f50c..c771a7bd86 100644 --- a/interop/core/src/main/java/org/teavm/interop/Address.java +++ b/interop/core/src/main/java/org/teavm/interop/Address.java @@ -15,6 +15,8 @@ */ package org.teavm.interop; +import java.nio.Buffer; + @StaticInit @Unmanaged public final class Address { diff --git a/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java b/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java index 842c66b8f8..cf593f171b 100644 --- a/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/nio/ByteBufferTest.java @@ -30,7 +30,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.classlib.java.lang.DoubleTest; +import org.teavm.junit.SkipPlatform; import org.teavm.junit.TeaVMTestRunner; +import org.teavm.junit.TestPlatform; @RunWith(TeaVMTestRunner.class) public class ByteBufferTest { @@ -631,8 +633,16 @@ public void putsDouble() { buffer.putDouble(1, 2.0); assertArrayEquals(new byte[] { 63, 64, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0 }, array); + } + + @Test + @SkipPlatform(TestPlatform.C) + public void putsDoubleNaN() { + var array = new byte[8]; + var buffer = ByteBuffer.wrap(array); + buffer.putDouble(0, DoubleTest.OTHER_NAN); - assertArrayEquals(new byte[] { 127, -8, 0, 0, 0, 0, 0, 1, 0, 55, 0, 0, 0, 0, 0, 0 }, array); + assertArrayEquals(new byte[] { 127, -8, 0, 0, 0, 0, 0, 1 }, array); } @Test