Skip to content

Commit 95430b2

Browse files
authored
Adds vm/AccessFlags.java. Adds bridge and origin elements to Domain. Fixes concurrency bug. (#19)
Signed-off-by: Laird Nelson <[email protected]>
1 parent f9d2b46 commit 95430b2

File tree

7 files changed

+323
-3
lines changed

7 files changed

+323
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ as a Maven dependency:
3131
Always check https://search.maven.org/artifact/org.microbean/microbean-construct
3232
for up-to-date available versions.
3333
-->
34-
<version>0.0.8</version>
34+
<version>0.0.10</version>
3535
</dependency>
3636
```
3737

src/main/java/org/microbean/construct/DefaultDomain.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import javax.lang.model.element.TypeElement;
4141

4242
import javax.lang.model.util.Elements;
43+
import javax.lang.model.util.Elements.Origin;
4344
import javax.lang.model.util.Types;
4445

4546
import javax.lang.model.type.DeclaredType;
@@ -203,6 +204,14 @@ public StringName binaryName(TypeElement e) {
203204
}
204205
}
205206

207+
@Override // Domain
208+
public boolean bridge(ExecutableElement e) {
209+
e = unwrap(e);
210+
try (var lock = lock()) {
211+
return this.elements().isBridge(e);
212+
}
213+
}
214+
206215
@Override // Domain
207216
public UniversalType capture(TypeMirror t) {
208217
t = unwrap(t);
@@ -368,6 +377,14 @@ public UniversalType nullType() {
368377
return UniversalType.of(this.types().getNullType(), this);
369378
}
370379

380+
@Override // Domain
381+
public Origin origin(Element e) {
382+
e = unwrap(e);
383+
try (var lock = lock()) {
384+
return this.elements().getOrigin(e);
385+
}
386+
}
387+
371388
// (Canonical.)
372389
@Override // Domain
373390
public UniversalElement packageElement(final CharSequence canonicalName) {

src/main/java/org/microbean/construct/Domain.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
import javax.lang.model.type.TypeVariable;
4141
import javax.lang.model.type.WildcardType;
4242

43+
import javax.lang.model.util.Elements;
44+
import javax.lang.model.util.Elements.Origin;
45+
4346
import org.microbean.construct.element.StringName;
4447
import org.microbean.construct.element.UniversalElement;
4548

@@ -150,13 +153,34 @@ public interface Domain {
150153
*
151154
* @return a non-{@code null} {@link Name}
152155
*
156+
* @exception NullPointerException if {@code e} is {@code null}
157+
*
153158
* @see javax.lang.model.util.Elements#getBinaryName(TypeElement)
154159
*
155160
* @spec https://docs.oracle.com/javase/specs/jls/se21/html/jls-13.html#jls-13.1 Java Language Specification, section
156161
* 13.1
157162
*/
158163
public Name binaryName(final TypeElement e);
159164

165+
/**
166+
* Returns {@code true} if and only if the supplied {@link ExecutableElement} represents a <dfn>bridge method</dfn>.
167+
*
168+
* @param e an {@link ExecutableElement}; must not be {@code null}
169+
*
170+
* @return {@code true} if and only if the supplied {@link ExecutableElement} represents a bridge method
171+
*
172+
* @exception NullPointerException if {@code e} is {@code null}
173+
*
174+
* @see javax.lang.model.util.Elements#isBridge(ExecutableElement)
175+
*
176+
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.8.3 Java Language Specification,
177+
* section 8.4.8.3
178+
*
179+
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-15.html#jls-15.12.4.5 Java Language Specification,
180+
* section 15.12.4.5
181+
*/
182+
public boolean bridge(final ExecutableElement e);
183+
160184
/**
161185
* Applies <a href="https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.10"><dfn>capture
162186
* conversion</dfn></a> to the supplied {@link TypeMirror}, which is normally a {@linkplain TypeKind#WILDCARD wildcard
@@ -687,6 +711,17 @@ public default TypeElement javaLangObject() {
687711
*/
688712
public NullType nullType();
689713

714+
/**
715+
* Returns the {@linkplain Origin origin} of the supplied {@link Element}.
716+
*
717+
* @param e a non-{@code null} {@link Element}
718+
*
719+
* @return a non-{@code null} {@link Origin}
720+
*
721+
* @see Elements#getOrigin(Element)
722+
*/
723+
public Origin origin(final Element e);
724+
690725
/**
691726
* Returns a {@link PackageElement} representing the package bearing the supplied {@code canonicalName}, <strong>or
692727
* {@code null} if there is no such {@link PackageElement}</strong>.

src/main/java/org/microbean/construct/UniversalConstruct.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ protected UniversalConstruct(final T delegate, final Domain domain) {
9595
final T unwrappedDelegate = unwrap(Objects.requireNonNull(delegate, "delegate"));
9696
final Runnable symbolCompleter = switch (unwrappedDelegate) {
9797
case null -> throw new IllegalArgumentException("delegate: " + delegate);
98-
case Element e -> e::getKind;
98+
case Element e -> e::getModifiers;
9999
case TypeMirror t -> t::getKind;
100100
default -> UniversalConstruct::doNothing;
101101
};

src/main/java/org/microbean/construct/element/UniversalElement.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.util.Optional;
2121
import java.util.Set;
2222

23+
import java.util.function.Supplier;
24+
2325
import javax.lang.model.element.Element;
2426
import javax.lang.model.element.ElementKind;
2527
import javax.lang.model.element.ElementVisitor;
@@ -63,6 +65,9 @@ public final class UniversalElement
6365
TypeParameterElement,
6466
VariableElement {
6567

68+
// volatile not needed
69+
private Supplier<? extends List<? extends UniversalElement>> enclosedElementsSupplier;
70+
6671
/**
6772
* Creates a new {@link UniversalElement}.
6873
*
@@ -75,8 +80,17 @@ public final class UniversalElement
7580
*
7681
* @see #delegate()
7782
*/
83+
@SuppressWarnings("try")
7884
public UniversalElement(final Element delegate, final Domain domain) {
7985
super(delegate, domain);
86+
this.enclosedElementsSupplier = () -> {
87+
final List<? extends UniversalElement> ees;
88+
try (var lock = domain.lock()) {
89+
ees = this.wrap(this.delegate().getEnclosedElements());
90+
this.enclosedElementsSupplier = () -> ees;
91+
}
92+
return ees;
93+
};
8094
}
8195

8296
@Override // Element
@@ -190,9 +204,23 @@ public final List<? extends UniversalDirective> getDirectives() {
190204
};
191205
}
192206

207+
/*
208+
java.lang.AssertionError: Filling jrt:/java.base/java/lang/String$CaseInsensitiveComparator.class during DirectoryFileObject[/modules/java.base:java/lang/Integer$IntegerCache.class]
209+
at jdk.compiler/com.sun.tools.javac.util.Assert.error(Assert.java:162)
210+
at jdk.compiler/com.sun.tools.javac.code.ClassFinder.fillIn(ClassFinder.java:366)
211+
at jdk.compiler/com.sun.tools.javac.code.ClassFinder.complete(ClassFinder.java:302)
212+
at jdk.compiler/com.sun.tools.javac.code.Symbol.complete(Symbol.java:687)
213+
at jdk.compiler/com.sun.tools.javac.code.Symbol$ClassSymbol.complete(Symbol.java:1455)
214+
at jdk.compiler/com.sun.tools.javac.code.Symbol.apiComplete(Symbol.java:693)
215+
at jdk.compiler/com.sun.tools.javac.code.Symbol$TypeSymbol.getEnclosedElements(Symbol.java:864)
216+
at jdk.compiler/com.sun.tools.javac.code.Symbol$ClassSymbol.getEnclosedElements(Symbol.java:1420)
217+
at jdk.compiler/com.sun.tools.javac.code.Symbol$ClassSymbol.getEnclosedElements(Symbol.java:1264)
218+
at [email protected]/org.microbean.construct.element.UniversalElement.getEnclosedElements(UniversalElement.java:195)
219+
*/
220+
// See https://github.com/microbean/microbean-construct/issues/18
193221
@Override // Element
194222
public final List<? extends UniversalElement> getEnclosedElements() {
195-
return this.wrap(this.delegate().getEnclosedElements());
223+
return this.enclosedElementsSupplier.get();
196224
}
197225

198226
@Override // Element
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
2+
*
3+
* Copyright © 2025 microBean™.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
11+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
12+
* specific language governing permissions and limitations under the License.
13+
*/
14+
package org.microbean.construct.vm;
15+
16+
// Even though this is in the java.lang.reflect package no reflection takes place; it's just an enum
17+
import java.lang.reflect.AccessFlag;
18+
19+
import javax.lang.model.element.Element;
20+
import javax.lang.model.element.ElementKind;
21+
import javax.lang.model.element.ExecutableElement;
22+
import javax.lang.model.element.Modifier;
23+
import javax.lang.model.element.ModuleElement;
24+
25+
import javax.lang.model.util.Elements.Origin;
26+
27+
import org.microbean.construct.Domain;
28+
29+
/**
30+
* A utility class for working with access flags.
31+
*
32+
* @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
33+
*
34+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1 Java Virtual Machine
35+
* Specification, section 4.5
36+
*
37+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.6-200-A.1 Java Virtual Machine
38+
* Specification, section 4.6
39+
*
40+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.7 Java Virtual Machine Specification,
41+
* section 4.7
42+
*/
43+
public final class AccessFlags {
44+
45+
// Flags pulled from the ASM library (https://gitlab.ow2.org/asm/asm/-/blob/master/asm/src/main/java/org/objectweb/asm/Opcodes.java):
46+
47+
private static final int ASM_RECORD = 1 << 16; // 0x10000 // class // (see for example https://github.com/raphw/byte-buddy/blob/byte-buddy-1.14.5/byte-buddy-dep/src/main/java/net/bytebuddy/pool/TypePool.java#L2949)
48+
49+
private AccessFlags() {
50+
super();
51+
}
52+
53+
/**
54+
* Returns the <dfn>access flags</dfn> for the supplied {@link Element}, which is presumed to have come from the
55+
* supplied {@link Domain}.
56+
*
57+
* @param e an {@link Element}; must not be {@code null}
58+
*
59+
* @param domain a {@link Domain}; must not be {@code null}
60+
*
61+
* @return the access flags for the supplied {@link Element}
62+
*
63+
* @exception NullPointerException if either argument is {@code null}
64+
*
65+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1 Java Virtual Machine
66+
* Specification, section 4.5
67+
*
68+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.6-200-A.1 Java Virtual Machine
69+
* Specification, section 4.6
70+
*
71+
* @spec https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.7 Java Virtual Machine Specification,
72+
* section 4.7
73+
*/
74+
@SuppressWarnings("try")
75+
public static final int accessFlags(final Element e, final Domain domain) {
76+
int accessFlags = 0;
77+
ElementKind k;
78+
try (var lock = domain.lock()) {
79+
for (final Modifier m : e.getModifiers()) { // not sure this actually causes symbol completion; could be hoisted out of lock
80+
accessFlags |= accessFlagMask(m);
81+
}
82+
k = e.getKind();
83+
switch (k) {
84+
case METHOD:
85+
// Handle just bridge and varargs here; other stuff will happen later
86+
final ExecutableElement ee = (ExecutableElement)e;
87+
if (domain.bridge(ee)) {
88+
accessFlags |= AccessFlag.BRIDGE.mask();
89+
}
90+
if (ee.isVarArgs()) {
91+
accessFlags |= AccessFlag.VARARGS.mask();
92+
}
93+
break;
94+
case MODULE:
95+
// Handle just openness here; other stuff will happen later
96+
if (((ModuleElement)e).isOpen()) {
97+
accessFlags |= AccessFlag.OPEN.mask();
98+
}
99+
break;
100+
}
101+
accessFlags |= accessFlagMask(domain.origin(e));
102+
}
103+
accessFlags |= accessFlagMask(k);
104+
return accessFlags;
105+
}
106+
107+
private static final int accessFlagMask(final Modifier m) {
108+
return switch (m) {
109+
case null -> throw new NullPointerException("m");
110+
case ABSTRACT -> AccessFlag.ABSTRACT.mask();
111+
case FINAL -> AccessFlag.FINAL.mask();
112+
case NATIVE -> AccessFlag.NATIVE.mask();
113+
case PRIVATE -> AccessFlag.PRIVATE.mask();
114+
case PROTECTED -> AccessFlag.PROTECTED.mask();
115+
case PUBLIC -> AccessFlag.PUBLIC.mask();
116+
case STATIC -> AccessFlag.STATIC.mask();
117+
case STRICTFP -> AccessFlag.STRICT.mask();
118+
case SYNCHRONIZED -> AccessFlag.SYNCHRONIZED.mask();
119+
case TRANSIENT -> AccessFlag.TRANSIENT.mask();
120+
case VOLATILE -> AccessFlag.VOLATILE.mask();
121+
case DEFAULT, NON_SEALED, SEALED -> 0; // pass through
122+
};
123+
}
124+
125+
private static final int accessFlagMask(final ElementKind k) {
126+
return switch (k) {
127+
case null -> throw new NullPointerException("k");
128+
case ANNOTATION_TYPE -> AccessFlag.ANNOTATION.mask() | AccessFlag.INTERFACE.mask(); // see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.1-200-E.1
129+
case ENUM -> AccessFlag.ENUM.mask();
130+
case ENUM_CONSTANT -> AccessFlag.ENUM.mask(); // perhaps odd but see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.5-200-A.1
131+
case INTERFACE -> AccessFlag.INTERFACE.mask(); // AccessFlag.ABSTRACT.mask() needs to be in play too, but that's covered in accessFlagMask(Modifier)
132+
case MODULE -> AccessFlag.MODULE.mask();
133+
case RECORD -> ASM_RECORD; // Some bytecode libraries use this to divine "recordness"; AccessFlag doesn't expose it and the JVM spec doesn't define it
134+
case
135+
BINDING_VARIABLE,
136+
CLASS,
137+
CONSTRUCTOR,
138+
EXCEPTION_PARAMETER,
139+
FIELD,
140+
INSTANCE_INIT,
141+
LOCAL_VARIABLE,
142+
METHOD,
143+
OTHER,
144+
PACKAGE,
145+
PARAMETER,
146+
RECORD_COMPONENT,
147+
RESOURCE_VARIABLE,
148+
STATIC_INIT,
149+
TYPE_PARAMETER -> 0; // pass through
150+
};
151+
}
152+
153+
private static final int accessFlagMask(final Origin o) {
154+
return switch (o) {
155+
case null -> throw new NullPointerException("o");
156+
case EXPLICIT -> 0;
157+
case MANDATED -> AccessFlag.MANDATED.mask();
158+
case SYNTHETIC -> AccessFlag.SYNTHETIC.mask();
159+
};
160+
}
161+
162+
}

0 commit comments

Comments
 (0)