46 return forShared(LinkerOptionImpl::validateForDowncall, desc, options);
47 }
48
49 public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) {
50 return forShared(LinkerOptionImpl::validateForUpcall, desc, options);
51 }
52
53 private static LinkerOptions forShared(BiConsumer<LinkerOptionImpl, FunctionDescriptor> validator,
54 FunctionDescriptor desc, Linker.Option... options) {
55 Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
56
57 for (Linker.Option option : options) {
58 if (optionMap.containsKey(option.getClass())) {
59 throw new IllegalArgumentException("Duplicate option: " + option);
60 }
61 LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
62 validator.accept(opImpl, desc);
63 optionMap.put(option.getClass(), opImpl);
64 }
65
66 return new LinkerOptions(optionMap);
67 }
68
69 public static LinkerOptions empty() {
70 return EMPTY;
71 }
72
73 private <T extends Linker.Option> T getOption(Class<T> type) {
74 return type.cast(optionsMap.get(type));
75 }
76
77 public boolean isVarargsIndex(int argIndex) {
78 FirstVariadicArg fva = getOption(FirstVariadicArg.class);
79 return fva != null && argIndex >= fva.index();
80 }
81
82 public boolean hasCapturedCallState() {
83 return getOption(CaptureCallState.class) != null;
84 }
85
86 public Stream<CapturableState> capturedCallState() {
87 CaptureCallState stl = getOption(CaptureCallState.class);
88 return stl == null ? Stream.empty() : stl.saved().stream();
89 }
90
91 public boolean isVariadicFunction() {
92 FirstVariadicArg fva = getOption(FirstVariadicArg.class);
93 return fva != null;
94 }
95
96 public int firstVariadicArgIndex() {
97 return getOption(FirstVariadicArg.class).index();
98 }
99
100 public boolean isTrivial() {
101 IsTrivial it = getOption(IsTrivial.class);
102 return it != null;
103 }
104
105 @Override
106 public boolean equals(Object o) {
107 if (this == o) return true;
108 return o instanceof LinkerOptions that
109 && Objects.equals(optionsMap, that.optionsMap);
110 }
111
112 @Override
113 public int hashCode() {
114 return Objects.hash(optionsMap);
115 }
116
117 public sealed interface LinkerOptionImpl extends Linker.Option
118 permits CaptureCallState, FirstVariadicArg, IsTrivial {
119 default void validateForDowncall(FunctionDescriptor descriptor) {
120 throw new IllegalArgumentException("Not supported for downcall: " + this);
121 }
122
123 default void validateForUpcall(FunctionDescriptor descriptor) {
124 throw new IllegalArgumentException("Not supported for upcall: " + this);
125 }
126 }
127
128 public record FirstVariadicArg(int index) implements LinkerOptionImpl {
129 @Override
130 public void validateForDowncall(FunctionDescriptor descriptor) {
131 if (index < 0 || index > descriptor.argumentLayouts().size()) {
132 throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor);
133 }
134 }
135 }
136
137 public record CaptureCallState(Set<CapturableState> saved) implements LinkerOptionImpl {
138 @Override
139 public void validateForDowncall(FunctionDescriptor descriptor) {
140 // done during construction
141 }
142 }
143
144 public record IsTrivial() implements LinkerOptionImpl {
145 public static IsTrivial INSTANCE = new IsTrivial();
146
147 @Override
148 public void validateForDowncall(FunctionDescriptor descriptor) {
149 // always allowed
150 }
151 }
152 }
|
46 return forShared(LinkerOptionImpl::validateForDowncall, desc, options);
47 }
48
49 public static LinkerOptions forUpcall(FunctionDescriptor desc, Linker.Option[] options) {
50 return forShared(LinkerOptionImpl::validateForUpcall, desc, options);
51 }
52
53 private static LinkerOptions forShared(BiConsumer<LinkerOptionImpl, FunctionDescriptor> validator,
54 FunctionDescriptor desc, Linker.Option... options) {
55 Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
56
57 for (Linker.Option option : options) {
58 if (optionMap.containsKey(option.getClass())) {
59 throw new IllegalArgumentException("Duplicate option: " + option);
60 }
61 LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
62 validator.accept(opImpl, desc);
63 optionMap.put(option.getClass(), opImpl);
64 }
65
66 LinkerOptions linkerOptions = new LinkerOptions(optionMap);
67 if (linkerOptions.hasCapturedCallState() && linkerOptions.isCritical()) {
68 throw new IllegalArgumentException("Incompatible linker options: captureCallState, critical");
69 }
70 return linkerOptions;
71 }
72
73 public static LinkerOptions empty() {
74 return EMPTY;
75 }
76
77 private <T extends Linker.Option> T getOption(Class<T> type) {
78 return type.cast(optionsMap.get(type));
79 }
80
81 public boolean isVarargsIndex(int argIndex) {
82 FirstVariadicArg fva = getOption(FirstVariadicArg.class);
83 return fva != null && argIndex >= fva.index();
84 }
85
86 public boolean hasCapturedCallState() {
87 return getOption(CaptureCallState.class) != null;
88 }
89
90 public Stream<CapturableState> capturedCallState() {
91 CaptureCallState stl = getOption(CaptureCallState.class);
92 return stl == null ? Stream.empty() : stl.saved().stream();
93 }
94
95 public boolean isVariadicFunction() {
96 FirstVariadicArg fva = getOption(FirstVariadicArg.class);
97 return fva != null;
98 }
99
100 public int firstVariadicArgIndex() {
101 return getOption(FirstVariadicArg.class).index();
102 }
103
104 public boolean isCritical() {
105 Critical c = getOption(Critical.class);
106 return c != null;
107 }
108
109 @Override
110 public boolean equals(Object o) {
111 if (this == o) return true;
112 return o instanceof LinkerOptions that
113 && Objects.equals(optionsMap, that.optionsMap);
114 }
115
116 @Override
117 public int hashCode() {
118 return Objects.hash(optionsMap);
119 }
120
121 public sealed interface LinkerOptionImpl extends Linker.Option
122 permits CaptureCallState, FirstVariadicArg, Critical {
123 default void validateForDowncall(FunctionDescriptor descriptor) {
124 throw new IllegalArgumentException("Not supported for downcall: " + this);
125 }
126
127 default void validateForUpcall(FunctionDescriptor descriptor) {
128 throw new IllegalArgumentException("Not supported for upcall: " + this);
129 }
130 }
131
132 public record FirstVariadicArg(int index) implements LinkerOptionImpl {
133 @Override
134 public void validateForDowncall(FunctionDescriptor descriptor) {
135 if (index < 0 || index > descriptor.argumentLayouts().size()) {
136 throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor);
137 }
138 }
139 }
140
141 public record CaptureCallState(Set<CapturableState> saved) implements LinkerOptionImpl {
142 @Override
143 public void validateForDowncall(FunctionDescriptor descriptor) {
144 // done during construction
145 }
146 }
147
148 public record Critical() implements LinkerOptionImpl {
149 public static Critical INSTANCE = new Critical();
150
151 @Override
152 public void validateForDowncall(FunctionDescriptor descriptor) {
153 // always allowed
154 }
155 }
156 }
|