Skip to content

Commit

Permalink
[GR-57539] [GR-57538] Parse Stable and DontInline vm annotations, onl…
Browse files Browse the repository at this point in the history
…y apply vm annotations for privileged classes.

PullRequest: graal/18637
  • Loading branch information
gilles-duboscq committed Aug 27, 2024
2 parents 65f4e4f + efafec4 commit b704d68
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import static com.oracle.truffle.espresso.classfile.Constants.ACC_ABSTRACT;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_ANNOTATION;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_CALLER_SENSITIVE;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_DONT_INLINE;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_ENUM;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_FINAL;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_FINALIZER;
Expand All @@ -39,6 +40,7 @@
import static com.oracle.truffle.espresso.classfile.Constants.ACC_PROTECTED;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_PUBLIC;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_SCOPED;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_STABLE;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_STATIC;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_STRICT;
import static com.oracle.truffle.espresso.classfile.Constants.ACC_SUPER;
Expand Down Expand Up @@ -163,6 +165,7 @@ public final class ClassfileParser {
private final ClassfileStream stream;

private final ClassRegistry.ClassDefinitionInfo classDefinitionInfo;
private final boolean loaderIsBootOrPlatform;

private Symbol<Type> classType;

Expand All @@ -177,18 +180,19 @@ public final class ClassfileParser {

private ImmutableConstantPool pool;

private ClassfileParser(ClassLoadingEnv env, ClassfileStream stream, boolean verifiable, Symbol<Type> requestedClassType, ClassRegistry.ClassDefinitionInfo info) {
private ClassfileParser(ClassLoadingEnv env, ClassfileStream stream, boolean verifiable, boolean loaderIsBootOrPlatform, Symbol<Type> requestedClassType, ClassRegistry.ClassDefinitionInfo info) {
this.requestedClassType = requestedClassType;
this.env = env;
this.classfile = null;
this.stream = Objects.requireNonNull(stream);
this.verifiable = verifiable;
this.loaderIsBootOrPlatform = loaderIsBootOrPlatform;
this.classDefinitionInfo = info;
}

// Note: only used for reading the class name from class bytes
private ClassfileParser(ClassLoadingEnv env, ClassfileStream stream) {
this(env, stream, false, null, ClassRegistry.ClassDefinitionInfo.EMPTY);
this(env, stream, false, false, null, ClassRegistry.ClassDefinitionInfo.EMPTY);
}

void handleBadConstant(Tag tag, ClassfileStream s) {
Expand Down Expand Up @@ -222,15 +226,12 @@ void checkDynamicConstantSupport(Tag tag) {

public static ParserKlass parse(ClassLoadingEnv env, ClassfileStream stream, StaticObject loader, Symbol<Type> requestedClassName) {
boolean verifiable = MethodVerifier.needsVerify(env.getLanguage(), loader);
return parse(env, stream, verifiable, requestedClassName, ClassRegistry.ClassDefinitionInfo.EMPTY);
return parse(env, stream, verifiable, env.loaderIsBootOrPlatform(loader), requestedClassName, ClassRegistry.ClassDefinitionInfo.EMPTY);
}

public static ParserKlass parse(ClassLoadingEnv env, ClassfileStream stream, boolean verifiable, Symbol<Type> requestedClassName) {
return parse(env, stream, verifiable, requestedClassName, ClassRegistry.ClassDefinitionInfo.EMPTY);
}

public static ParserKlass parse(ClassLoadingEnv env, ClassfileStream stream, boolean verifiable, Symbol<Type> requestedClassType, ClassRegistry.ClassDefinitionInfo info) {
return new ClassfileParser(env, stream, verifiable, requestedClassType, info).parseClass();
public static ParserKlass parse(ClassLoadingEnv env, ClassfileStream stream, boolean verifiable, boolean loaderIsBootOrPlatform, Symbol<Type> requestedClassType,
ClassRegistry.ClassDefinitionInfo info) {
return new ClassfileParser(env, stream, verifiable, loaderIsBootOrPlatform, requestedClassType, info).parseClass();
}

private ParserKlass parseClass() {
Expand Down Expand Up @@ -460,11 +461,11 @@ private ParserKlass parseClassImpl() {
return new ParserKlass(pool, classDefinitionInfo.patchFlags(classFlags), thisKlassName, thisKlassType, superKlass, superInterfaces, methods, fields, attributes, thisKlassIndex);
}

public static Symbol<Symbol.Name> getClassName(ClassLoadingEnv env, byte[] bytes) {
public static Symbol<Name> getClassName(ClassLoadingEnv env, byte[] bytes) {
return new ClassfileParser(env, new ClassfileStream(bytes, null)).getClassName();
}

private Symbol<Symbol.Name> getClassName() {
private Symbol<Name> getClassName() {

readMagic();

Expand Down Expand Up @@ -631,6 +632,18 @@ boolean supports(int annotation) {
}
}

private enum AnnotationLocation {
Method,
Field,
Class
}

private record RuntimeVisibleAnnotationsAttribute(Attribute attribute, int flags) {
private RuntimeVisibleAnnotationsAttribute {
Objects.requireNonNull(attribute);
}
}

private class CommonAttributeParser {

final InfoType infoType;
Expand Down Expand Up @@ -695,6 +708,73 @@ Attribute parseCommonAttribute(Symbol<Name> attributeName, int attributeSize) {
}
return null;
}

RuntimeVisibleAnnotationsAttribute parseRuntimeVisibleAnnotations(int attributeSize, AnnotationLocation location) {
assert infoType.supports(RUNTIME_VISIBLE_ANNOTATIONS);
if (runtimeVisibleAnnotations != null) {
throw ConstantPool.classFormatError("Duplicate RuntimeVisibleAnnotations attribute");
}

// Check for special internal annotations
byte[] data = stream.readByteArray(attributeSize);
ClassfileStream subStream = new ClassfileStream(data, classfile);
int flags = 0;
if (loaderIsBootOrPlatform || classDefinitionInfo.forceAllowVMAnnotations()) {
flags = switch (location) {
case Method -> parseMethodVMAnnotations(subStream);
case Field -> parseFieldVMAnnotations(subStream);
case Class -> 0;
};
}

Attribute attribute = new Attribute(Name.RuntimeVisibleAnnotations, data);
runtimeVisibleAnnotations = attribute;
return new RuntimeVisibleAnnotationsAttribute(attribute, flags);
}

private int parseMethodVMAnnotations(ClassfileStream subStream) {
int flags = 0;
int count = subStream.readU2();
for (int j = 0; j < count; j++) {
int typeIndex = parseAnnotation(subStream);
Utf8Constant constant = pool.utf8At(typeIndex, "annotation type");
// Validation of the type is done at runtime by guest java code.
Symbol<Type> annotType = constant.value();
if (Type.java_lang_invoke_LambdaForm$Compiled.equals(annotType)) {
flags |= ACC_LAMBDA_FORM_COMPILED;
} else if (Type.java_lang_invoke_LambdaForm$Hidden.equals(annotType) ||
Type.jdk_internal_vm_annotation_Hidden.equals(annotType)) {
flags |= ACC_HIDDEN;
} else if (Type.sun_reflect_CallerSensitive.equals(annotType) ||
Type.jdk_internal_reflect_CallerSensitive.equals(annotType)) {
flags |= ACC_CALLER_SENSITIVE;
} else if (Type.java_lang_invoke_ForceInline.equals(annotType) ||
Type.jdk_internal_vm_annotation_ForceInline.equals(annotType)) {
flags |= ACC_FORCE_INLINE;
} else if (Type.java_lang_invoke_DontInline.equals(annotType) ||
Type.jdk_internal_vm_annotation_DontInline.equals(annotType)) {
flags |= ACC_DONT_INLINE;
} else if (Type.jdk_internal_misc_ScopedMemoryAccess$Scoped.equals(annotType)) {
flags |= ACC_SCOPED;
}
}
return flags;
}

private int parseFieldVMAnnotations(ClassfileStream subStream) {
int flags = 0;
int count = subStream.readU2();
for (int j = 0; j < count; j++) {
int typeIndex = parseAnnotation(subStream);
Utf8Constant constant = pool.utf8At(typeIndex, "annotation type");
// Validation of the type is done at runtime by guest java code.
Symbol<Type> annotType = constant.value();
if (Type.jdk_internal_vm_annotation_Stable.equals(annotType)) {
flags |= ACC_STABLE;
}
}
return flags;
}
}

private ParserMethod parseMethod(boolean isInterface) {
Expand Down Expand Up @@ -787,7 +867,6 @@ private ParserMethod parseMethod(boolean isInterface) {
CodeAttribute codeAttribute = null;
Attribute checkedExceptions = null;

Attribute runtimeVisibleAnnotations = null;
CommonAttributeParser commonAttributeParser = new CommonAttributeParser(InfoType.Method);

MethodParametersAttribute methodParameters = null;
Expand All @@ -814,47 +893,19 @@ private ParserMethod parseMethod(boolean isInterface) {
methodAttributes[i] = checkedExceptions = new Attribute(attributeName, null);
} else if (majorVersion >= JAVA_1_5_VERSION) {
if (attributeName.equals(Name.RuntimeVisibleAnnotations)) {
if (runtimeVisibleAnnotations != null) {
throw ConstantPool.classFormatError("Duplicate RuntimeVisibleAnnotations attribute");
}
// Check if java.lang.invoke.LambdaForm.Compiled is present here.
byte[] data = stream.readByteArray(attributeSize);
ClassfileStream subStream = new ClassfileStream(data, this.classfile);
int count = subStream.readU2();
for (int j = 0; j < count; j++) {
int typeIndex = parseAnnotation(subStream);
Utf8Constant constant = pool.utf8At(typeIndex, "annotation type");
// Validation of the type is done at runtime by guest java code.
Symbol<Type> annotType = constant.value();
if (Type.java_lang_invoke_LambdaForm$Compiled.equals(annotType)) {
methodFlags |= ACC_LAMBDA_FORM_COMPILED;
} else if (Type.java_lang_invoke_LambdaForm$Hidden.equals(annotType) ||
Type.jdk_internal_vm_annotation_Hidden.equals(annotType)) {
methodFlags |= ACC_HIDDEN;
} else if (Type.sun_reflect_CallerSensitive.equals(annotType) ||
Type.jdk_internal_reflect_CallerSensitive.equals(annotType)) {
methodFlags |= ACC_CALLER_SENSITIVE;
} else if (Type.java_lang_invoke_ForceInline.equals(annotType) ||
Type.jdk_internal_vm_annotation_ForceInline.equals(annotType)) {
methodFlags |= ACC_FORCE_INLINE;
} else if (Type.jdk_internal_misc_ScopedMemoryAccess$Scoped.equals(annotType)) {
methodFlags |= ACC_SCOPED;
}
}
methodAttributes[i] = runtimeVisibleAnnotations = new Attribute(attributeName, data);
RuntimeVisibleAnnotationsAttribute annotations = commonAttributeParser.parseRuntimeVisibleAnnotations(attributeSize, AnnotationLocation.Method);
methodFlags |= annotations.flags;
methodAttributes[i] = annotations.attribute;
} else if (attributeName.equals(Name.MethodParameters)) {
if (methodParameters != null) {
throw ConstantPool.classFormatError("Duplicate MethodParameters attribute");
}
methodAttributes[i] = methodParameters = parseMethodParameters(attributeName);
} else {
Attribute attr = commonAttributeParser.parseCommonAttribute(attributeName, attributeSize);
// stream.skip(attributeSize);
methodAttributes[i] = attr == null ? new Attribute(attributeName, stream.readByteArray(attributeSize)) : attr;

}
} else {
// stream.skip(attributeSize);
methodAttributes[i] = new Attribute(attributeName, stream.readByteArray(attributeSize));
}

Expand Down Expand Up @@ -1607,9 +1658,15 @@ private ParserField parseField(boolean isInterface) {
fieldFlags |= ACC_SYNTHETIC;
fieldAttributes[i] = new Attribute(attributeName, null);
} else if (majorVersion >= JAVA_1_5_VERSION) {
Attribute attr = commonAttributeParser.parseCommonAttribute(attributeName, attributeSize);
// stream.skip(attributeSize);
fieldAttributes[i] = attr == null ? new Attribute(attributeName, stream.readByteArray(attributeSize)) : attr;
if (attributeName.equals(Name.RuntimeVisibleAnnotations)) {
RuntimeVisibleAnnotationsAttribute annotations = commonAttributeParser.parseRuntimeVisibleAnnotations(attributeSize, AnnotationLocation.Field);
fieldFlags |= annotations.flags;
fieldAttributes[i] = annotations.attribute;
} else {
Attribute attr = commonAttributeParser.parseCommonAttribute(attributeName, attributeSize);
// stream.skip(attributeSize);
fieldAttributes[i] = attr == null ? new Attribute(attributeName, stream.readByteArray(attributeSize)) : attr;
}
} else {
// stream.skip(attributeSize);
fieldAttributes[i] = new Attribute(attributeName, stream.readByteArray(attributeSize));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ public final class Constants {
public static final int ACC_MODULE = 0x00008000;

// Not part of the spec, used internally by the VM.
// Methods
public static final int ACC_FINALIZER = 0x00010000;
public static final int ACC_FORCE_INLINE = 0x00020000;
public static final int ACC_LAMBDA_FORM_COMPILED = 0x00040000;
public static final int ACC_CALLER_SENSITIVE = 0x00080000;
public static final int ACC_HIDDEN = 0x00100000;
public static final int ACC_HIDDEN = 0x00100000; // also for fields
public static final int ACC_SCOPED = 0x00200000;
public static final int ACC_IS_HIDDEN_CLASS = 0x04000000;
public static final int ACC_DONT_INLINE = 0x00400000;
// Classes
public static final int ACC_IS_HIDDEN_CLASS = 0x04000000; // synchronized with JVM_ACC_IS_HIDDEN_CLASS
// Fields
public static final int ACC_STABLE = 0x00010000;

public static final int FIELD_ID_TYPE = 0x01000000;
public static final int FIELD_ID_OBFUSCATE = 0x02000000;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -904,10 +904,13 @@ public static void ensureInitialized() {
public static final Symbol<Type> java_lang_invoke_LambdaForm$Compiled = StaticSymbols.putType("Ljava/lang/invoke/LambdaForm$Compiled;");
public static final Symbol<Type> java_lang_invoke_LambdaForm$Hidden = StaticSymbols.putType("Ljava/lang/invoke/LambdaForm$Hidden;");
public static final Symbol<Type> jdk_internal_vm_annotation_Hidden = StaticSymbols.putType("Ljdk/internal/vm/annotation/Hidden;");
public static final Symbol<Type> jdk_internal_vm_annotation_Stable = StaticSymbols.putType("Ljdk/internal/vm/annotation/Stable;");
public static final Symbol<Type> sun_reflect_CallerSensitive = StaticSymbols.putType("Lsun/reflect/CallerSensitive;");
public static final Symbol<Type> jdk_internal_reflect_CallerSensitive = StaticSymbols.putType("Ljdk/internal/reflect/CallerSensitive;");
public static final Symbol<Type> java_lang_invoke_ForceInline = StaticSymbols.putType("Ljava/lang/invoke/ForceInline;");
public static final Symbol<Type> jdk_internal_vm_annotation_ForceInline = StaticSymbols.putType("Ljdk/internal/vm/annotation/ForceInline;");
public static final Symbol<Type> java_lang_invoke_DontInline = StaticSymbols.putType("Ljava/lang/invoke/DontInline;");
public static final Symbol<Type> jdk_internal_vm_annotation_DontInline = StaticSymbols.putType("Ljdk/internal/vm/annotation/DontInline;");

// ScopedMemoryAccess
public static final Symbol<Type> jdk_internal_misc_ScopedMemoryAccess$Scoped = StaticSymbols.putType("Ljdk/internal/misc/ScopedMemoryAccess$Scoped;");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,6 @@ public Klass loadKlass(Symbol<Type> type, @JavaType(ClassLoader.class) StaticObj
return registry.loadKlass(context, type, protectionDomain);
}

@TruffleBoundary
public ObjectKlass defineKlass(Symbol<Type> type, byte[] bytes, StaticObject classLoader) throws EspressoClassLoadingException {
return defineKlass(type, bytes, classLoader, ClassRegistry.ClassDefinitionInfo.EMPTY);
}

@TruffleBoundary
public ObjectKlass defineKlass(Symbol<Type> type, byte[] bytes, StaticObject classLoader, ClassRegistry.ClassDefinitionInfo info) throws EspressoClassLoadingException {
assert classLoader != null;
Expand Down
Loading

0 comments on commit b704d68

Please sign in to comment.