From a20cacf2e2f5b800f5bb19f3742d5d1bd6b39407 Mon Sep 17 00:00:00 2001 From: squid233 <60126026+squid233@users.noreply.github.com> Date: Sun, 12 May 2024 17:43:54 +0800 Subject: [PATCH] Added ArgumentProcessor --- README.md | 2 +- gradle.properties | 2 +- src/main/java/module-info.java | 2 + src/main/java/overrun/marshal/Downcall.java | 127 ++------- .../java/overrun/marshal/gen/Convert.java | 3 +- .../gen/processor/ArgumentProcessor.java | 148 ++++++++++ .../processor/ArgumentProcessorContext.java | 39 +++ .../marshal/gen/processor/Processor.java | 39 +++ .../marshal/gen/processor/ProcessorType.java | 265 ++++++++++++++++++ .../marshal/gen/processor/ProcessorTypes.java | 76 +++++ .../marshal/internal/StringCharset.java | 86 ++++++ 11 files changed, 675 insertions(+), 114 deletions(-) create mode 100644 src/main/java/overrun/marshal/gen/processor/ArgumentProcessor.java create mode 100644 src/main/java/overrun/marshal/gen/processor/ArgumentProcessorContext.java create mode 100644 src/main/java/overrun/marshal/gen/processor/Processor.java create mode 100644 src/main/java/overrun/marshal/gen/processor/ProcessorType.java create mode 100644 src/main/java/overrun/marshal/gen/processor/ProcessorTypes.java create mode 100644 src/main/java/overrun/marshal/internal/StringCharset.java diff --git a/README.md b/README.md index d24acee..4ec624d 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Import as a Gradle dependency: ```groovy dependencies { - implementation("io.github.over-run:marshal:0.1.0-alpha.25-jdk22") + implementation("io.github.over-run:marshal:0.1.0-alpha.26-jdk22") } ``` diff --git a/gradle.properties b/gradle.properties index b57879a..34c5ad7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ projGroupId=io.github.over-run projArtifactId=marshal # The project name should only contain lowercase letters, numbers and hyphen. projName=marshal -projVersion=0.1.0-alpha.25-jdk22 +projVersion=0.1.0-alpha.26-jdk22 projDesc=Marshaler of native libraries # Uncomment them if you want to publish to maven repository. projUrl=https://github.com/Over-Run/marshal diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4d35c6f..01c157d 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -23,9 +23,11 @@ module io.github.overrun.marshal { exports overrun.marshal; exports overrun.marshal.gen; + exports overrun.marshal.gen.processor; exports overrun.marshal.struct; opens overrun.marshal.internal.data; requires static org.jetbrains.annotations; + requires jdk.jshell; } diff --git a/src/main/java/overrun/marshal/Downcall.java b/src/main/java/overrun/marshal/Downcall.java index f2290eb..8db663b 100644 --- a/src/main/java/overrun/marshal/Downcall.java +++ b/src/main/java/overrun/marshal/Downcall.java @@ -16,8 +16,10 @@ package overrun.marshal; -import overrun.marshal.gen.Type; import overrun.marshal.gen.*; +import overrun.marshal.gen.processor.ArgumentProcessor; +import overrun.marshal.gen.processor.ArgumentProcessorContext; +import overrun.marshal.gen.processor.ProcessorTypes; import overrun.marshal.internal.DowncallOptions; import overrun.marshal.internal.data.DowncallData; import overrun.marshal.struct.ByValue; @@ -45,6 +47,7 @@ import static java.lang.classfile.ClassFile.*; import static java.lang.constant.ConstantDescs.*; import static overrun.marshal.internal.Constants.*; +import static overrun.marshal.internal.StringCharset.getCharset; /** * Downcall library loader. @@ -190,18 +193,6 @@ private static String unmarshalMethod(Class aClass) { return "unmarshal"; } - private static String marshalFromBooleanMethod(Type type) { - return switch (type) { - case CHAR -> "marshalAsChar"; - case BYTE -> "marshalAsByte"; - case SHORT -> "marshalAsShort"; - case INT -> "marshalAsInt"; - case LONG -> "marshalAsLong"; - case FLOAT -> "marshalAsFloat"; - case DOUBLE -> "marshalAsDouble"; - }; - } - // type private static ValueLayout getValueLayout(Class carrier) { @@ -223,41 +214,6 @@ private static boolean requireAllocator(Class aClass) { Upcall.class.isAssignableFrom(aClass)); } - // string charset - - private static boolean hasCharset(StrCharset strCharset) { - return strCharset != null && !strCharset.value().isBlank(); - } - - private static String getCharset(AnnotatedElement element) { - final StrCharset strCharset = element.getDeclaredAnnotation(StrCharset.class); - return hasCharset(strCharset) ? strCharset.value() : null; - } - - private static void getCharset(CodeBuilder codeBuilder, String charset) { - final String upperCase = charset.toUpperCase(Locale.ROOT); - switch (upperCase) { - case "UTF-8", "ISO-8859-1", "US-ASCII", - "UTF-16", "UTF-16BE", "UTF-16LE", - "UTF-32", "UTF-32BE", "UTF-32LE" -> - codeBuilder.getstatic(CD_StandardCharsets, upperCase.replace('-', '_'), CD_Charset); - case "UTF_8", "ISO_8859_1", "US_ASCII", - "UTF_16", "UTF_16BE", "UTF_16LE", - "UTF_32", "UTF_32BE", "UTF_32LE" -> codeBuilder.getstatic(CD_StandardCharsets, upperCase, CD_Charset); - default -> codeBuilder.ldc(charset) - .invokestatic(CD_Charset, "forName", MTD_Charset_String); - } - } - - private static boolean getCharset(CodeBuilder codeBuilder, AnnotatedElement element) { - final String charset = getCharset(element); - if (charset != null) { - getCharset(codeBuilder, charset); - return true; - } - return false; - } - // class desc private static ClassDesc convertToDowncallCD(AnnotatedElement element, Class aClass) { @@ -433,6 +389,7 @@ private static T loadBytecode(MethodHandles.Lookup caller, SymbolLookup look targetClass.isInterface() ).returnInstruction(returnTypeKind); } else { + //region body final boolean hasAllocator = !parameters.isEmpty() && SegmentAllocator.class.isAssignableFrom(parameters.getFirst().getType()); @@ -533,69 +490,16 @@ private static T loadBytecode(MethodHandles.Lookup caller, SymbolLookup look if (refSlot != null) { blockCodeBuilder.aload(refSlot); } else { - final int slot = blockCodeBuilder.parameterSlot(i); - final Convert convert = parameter.getDeclaredAnnotation(Convert.class); - if (convert != null && type == boolean.class) { - final Type convertType = convert.value(); - blockCodeBuilder.loadInstruction( - TypeKind.fromDescriptor(type.descriptorString()).asLoadable(), - slot - ).invokestatic(CD_Marshal, - marshalFromBooleanMethod(convertType), - MethodTypeDesc.of(convertType.classDesc(), CD_boolean)); - } else if (type.isPrimitive() || - type == MemorySegment.class || - SegmentAllocator.class.isAssignableFrom(type)) { - blockCodeBuilder.loadInstruction( - TypeKind.fromDescriptor(type.descriptorString()).asLoadable(), - slot - ); - } else if (type == String.class) { - blockCodeBuilder.aload(allocatorSlot) - .aload(slot); - if (getCharset(blockCodeBuilder, parameter)) { - blockCodeBuilder.invokestatic(CD_Marshal, - "marshal", - MTD_MemorySegment_SegmentAllocator_String_Charset); - } else { - blockCodeBuilder.invokestatic(CD_Marshal, - "marshal", - MTD_MemorySegment_SegmentAllocator_String); - } - } else if (Addressable.class.isAssignableFrom(type) || - CEnum.class.isAssignableFrom(type)) { - blockCodeBuilder.aload(slot) - .invokestatic(CD_Marshal, - "marshal", - MethodTypeDesc.of(cd_parameterDowncall, convertToMarshalCD(type))); - } else if (Upcall.class.isAssignableFrom(type)) { - blockCodeBuilder.aload(allocatorSlot) - .checkcast(CD_Arena) - .aload(slot) - .invokestatic(CD_Marshal, - "marshal", - MTD_MemorySegment_Arena_Upcall); - } else if (type.isArray()) { - final Class componentType = type.getComponentType(); - final boolean isStringArray = componentType == String.class; - final boolean isUpcallArray = Upcall.class.isAssignableFrom(componentType); - blockCodeBuilder.aload(allocatorSlot); - if (isUpcallArray) { - blockCodeBuilder.checkcast(CD_Arena); - } - blockCodeBuilder.aload(slot); - if (isStringArray && getCharset(blockCodeBuilder, parameter)) { - blockCodeBuilder.invokestatic(CD_Marshal, - "marshal", - MTD_MemorySegment_SegmentAllocator_StringArray_Charset); - } else { - blockCodeBuilder.invokestatic(CD_Marshal, - "marshal", - MethodTypeDesc.of(CD_MemorySegment, - isUpcallArray ? CD_Arena : CD_SegmentAllocator, - convertToMarshalCD(componentType).arrayType())); - } - } + ArgumentProcessor.getInstance().process( + blockCodeBuilder, + ProcessorTypes.fromClass(type), + new ArgumentProcessorContext( + parameter, + blockCodeBuilder.parameterSlot(i), + allocatorSlot, + parameter.getDeclaredAnnotation(Convert.class) + ) + ); } parameterCDList.add(cd_parameterDowncall); @@ -734,6 +638,7 @@ private static T loadBytecode(MethodHandles.Lookup caller, SymbolLookup look blockCodeBuilder.athrow(); }) ); + //endregion } })); }); diff --git a/src/main/java/overrun/marshal/gen/Convert.java b/src/main/java/overrun/marshal/gen/Convert.java index fd9420f..fdf3ace 100644 --- a/src/main/java/overrun/marshal/gen/Convert.java +++ b/src/main/java/overrun/marshal/gen/Convert.java @@ -19,7 +19,8 @@ import java.lang.annotation.*; /** - * Marks an element that needs to convert from {@code boolean} to the given type. + * Marks a parameter that needs to convert from {@code boolean} to the given type, + * or a return type that needs to convert from the given type to {@code boolean}. *

