Skip to content

Commit

Permalink
Added ArgumentProcessor
Browse files Browse the repository at this point in the history
  • Loading branch information
squid233 committed May 12, 2024
1 parent 07534af commit a20cacf
Show file tree
Hide file tree
Showing 11 changed files with 675 additions and 114 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
127 changes: 16 additions & 111 deletions src/main/java/overrun/marshal/Downcall.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -433,6 +389,7 @@ private static <T> 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());
Expand Down Expand Up @@ -533,69 +490,16 @@ private static <T> 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);
Expand Down Expand Up @@ -734,6 +638,7 @@ private static <T> T loadBytecode(MethodHandles.Lookup caller, SymbolLookup look
blockCodeBuilder.athrow();
})
);
//endregion
}
}));
});
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/overrun/marshal/gen/Convert.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
* <p>
* The type of the marked element must be {@code boolean}; otherwise this annotation will be ignored.
* <h2>Example</h2>
Expand Down
148 changes: 148 additions & 0 deletions src/main/java/overrun/marshal/gen/processor/ArgumentProcessor.java
Original file line number Diff line number Diff line change
@@ -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<ProcessorType, ArgumentProcessorContext> {
private static final ArgumentProcessor INSTANCE = new ArgumentProcessor();
private final List<Processor<ProcessorType, ArgumentProcessorContext>> 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<ProcessorType, ArgumentProcessorContext> 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";
};
}
}
Loading

0 comments on commit a20cacf

Please sign in to comment.