package com.oracle.truffle.llvm.runtime.nodes.intrinsics.interop;
import java.nio.ByteBuffer;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.llvm.runtime.except.LLVMPolyglotException;
import com.oracle.truffle.llvm.runtime.interop.LLVMAsForeignNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMExpressionNode;
import com.oracle.truffle.llvm.runtime.nodes.api.LLVMNode;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.interop.LLVMPolyglotAsStringNodeGen.EncodeStringNodeGen;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.interop.LLVMPolyglotAsStringNodeGen.WriteStringNodeGen;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.interop.LLVMReadCharsetNode.LLVMCharset;
import com.oracle.truffle.llvm.runtime.nodes.intrinsics.llvm.LLVMIntrinsic;
import com.oracle.truffle.llvm.runtime.nodes.memory.store.LLVMI8StoreNode.LLVMI8OffsetStoreNode;
import com.oracle.truffle.llvm.runtime.pointer.LLVMManagedPointer;
import com.oracle.truffle.llvm.runtime.pointer.LLVMPointer;
@NodeChild(value = "object", type = LLVMExpressionNode.class)
@NodeChild(value = "buffer", type = LLVMExpressionNode.class)
@NodeChild(value = "buflen", type = LLVMExpressionNode.class)
@NodeChild(value = "charset", type = LLVMReadCharsetNode.class)
public abstract class LLVMPolyglotAsString extends LLVMIntrinsic {
public static LLVMPolyglotAsString create(LLVMExpressionNode object, LLVMExpressionNode buffer, LLVMExpressionNode buflen, LLVMExpressionNode charset) {
return LLVMPolyglotAsStringNodeGen.create(object, buffer, buflen, LLVMReadCharsetNodeGen.create(charset));
}
@Child EncodeStringNode encodeString = EncodeStringNodeGen.create();
@Child WriteStringNode writeString = WriteStringNodeGen.create();
@Specialization
long doAsString(VirtualFrame frame, Object object, Object buffer, long buflen, LLVMCharset charset) {
ByteBuffer result = encodeString.execute(object, charset);
return writeString.execute(frame, result, buffer, buflen, charset.zeroTerminatorLen);
}
abstract static class EncodeStringNode extends LLVMNode {
protected abstract ByteBuffer execute(Object str, LLVMCharset charset);
@Specialization
ByteBuffer doString(String str, LLVMCharset charset) {
return charset.encode(str);
}
@Specialization
ByteBuffer doForeign(LLVMManagedPointer obj, LLVMCharset charset,
@Cached LLVMAsForeignNode asForeign,
@Cached BoxedEncodeStringNode encode) {
return encode.execute(asForeign.execute(obj), charset);
}
}
abstract static class BoxedEncodeStringNode extends LLVMNode {
abstract ByteBuffer execute(Object object, LLVMCharset charset);
@Specialization(limit = "3")
ByteBuffer doBoxed(Object object, LLVMCharset charset,
@CachedLibrary("object") InteropLibrary interop,
@Cached BranchProfile exception) {
try {
String unboxed = interop.asString(object);
return charset.encode(unboxed);
} catch (UnsupportedMessageException ex) {
exception.enter();
throw new LLVMPolyglotException(this, "Polyglot value is not a string.");
}
}
}
abstract static class WriteStringNode extends LLVMNode {
protected abstract long execute(VirtualFrame frame, ByteBuffer source, Object target, long targetLen, int zeroTerminatorLen);
@Specialization(guards = "srcBuffer.getClass() == srcBufferClass")
long doWrite(ByteBuffer srcBuffer, LLVMPointer target, long targetLen, int zeroTerminatorLen,
@Cached("srcBuffer.getClass()") Class<? extends ByteBuffer> srcBufferClass,
@Cached LLVMI8OffsetStoreNode write) {
ByteBuffer source = CompilerDirectives.castExact(srcBuffer, srcBufferClass);
long bytesWritten = 0;
while (source.hasRemaining() && bytesWritten < targetLen) {
write.executeWithTarget(target, bytesWritten, source.get());
bytesWritten++;
}
long ret = bytesWritten;
for (int i = 0; i < zeroTerminatorLen && bytesWritten < targetLen; i++) {
write.executeWithTarget(target, bytesWritten, (byte) 0);
bytesWritten++;
}
return ret;
}
}
}