* The type of the marked element must be {@code boolean}; otherwise this annotation will be ignored. *

Example

diff --git a/src/main/java/overrun/marshal/gen/processor/ArgumentProcessor.java b/src/main/java/overrun/marshal/gen/processor/ArgumentProcessor.java new file mode 100644 index 0000000..8278a39 --- /dev/null +++ b/src/main/java/overrun/marshal/gen/processor/ArgumentProcessor.java @@ -0,0 +1,148 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.gen.processor; + +import overrun.marshal.gen.Type; +import overrun.marshal.internal.StringCharset; + +import java.lang.classfile.CodeBuilder; +import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.constant.ConstantDescs.CD_boolean; +import static overrun.marshal.internal.Constants.*; + +/** + * Method argument processor + * + * @author squid233 + * @since 0.1.0 + */ +public final class ArgumentProcessor implements Processor { + private static final ArgumentProcessor INSTANCE = new ArgumentProcessor(); + private final List> list = new ArrayList<>(0); + + private ArgumentProcessor() { + } + + @SuppressWarnings("preview") + public boolean process(CodeBuilder builder, ProcessorType type, ArgumentProcessorContext context) { + switch (type) { + case ProcessorType.Value value -> { + if (value == ProcessorType.Value.BOOLEAN && + context.convert() != null) { + final Type convertType = context.convert().value(); + builder.loadInstruction( + value.typeKind(), + context.parameterSlot() + ).invokestatic(CD_Marshal, + marshalFromBooleanMethod(convertType), + MethodTypeDesc.of(convertType.classDesc(), CD_boolean)); + } else { + builder.loadInstruction( + value.typeKind().asLoadable(), + context.parameterSlot() + ); + } + } + case ProcessorType.Allocator _ -> builder.aload(context.parameterSlot()); + case ProcessorType.Str _ -> { + builder.aload(context.allocatorSlot()) + .aload(context.parameterSlot()); + if (StringCharset.getCharset(builder, context.parameter())) { + builder.invokestatic(CD_Marshal, + "marshal", + MTD_MemorySegment_SegmentAllocator_String_Charset); + } else { + builder.invokestatic(CD_Marshal, + "marshal", + MTD_MemorySegment_SegmentAllocator_String); + } + } + case ProcessorType.Addr _, ProcessorType.CEnum _ -> builder + .aload(context.parameterSlot()) + .invokestatic(CD_Marshal, + "marshal", + MethodTypeDesc.of(type.downcallClassDesc(), type.marshalClassDesc())); + case ProcessorType.Upcall _ -> builder + .aload(context.allocatorSlot()) + .checkcast(CD_Arena) + .aload(context.parameterSlot()) + .invokestatic(CD_Marshal, + "marshal", + MTD_MemorySegment_Arena_Upcall); + case ProcessorType.Array array -> { + final ProcessorType componentType = array.componentType(); + final boolean isStringArray = componentType instanceof ProcessorType.Str; + final boolean isUpcallArray = componentType instanceof ProcessorType.Upcall; + builder.aload(context.allocatorSlot()); + if (isUpcallArray) { + builder.checkcast(CD_Arena); + } + builder.aload(context.parameterSlot()); + if (isStringArray && StringCharset.getCharset(builder, context.parameter())) { + builder.invokestatic(CD_Marshal, + "marshal", + MTD_MemorySegment_SegmentAllocator_StringArray_Charset); + } else { + builder.invokestatic(CD_Marshal, + "marshal", + MethodTypeDesc.of(CD_MemorySegment, + isUpcallArray ? CD_Arena : CD_SegmentAllocator, + array.marshalClassDesc())); + } + } + default -> { + for (var processor : list) { + if (!processor.process(builder, type, context)) { + break; + } + } + } + } + return false; + } + + /** + * Registers a processor + * + * @param processor the processor + */ + public void registerProcessor(Processor processor) { + list.add(processor); + } + + /** + * {@return this} + */ + public static ArgumentProcessor getInstance() { + return INSTANCE; + } + + private static String marshalFromBooleanMethod(Type convertType) { + return switch (convertType) { + case CHAR -> "marshalAsChar"; + case BYTE -> "marshalAsByte"; + case SHORT -> "marshalAsShort"; + case INT -> "marshalAsInt"; + case LONG -> "marshalAsLong"; + case FLOAT -> "marshalAsFloat"; + case DOUBLE -> "marshalAsDouble"; + }; + } +} diff --git a/src/main/java/overrun/marshal/gen/processor/ArgumentProcessorContext.java b/src/main/java/overrun/marshal/gen/processor/ArgumentProcessorContext.java new file mode 100644 index 0000000..6fc4f25 --- /dev/null +++ b/src/main/java/overrun/marshal/gen/processor/ArgumentProcessorContext.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.gen.processor; + +import overrun.marshal.gen.Convert; + +import java.lang.reflect.Parameter; + +/** + * The argument processor context + * + * @param parameter parameter + * @param parameterSlot parameter slot + * @param allocatorSlot allocator slot + * @param convert boolean convert + * @author squid233 + * @since 0.1.0 + */ +public record ArgumentProcessorContext( + Parameter parameter, + int parameterSlot, + int allocatorSlot, + Convert convert +) { +} diff --git a/src/main/java/overrun/marshal/gen/processor/Processor.java b/src/main/java/overrun/marshal/gen/processor/Processor.java new file mode 100644 index 0000000..567a7cd --- /dev/null +++ b/src/main/java/overrun/marshal/gen/processor/Processor.java @@ -0,0 +1,39 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.gen.processor; + +import java.lang.classfile.CodeBuilder; + +/** + * Processor + * + * @param target type + * @param context type + * @author squid233 + * @since 0.1.0 + */ +public interface Processor { + /** + * Processes with the context + * + * @param builder the code builder + * @param type the type + * @param context the context + * @return {@code true} if should continue; {@code false} otherwise + */ + boolean process(CodeBuilder builder, T type, C context); +} diff --git a/src/main/java/overrun/marshal/gen/processor/ProcessorType.java b/src/main/java/overrun/marshal/gen/processor/ProcessorType.java new file mode 100644 index 0000000..c963d36 --- /dev/null +++ b/src/main/java/overrun/marshal/gen/processor/ProcessorType.java @@ -0,0 +1,265 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.gen.processor; + +import overrun.marshal.Addressable; + +import java.lang.classfile.TypeKind; +import java.lang.constant.ClassDesc; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.lang.foreign.ValueLayout; + +import static java.lang.constant.ConstantDescs.*; +import static overrun.marshal.internal.Constants.*; + +/** + * Types to be processed + * + * @author squid233 + * @since 0.1.0 + */ +public sealed interface ProcessorType { + /** + * @return the class desc for method handles + */ + ClassDesc downcallClassDesc(); + + /** + * @return the class desc for methods in {@link overrun.marshal.Marshal} + */ + ClassDesc marshalClassDesc(); + + /** + * Primitive types, including {@link MemorySegment} + */ + enum Value implements ProcessorType { + /** + * {@code boolean} type + */ + BOOLEAN(CD_boolean, ValueLayout.JAVA_BOOLEAN), + /** + * {@code char} type + */ + CHAR(CD_char, ValueLayout.JAVA_CHAR), + /** + * {@code byte} type + */ + BYTE(CD_byte, ValueLayout.JAVA_BYTE), + /** + * {@code short} type + */ + SHORT(CD_short, ValueLayout.JAVA_SHORT), + /** + * {@code int} type + */ + INT(CD_int, ValueLayout.JAVA_INT), + /** + * {@code long} type + */ + LONG(CD_long, ValueLayout.JAVA_LONG), + /** + * {@code float} type + */ + FLOAT(CD_float, ValueLayout.JAVA_FLOAT), + /** + * {@code double} type + */ + DOUBLE(CD_double, ValueLayout.JAVA_DOUBLE), + /** + * {@link MemorySegment} type + */ + ADDRESS(CD_MemorySegment, ValueLayout.ADDRESS); + + private final ClassDesc classDesc; + private final TypeKind typeKind; + private final ValueLayout layout; + + Value(ClassDesc classDesc, ValueLayout layout) { + this.classDesc = classDesc; + this.typeKind = TypeKind.from(classDesc); + this.layout = layout; + } + + /** + * {@return the class desc of this type} + */ + public ClassDesc classDesc() { + return classDesc; + } + + /** + * {@return the type kind of this type} + */ + public TypeKind typeKind() { + return typeKind; + } + + /** + * {@return the layout of this type} + */ + public ValueLayout layout() { + return layout; + } + + @Override + public ClassDesc downcallClassDesc() { + return classDesc(); + } + + @Override + public ClassDesc marshalClassDesc() { + return classDesc(); + } + } + + /** + * {@link SegmentAllocator} + */ + final class Allocator implements ProcessorType { + /** + * The instance + */ + public static final Allocator INSTANCE = new Allocator(); + + private Allocator() { + } + + @Override + public ClassDesc downcallClassDesc() { + return CD_SegmentAllocator; + } + + @Override + public ClassDesc marshalClassDesc() { + return CD_SegmentAllocator; + } + } + + /** + * {@link String} + */ + final class Str implements ProcessorType { + /** + * The instance + */ + public static final Str INSTANCE = new Str(); + + private Str() { + } + + @Override + public ClassDesc downcallClassDesc() { + return CD_MemorySegment; + } + + @Override + public ClassDesc marshalClassDesc() { + return CD_String; + } + } + + /** + * {@link Addressable} + */ + final class Addr implements ProcessorType { + /** + * The instance + */ + public static final Addr INSTANCE = new Addr(); + + private Addr() { + } + + @Override + public ClassDesc downcallClassDesc() { + return CD_MemorySegment; + } + + @Override + public ClassDesc marshalClassDesc() { + return CD_Addressable; + } + } + + /** + * {@link overrun.marshal.CEnum} + */ + final class CEnum implements ProcessorType { + /** + * The instance + */ + public static final CEnum INSTANCE = new CEnum(); + + private CEnum() { + } + + @Override + public ClassDesc downcallClassDesc() { + return CD_int; + } + + @Override + public ClassDesc marshalClassDesc() { + return CD_CEnum; + } + } + + /** + * {@link overrun.marshal.Upcall} + */ + final class Upcall implements ProcessorType { + /** + * The instance + */ + public static final Upcall INSTANCE = new Upcall(); + + private Upcall() { + } + + @Override + public ClassDesc downcallClassDesc() { + return CD_MemorySegment; + } + + @Override + public ClassDesc marshalClassDesc() { + return CD_Upcall; + } + } + + /** + * Array type + */ + record Array(ProcessorType componentType) implements ProcessorType { + @Override + public ClassDesc downcallClassDesc() { + return CD_MemorySegment; + } + + @Override + public ClassDesc marshalClassDesc() { + return componentType().marshalClassDesc().arrayType(); + } + } + + /** + * Custom type + */ + non-sealed interface Custom extends ProcessorType { + } +} diff --git a/src/main/java/overrun/marshal/gen/processor/ProcessorTypes.java b/src/main/java/overrun/marshal/gen/processor/ProcessorTypes.java new file mode 100644 index 0000000..3f59073 --- /dev/null +++ b/src/main/java/overrun/marshal/gen/processor/ProcessorTypes.java @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.gen.processor; + +import overrun.marshal.Addressable; +import overrun.marshal.CEnum; +import overrun.marshal.Upcall; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.SegmentAllocator; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Processor types + * + * @author squid233 + * @since 0.1.0 + */ +public final class ProcessorTypes { + private static final Map, ProcessorType> map = new HashMap<>(0); + + /** + * Get the processor type from the given class. + * + * @param aClass the class + * @return the processor type + */ + public static ProcessorType fromClass(Class aClass) { + if (aClass == boolean.class) return ProcessorType.Value.BOOLEAN; + if (aClass == char.class) return ProcessorType.Value.CHAR; + if (aClass == byte.class) return ProcessorType.Value.BYTE; + if (aClass == short.class) return ProcessorType.Value.SHORT; + if (aClass == int.class) return ProcessorType.Value.INT; + if (aClass == long.class) return ProcessorType.Value.LONG; + if (aClass == float.class) return ProcessorType.Value.FLOAT; + if (aClass == double.class) return ProcessorType.Value.DOUBLE; + if (aClass == MemorySegment.class) return ProcessorType.Value.ADDRESS; + if (aClass == String.class) return ProcessorType.Str.INSTANCE; + if (SegmentAllocator.class.isAssignableFrom(aClass)) return ProcessorType.Allocator.INSTANCE; + if (Addressable.class.isAssignableFrom(aClass)) return ProcessorType.Addr.INSTANCE; + if (CEnum.class.isAssignableFrom(aClass)) return ProcessorType.CEnum.INSTANCE; + if (Upcall.class.isAssignableFrom(aClass)) return ProcessorType.Upcall.INSTANCE; + if (aClass.isArray()) return new ProcessorType.Array(fromClass(aClass.componentType())); + return Objects.requireNonNull(map.get(aClass), STR."Cannot find processor type of \{aClass}"); + } + + /** + * Registers a processor type for the given class. + * + * @param aClass the class + * @param type the processor type + */ + public static void registerClass(Class aClass, ProcessorType type) { + if (type != null) { + map.put(aClass, type); + } else { + map.remove(aClass); + } + } +} diff --git a/src/main/java/overrun/marshal/internal/StringCharset.java b/src/main/java/overrun/marshal/internal/StringCharset.java new file mode 100644 index 0000000..483a1d4 --- /dev/null +++ b/src/main/java/overrun/marshal/internal/StringCharset.java @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2024 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.internal; + +import overrun.marshal.gen.StrCharset; + +import java.lang.classfile.CodeBuilder; +import java.lang.reflect.AnnotatedElement; +import java.util.Locale; + +import static overrun.marshal.internal.Constants.*; + +/** + * String charsets + * + * @author squid233 + * @since 0.1.0 + */ +public final class StringCharset { + /** + * {@return hasCharset} + * + * @param strCharset strCharset + */ + public static boolean hasCharset(StrCharset strCharset) { + return strCharset != null && !strCharset.value().isBlank(); + } + + /** + * {@return getCharset} + * + * @param element element + */ + public static String getCharset(AnnotatedElement element) { + final StrCharset strCharset = element.getDeclaredAnnotation(StrCharset.class); + return hasCharset(strCharset) ? strCharset.value() : null; + } + + /** + * @param codeBuilder codeBuilder + * @param charset charset + */ + public static void getCharset(CodeBuilder codeBuilder, String charset) { + final String upperCase = charset.toUpperCase(Locale.ROOT); + switch (upperCase) { + case "UTF-8", "ISO-8859-1", "US-ASCII", + "UTF-16", "UTF-16BE", "UTF-16LE", + "UTF-32", "UTF-32BE", "UTF-32LE" -> + codeBuilder.getstatic(CD_StandardCharsets, upperCase.replace('-', '_'), CD_Charset); + case "UTF_8", "ISO_8859_1", "US_ASCII", + "UTF_16", "UTF_16BE", "UTF_16LE", + "UTF_32", "UTF_32BE", "UTF_32LE" -> codeBuilder.getstatic(CD_StandardCharsets, upperCase, CD_Charset); + default -> codeBuilder.ldc(charset) + .invokestatic(CD_Charset, "forName", MTD_Charset_String); + } + } + + /** + * {@return getCharset} + * + * @param codeBuilder codeBuilder + * @param element element + */ + public static boolean getCharset(CodeBuilder codeBuilder, AnnotatedElement element) { + final String charset = getCharset(element); + if (charset != null) { + getCharset(codeBuilder, charset); + return true; + } + return false; + } +}