diff --git a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/nodes/primitives/PrimitiveNodeFactory.java b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/nodes/primitives/PrimitiveNodeFactory.java index 1b203314d..80b8b5dcc 100644 --- a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/nodes/primitives/PrimitiveNodeFactory.java +++ b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/nodes/primitives/PrimitiveNodeFactory.java @@ -6,10 +6,13 @@ */ package de.hpi.swa.trufflesqueak.nodes.primitives; -import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.source.Source; import de.hpi.swa.trufflesqueak.exceptions.PrimitiveFailed; import de.hpi.swa.trufflesqueak.image.SqueakImageContext; @@ -56,15 +59,13 @@ import de.hpi.swa.trufflesqueak.nodes.primitives.impl.StoragePrimitives; import de.hpi.swa.trufflesqueak.util.FrameAccess; import de.hpi.swa.trufflesqueak.util.OS; +import de.hpi.swa.trufflesqueak.util.UnsafeUtils; import org.graalvm.collections.EconomicMap; -import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; public final class PrimitiveNodeFactory { public static final int PRIMITIVE_SIMULATION_GUARD_INDEX = 19; @@ -220,6 +221,57 @@ public static AbstractPrimitiveNode getOrCreateNamed(final CompiledCodeObject me } } + @ExportLibrary(InteropLibrary.class) + static class TruffleExecutable implements TruffleObject { + ITruffleExecutable executable; + + public TruffleExecutable(ITruffleExecutable executable) { + this.executable = executable; + } + + static TruffleExecutable wrapFunction(TruffleFunction function) { + return new TruffleExecutable(function); + } + + static TruffleExecutable wrapSupplier(TruffleSupplier supplier) { + return new TruffleExecutable(supplier); + } + + @ExportMessage + boolean isExecutable() { + return true; + } + + @ExportMessage + Object execute(Object... arguments) { + return executable.execute(arguments); + } + } + + interface ITruffleExecutable { + Object execute(Object... arguments); + } + + @FunctionalInterface + interface TruffleFunction extends ITruffleExecutable { + R run(T argument); + + default Object execute(Object... arguments) { + assert arguments.length == 1; + return run((T) arguments[0]); + } + } + + @FunctionalInterface + interface TruffleSupplier extends ITruffleExecutable { + R run(); + + default Object execute(Object... arguments) { + assert arguments.length == 0; + return run(); + } + } + static class NonExistentPrimitiveNode extends AbstractPrimitiveNode { final String moduleName; final String functionName; @@ -232,31 +284,37 @@ public NonExistentPrimitiveNode(String moduleName, String functionName) { @Override public Object execute(VirtualFrame frame) { final Object interpreterProxy = loadLibrary("libInterpreterProxy", - "{ createInterpreterProxy((SINT64):SINT64,(SINT64):[UINT8],(SINT64):SINT64,():SINT64,():SINT64,():SINT64,():SINT64,(SINT64):SINT64):POINTER; }"); + "{ createInterpreterProxy((SINT64):SINT64,(SINT64):POINTER,(SINT64):SINT64,():SINT64,():SINT64,():SINT64,():SINT64,(SINT64):SINT64):POINTER; }"); final Object uuidPlugin = loadLibrary("UUIDPlugin", "{ " + - "initialiseModule():VOID; " + - "setInterpreter(POINTER):VOID; " + - "shutdownModule():VOID; " + - functionName + "():STRING; " + + "initialiseModule():SINT64; " + + "setInterpreter(POINTER):SINT64; " + + "shutdownModule():SINT64; " + + functionName + "():SINT64; " + " }"); final InteropLibrary interpreterProxyLibrary = getInteropLibrary(interpreterProxy); final InteropLibrary uuidPluginLibrary = getInteropLibrary(uuidPlugin); + ArrayList postPrimitiveCleanups = new ArrayList<>(); try { ArrayList objectRegistry = new ArrayList<>(); - Function byteSizeOf = (index) -> 16; - Function firstIndexableField = (index) -> ((NativeObject)objectRegistry.get(index)).getByteStorage(); - Function isBytes = (integer) -> true; - Supplier majorVersion = () -> 1; - Supplier methodArgumentCount = () -> 0; - Supplier minorVersion = () -> 17; - Supplier primitiveFail = () -> { assert false; return -1; }; - Function stackValue = (stackIndex) -> { - Object objectOnStack = FrameAccess.getStackValue(frame, stackIndex, FrameAccess.getNumArguments(frame)); + TruffleExecutable byteSizeOf = TruffleExecutable.wrapSupplier(() -> 16L); + TruffleExecutable firstIndexableField = TruffleExecutable.wrapFunction((index) -> { + byte[] storage = ((NativeObject) objectRegistry.get((int)(long)index)).getByteStorage(); + ByteStorage byteStorage = new ByteStorage(storage); + postPrimitiveCleanups.add(byteStorage); + return byteStorage; + }); + TruffleExecutable isBytes = TruffleExecutable.wrapFunction((integer) -> 1L); // true + TruffleExecutable majorVersion = TruffleExecutable.wrapSupplier(() -> 1L); + TruffleExecutable methodArgumentCount = TruffleExecutable.wrapSupplier(() -> 0L); + TruffleExecutable minorVersion = TruffleExecutable.wrapSupplier(() -> 17L); + TruffleExecutable primitiveFail = TruffleExecutable.wrapSupplier(() -> { assert false; return -1L; }); + TruffleExecutable stackValue = TruffleExecutable.wrapFunction((stackIndex) -> { + Object objectOnStack = FrameAccess.getStackValue(frame, (int)(long)stackIndex, FrameAccess.getNumArguments(frame)); int objectIndex = objectRegistry.size(); objectRegistry.add(objectOnStack); - return objectIndex; - }; + return (long)objectIndex; + }); final Object interpreterProxyPointer = interpreterProxyLibrary.invokeMember( interpreterProxy, "createInterpreterProxy", @@ -269,7 +327,6 @@ public Object execute(VirtualFrame frame) { primitiveFail, stackValue); System.out.println("interpreterProxyPointer = " + interpreterProxyPointer); - final Object initialiseOk = uuidPluginLibrary.invokeMember(uuidPlugin, "initialiseModule"); System.out.println("initialiseOk = " + initialiseOk); final Object setInterpreterOk = uuidPluginLibrary.invokeMember(uuidPlugin, "setInterpreter", interpreterProxyPointer); @@ -282,7 +339,11 @@ public Object execute(VirtualFrame frame) { } catch (Exception e) { System.out.println("error"); e.printStackTrace(System.err); - return null; + throw PrimitiveFailed.GENERIC_ERROR; + } finally { + for (var postPrimitiveCleanup : postPrimitiveCleanups) { + postPrimitiveCleanup.cleanup(); + } } } @@ -309,6 +370,36 @@ public Object executeWithArguments(VirtualFrame frame, Object... receiverAndArgu } } + interface PostPrimitiveCleanup { + void cleanup(); + } + + @ExportLibrary(InteropLibrary.class) + static class ByteStorage implements PostPrimitiveCleanup, TruffleObject { + byte[] storage; + public long nativeAddress; + + public ByteStorage(byte[] storage) { + this.storage = storage; + nativeAddress = UnsafeUtils.allocateNativeBytes(storage); + } + + @ExportMessage + public boolean isPointer() { + return true; + } + + @ExportMessage + public long asPointer() { + return nativeAddress; + } + + @Override + public void cleanup() { + UnsafeUtils.copyNativeBytesBackAndFree(nativeAddress, storage); + } + } + private static boolean isLoadInstVar(final int primitiveIndex) { return PRIMITIVE_LOAD_INST_VAR_LOWER_INDEX <= primitiveIndex && primitiveIndex <= PRIMITIVE_LOAD_INST_VAR_UPPER_INDEX; } diff --git a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/UnsafeUtils.java b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/UnsafeUtils.java index 324f0f31d..aea73e632 100644 --- a/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/UnsafeUtils.java +++ b/src/de.hpi.swa.trufflesqueak/src/de/hpi/swa/trufflesqueak/util/UnsafeUtils.java @@ -66,6 +66,17 @@ public static void copyShorts(final short[] src, final long srcPos, final short[ dest, Unsafe.ARRAY_SHORT_BASE_OFFSET + destPos * Unsafe.ARRAY_SHORT_INDEX_SCALE, Short.BYTES * length); } + public static long allocateNativeBytes(final byte[] src) { + final long address = UNSAFE.allocateMemory(src.length); + UNSAFE.copyMemory(src, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, address, src.length * Byte.BYTES); + return address; + } + + public static void copyNativeBytesBackAndFree(final long address, byte[] dest) { + UNSAFE.copyMemory(null, address, dest, Unsafe.ARRAY_BYTE_BASE_OFFSET, dest.length * Byte.BYTES); + UNSAFE.freeMemory(address); + } + public static long fromLongsOffset(final long offset) { return (offset - Unsafe.ARRAY_LONG_BASE_OFFSET) / Unsafe.ARRAY_LONG_INDEX_SCALE; }