From c2001bf3c977f8ff9324aa8af3c5c48eab294f7c Mon Sep 17 00:00:00 2001 From: squid233 <60126026+squid233@users.noreply.github.com> Date: Sat, 16 Dec 2023 12:57:14 +0800 Subject: [PATCH] Added annotation Critical --- README.md | 4 ++ .../overrun/marshal/test/CDowncallTest.java | 6 +++ src/main/java/overrun/marshal/Critical.java | 41 +++++++++++++++++++ .../overrun/marshal/DowncallProcessor.java | 16 +++++++- src/main/java/overrun/marshal/gen/Spec.java | 11 +++++ 5 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/main/java/overrun/marshal/Critical.java diff --git a/README.md b/README.md index e66442f..228b6b5 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,12 @@ interface CGLFW { /** * Fixed size array. * Note: this method doesn't exist in GLFW + *

+ * You can mark methods with Critical * * @param arr The array */ + @Critical(allowHeapAccess = true) void fixedSizeArray(@FixedSize(2) int[] arr); /** @@ -97,6 +100,7 @@ class Main { double time = GLFW.getTime(); GLFW.fixedSizeArray(new int[]{4, 2}); MemorySegment windowHandle = /*...*/createWindow(); + // MemoryStack is a placeholder type try (MemoryStack stack = /*...*/stackPush()) { MemorySegment bufX1 = stack.callocInt(1); MemorySegment bufY1 = stack.callocInt(1); diff --git a/demo/src/main/java/overrun/marshal/test/CDowncallTest.java b/demo/src/main/java/overrun/marshal/test/CDowncallTest.java index dd9b399..9ab308a 100644 --- a/demo/src/main/java/overrun/marshal/test/CDowncallTest.java +++ b/demo/src/main/java/overrun/marshal/test/CDowncallTest.java @@ -112,6 +112,12 @@ interface CDowncallTest { @Overload void testMixArrSeg(MemorySegment segment, @Ref int[] arr); + @Critical(allowHeapAccess = true) + void testCriticalTrue(); + + @Critical(allowHeapAccess = false) + void testCriticalFalse(); + /** * This is a test that tests all features. * diff --git a/src/main/java/overrun/marshal/Critical.java b/src/main/java/overrun/marshal/Critical.java new file mode 100644 index 0000000..1fdd09c --- /dev/null +++ b/src/main/java/overrun/marshal/Critical.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2023 Overrun Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ + +package overrun.marshal; + +import java.lang.annotation.*; + +/** + * Marks a method that invokes a critical function. + *

Example

+ *
{@code
+ * @Critical(allowHeapAccess = false)
+ * void criticalFunction();
+ * }
+ * + * @author squid233 + * @see java.lang.foreign.Linker.Option#critical(boolean) Linker.Option.critical + * @since 0.1.0 + */ +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Critical { + /** + * {@return whether the linked function should allow access to the Java heap} + */ + boolean allowHeapAccess(); +} diff --git a/src/main/java/overrun/marshal/DowncallProcessor.java b/src/main/java/overrun/marshal/DowncallProcessor.java index 2366d37..de15e07 100644 --- a/src/main/java/overrun/marshal/DowncallProcessor.java +++ b/src/main/java/overrun/marshal/DowncallProcessor.java @@ -102,6 +102,7 @@ private void writeFile( file.addImports( BoolHelper.class, Checks.class, + Critical.class, Default.class, FixedSize.class, Ref.class, @@ -154,12 +155,18 @@ private void writeFile( final boolean shouldStoreResult = notVoid && parameters.stream().anyMatch(p -> p.getAnnotation(Ref.class) != null); final Access access = e.getAnnotation(Access.class); + final Critical critical = e.getAnnotation(Critical.class); final Custom custom = e.getAnnotation(Custom.class); final Default defaultAnnotation = e.getAnnotation(Default.class); final boolean isDefaulted = defaultAnnotation != null; methodSpec.setDocument(getDocument(e)); methodSpec.setStatic(true); + + if (critical != null) { + methodSpec.addAnnotation(new AnnotationSpec(Critical.class) + .addArgument("allowHeapAccess", getConstExp(critical.allowHeapAccess()))); + } if (isDefaulted) { methodSpec.addAnnotation(new AnnotationSpec(Default.class).also(annotationSpec -> { if (!defaultAnnotation.value().isBlank()) { @@ -167,6 +174,7 @@ private void writeFile( } })); } + if (access != null) { methodSpec.setAccessModifier(access.value()); } @@ -502,6 +510,7 @@ private void addMethodHandles(TypeElement type, List methods, return e2; }, LinkedHashMap::new)).forEach((k, v) -> { final TypeMirror returnType = v.getReturnType(); + final Critical critical = v.getAnnotation(Critical.class); final boolean defaulted = v.getAnnotation(Default.class) != null; classSpec.addField(new VariableStatement(MethodHandle.class, k, new InvokeSpec(new InvokeSpec( @@ -516,7 +525,12 @@ private void addMethodHandles(TypeElement type, List methods, invokeSpec.addArgument(toValueLayout(returnType)); } v.getParameters().forEach(e -> invokeSpec.addArgument(toValueLayout(e.asType()))); - })))), defaulted ? "orElse" : "orElseThrow").also(invokeSpec -> { + })).also(invokeSpec -> { + if (critical != null) { + invokeSpec.addArgument(new InvokeSpec(Spec.accessSpec(Linker.class, Linker.Option.class), "critical") + .addArgument(getConstExp(critical.allowHeapAccess()))); + } + }))), defaulted ? "orElse" : "orElseThrow").also(invokeSpec -> { if (defaulted) { invokeSpec.addArgument("null"); } diff --git a/src/main/java/overrun/marshal/gen/Spec.java b/src/main/java/overrun/marshal/gen/Spec.java index 26499b5..2989d61 100644 --- a/src/main/java/overrun/marshal/gen/Spec.java +++ b/src/main/java/overrun/marshal/gen/Spec.java @@ -56,6 +56,17 @@ static Spec accessSpec(Class object, String member) { return literal(object.getSimpleName() + '.' + member); } + /** + * Create access spec + * + * @param object object + * @param member member + * @return spec + */ + static Spec accessSpec(Class object, Class member) { + return literal(object.getSimpleName() + '.' + member.getSimpleName()); + } + /** * Create access spec *