use of com.oracle.truffle.espresso.impl.ParserField in project graal by oracle.
the class ClassInfo method create.
public static HotSwapClassInfo create(ObjectKlass klass, Symbol<Name> name, byte[] bytes, StaticObject definingLoader, EspressoContext context) {
Symbol<Type> type = context.getTypes().fromName(name);
ParserKlass parserKlass = ClassfileParser.parse(new ClassfileStream(bytes, null), definingLoader, type, context);
StringBuilder hierarchy = new StringBuilder();
StringBuilder methods = new StringBuilder();
StringBuilder fields = new StringBuilder();
StringBuilder enclosing = new StringBuilder();
Matcher matcher = InnerClassRedefiner.ANON_INNER_CLASS_PATTERN.matcher(name.toString());
if (matcher.matches()) {
// fingerprints are only relevant for inner classes
hierarchy.append(parserKlass.getSuperKlass().toString()).append(";");
for (Symbol<Type> itf : parserKlass.getSuperInterfaces()) {
hierarchy.append(itf.toString()).append(";");
}
for (ParserMethod method : parserKlass.getMethods()) {
methods.append(method.getName().toString()).append(";");
methods.append(method.getSignature().toString()).append(";");
}
for (ParserField field : parserKlass.getFields()) {
fields.append(field.getType().toString()).append(";");
fields.append(field.getName().toString()).append(";");
}
ConstantPool pool = parserKlass.getConstantPool();
EnclosingMethodAttribute attr = (EnclosingMethodAttribute) parserKlass.getAttribute(EnclosingMethodAttribute.NAME);
NameAndTypeConstant nmt = pool.nameAndTypeAt(attr.getMethodIndex());
enclosing.append(nmt.getName(pool)).append(";").append(nmt.getDescriptor(pool));
}
return new HotSwapClassInfo(klass, name, definingLoader, hierarchy.toString(), methods.toString(), fields.toString(), enclosing.toString(), new ArrayList<>(1), bytes);
}
use of com.oracle.truffle.espresso.impl.ParserField in project graal by oracle.
the class ClassRedefinition method detectClassChanges.
// detect all types of class changes, but return early when a change that require arbitrary
// changes
private static ClassChange detectClassChanges(ParserKlass newParserKlass, ObjectKlass oldKlass, DetectedChange collectedChanges, ParserKlass finalParserKlass) throws RedefintionNotSupportedException {
ClassChange result = ClassChange.NO_CHANGE;
ParserKlass oldParserKlass = oldKlass.getLinkedKlass().getParserKlass();
boolean isPatched = finalParserKlass != null;
// detect method changes (including constructors)
ParserMethod[] newParserMethods = newParserKlass.getMethods();
List<Method> oldMethods = new ArrayList<>(Arrays.asList(oldKlass.getDeclaredMethods()));
List<ParserMethod> newMethods = new ArrayList<>(Arrays.asList(newParserMethods));
Map<Method, ParserMethod> bodyChanges = new HashMap<>();
List<ParserMethod> newSpecialMethods = new ArrayList<>(1);
boolean constantPoolChanged = false;
if (!Arrays.equals(oldParserKlass.getConstantPool().getRawBytes(), newParserKlass.getConstantPool().getRawBytes())) {
constantPoolChanged = true;
}
Iterator<Method> oldIt = oldMethods.iterator();
Iterator<ParserMethod> newIt;
while (oldIt.hasNext()) {
Method oldMethod = oldIt.next();
ParserMethod oldParserMethod = oldMethod.getLinkedMethod().getParserMethod();
// verify that there is a new corresponding method
newIt = newMethods.iterator();
while (newIt.hasNext()) {
ParserMethod newMethod = newIt.next();
if (isSameMethod(oldParserMethod, newMethod)) {
// detect method changes
ClassChange change = detectMethodChanges(oldParserMethod, newMethod);
switch(change) {
case NO_CHANGE:
if (isPatched) {
checkForSpecialConstructor(collectedChanges, bodyChanges, newSpecialMethods, oldMethod, oldParserMethod, newMethod);
} else if (constantPoolChanged) {
if (isObsolete(oldParserMethod, newMethod, oldParserKlass.getConstantPool(), newParserKlass.getConstantPool())) {
result = ClassChange.CONSTANT_POOL_CHANGE;
collectedChanges.addMethodBodyChange(oldMethod, newMethod);
} else {
collectedChanges.addUnchangedMethod(oldMethod);
}
} else {
collectedChanges.addUnchangedMethod(oldMethod);
}
break;
case METHOD_BODY_CHANGE:
result = change;
if (isPatched) {
checkForSpecialConstructor(collectedChanges, bodyChanges, newSpecialMethods, oldMethod, oldParserMethod, newMethod);
} else {
collectedChanges.addMethodBodyChange(oldMethod, newMethod);
}
break;
default:
return change;
}
newIt.remove();
oldIt.remove();
break;
}
}
}
if (isPatched) {
ParserMethod[] finalMethods = finalParserKlass.getMethods();
// map found changed methods
for (Map.Entry<Method, ParserMethod> entry : bodyChanges.entrySet()) {
Method oldMethod = entry.getKey();
ParserMethod changed = entry.getValue();
for (int i = 0; i < newParserMethods.length; i++) {
if (newParserMethods[i] == changed) {
collectedChanges.addMethodBodyChange(oldMethod, finalMethods[i]);
break;
}
}
}
// map found new methods
newMethods.addAll(newSpecialMethods);
for (ParserMethod changed : newMethods) {
for (int i = 0; i < newParserMethods.length; i++) {
if (newParserMethods[i] == changed) {
collectedChanges.addNewMethod(finalMethods[i]);
break;
}
}
}
} else {
collectedChanges.addNewMethods(newMethods);
}
for (Method oldMethod : oldMethods) {
collectedChanges.addRemovedMethod(oldMethod.getMethodVersion());
}
if (!oldMethods.isEmpty()) {
result = ClassChange.REMOVE_METHOD;
} else if (!newMethods.isEmpty()) {
result = ClassChange.ADD_METHOD;
}
if (isPatched) {
result = ClassChange.CLASS_NAME_CHANGED;
}
// detect field changes
Field[] oldFields = oldKlass.getDeclaredFields();
ParserField[] newFields = newParserKlass.getFields();
ArrayList<Field> oldFieldsList = new ArrayList<>(Arrays.asList(oldFields));
ArrayList<ParserField> newFieldsList = new ArrayList<>(Arrays.asList(newFields));
Map<ParserField, Field> compatibleFields = new HashMap<>();
Iterator<Field> oldFieldsIt = oldFieldsList.iterator();
Iterator<ParserField> newFieldsIt;
while (oldFieldsIt.hasNext()) {
Field oldField = oldFieldsIt.next();
newFieldsIt = newFieldsList.iterator();
// search for a new corresponding field
while (newFieldsIt.hasNext()) {
ParserField newField = newFieldsIt.next();
// first look for a perfect match
if (isUnchangedField(oldField, newField, compatibleFields)) {
// A nested anonymous inner class may contain a field reference to the outer
// class instance. Since we match against the patched (inner class rename rules
// applied) if the current class was patched (renamed) the resulting outer
// field pointer will have a changed type. Hence we should mark it as a new
// field.
Matcher matcher = InnerClassRedefiner.ANON_INNER_CLASS_PATTERN.matcher(oldField.getType().toString());
if (isPatched && matcher.matches()) {
break;
}
oldFieldsIt.remove();
newFieldsIt.remove();
break;
}
}
}
if (!newFieldsList.isEmpty()) {
if (isPatched) {
ParserField[] finalFields = finalParserKlass.getFields();
// lookup the final new field based on the index in the parser field array
for (ParserField parserField : newFieldsList) {
for (int i = 0; i < newFields.length; i++) {
if (parserField == newFields[i]) {
collectedChanges.addNewField(finalFields[i]);
break;
}
}
}
} else {
collectedChanges.addNewFields(newFieldsList);
}
result = ClassChange.SCHEMA_CHANGE;
}
if (!oldFieldsList.isEmpty()) {
collectedChanges.addRemovedFields(oldFieldsList);
result = ClassChange.SCHEMA_CHANGE;
}
// detect class-level changes
if (newParserKlass.getFlags() != oldParserKlass.getFlags()) {
result = ClassChange.SCHEMA_CHANGE;
}
collectedChanges.addCompatibleFields(compatibleFields);
// detect changes to superclass and implemented interfaces
Klass superKlass = oldKlass.getSuperKlass();
if (!newParserKlass.getSuperKlass().equals(oldParserKlass.getSuperKlass())) {
result = ClassChange.CLASS_HIERARCHY_CHANGED;
superKlass = getLoadedKlass(newParserKlass.getSuperKlass(), oldKlass);
}
collectedChanges.addSuperKlass((ObjectKlass) superKlass);
ObjectKlass[] newSuperInterfaces = oldKlass.getSuperInterfaces();
if (!Arrays.equals(newParserKlass.getSuperInterfaces(), oldParserKlass.getSuperInterfaces())) {
result = ClassChange.CLASS_HIERARCHY_CHANGED;
newSuperInterfaces = new ObjectKlass[newParserKlass.getSuperInterfaces().length];
for (int i = 0; i < newParserKlass.getSuperInterfaces().length; i++) {
newSuperInterfaces[i] = (ObjectKlass) getLoadedKlass(newParserKlass.getSuperInterfaces()[i], oldKlass);
}
}
collectedChanges.addSuperInterfaces(newSuperInterfaces);
return result;
}
use of com.oracle.truffle.espresso.impl.ParserField in project graal by oracle.
the class ClassfileParser method parseField.
private ParserField parseField(boolean isInterface) {
int fieldFlags = stream.readU2();
int nameIndex = stream.readU2();
int typeIndex = stream.readU2();
pool.utf8At(nameIndex).validateFieldName();
final Symbol<Name> name = pool.symbolAt(nameIndex, "field name");
verifyFieldFlags(name, fieldFlags, isInterface);
final boolean isStatic = Modifier.isStatic(fieldFlags);
pool.utf8At(typeIndex).validateType(false);
Symbol<ModifiedUTF8> rawDescriptor = pool.symbolAt(typeIndex, "field descriptor");
final Symbol<Type> descriptor = Types.fromSymbol(rawDescriptor);
if (descriptor == null) {
throw ConstantPool.classFormatError("Invalid descriptor: " + rawDescriptor);
}
final int attributeCount = stream.readU2();
final Attribute[] fieldAttributes = spawnAttributesArray(attributeCount);
ConstantValueAttribute constantValue = null;
CommonAttributeParser commonAttributeParser = new CommonAttributeParser(InfoType.Field);
for (int i = 0; i < attributeCount; ++i) {
final int attributeNameIndex = stream.readU2();
final Symbol<Name> attributeName = pool.symbolAt(attributeNameIndex, "attribute name");
final int attributeSize = stream.readS4();
final int startPosition = stream.getPosition();
if (isStatic && attributeName.equals(Name.ConstantValue)) {
if (constantValue != null) {
throw ConstantPool.classFormatError("Duplicate ConstantValue attribute");
}
fieldAttributes[i] = constantValue = new ConstantValueAttribute(stream.readU2());
if (constantValue.getConstantValueIndex() == 0) {
throw ConstantPool.classFormatError("Invalid ConstantValue index");
}
} else if (attributeName.equals(Name.Synthetic)) {
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;
} else {
// stream.skip(attributeSize);
fieldAttributes[i] = new Attribute(attributeName, stream.readByteArray(attributeSize));
}
if (attributeSize != stream.getPosition() - startPosition) {
throw ConstantPool.classFormatError("Invalid attribute_length for " + attributeName + " attribute");
}
}
final JavaKind kind = Types.getJavaKind(descriptor);
if (kind == JavaKind.Void) {
throw ConstantPool.classFormatError("Fields cannot be of type void");
}
if (constantValue != null) {
Tag tag = pool.tagAt(constantValue.getConstantValueIndex());
boolean valid = false;
switch(kind) {
// fall through
case Boolean:
// fall through
case Byte:
// fall through
case Short:
// fall through
case Char:
case // fall through
Int:
valid = (tag == Tag.INTEGER);
break;
case Float:
valid = (tag == Tag.FLOAT);
break;
case Long:
valid = (tag == Tag.LONG);
break;
case Double:
valid = (tag == Tag.DOUBLE);
break;
case Object:
valid = (tag == Tag.STRING) && descriptor.equals(Type.java_lang_String);
break;
default:
{
throw ConstantPool.classFormatError("Cannot have ConstantValue for fields of type " + kind);
}
}
if (!valid) {
throw ConstantPool.classFormatError("ConstantValue attribute does not match field type");
}
}
return new ParserField(fieldFlags, name, descriptor, fieldAttributes);
}
use of com.oracle.truffle.espresso.impl.ParserField in project graal by oracle.
the class ClassfileParser method parseClassImpl.
private ParserKlass parseClassImpl() {
readMagic();
minorVersion = stream.readU2();
majorVersion = stream.readU2();
verifyVersion(majorVersion, minorVersion);
try (DebugCloseable closeable = CONSTANT_POOL.scope(context.getTimers())) {
this.pool = ConstantPool.parse(context.getLanguage(), stream, this, classDefinitionInfo.patches, context, majorVersion, minorVersion);
}
// JVM_ACC_MODULE is defined in JDK-9 and later.
if (majorVersion >= JAVA_9_VERSION) {
classFlags = stream.readU2() & (JVM_RECOGNIZED_CLASS_MODIFIERS | ACC_MODULE);
} else {
classFlags = stream.readU2() & (JVM_RECOGNIZED_CLASS_MODIFIERS);
}
if ((classFlags & ACC_INTERFACE) != 0 && majorVersion < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
classFlags |= ACC_ABSTRACT;
}
boolean isModule = (classFlags & ACC_MODULE) != 0;
if (isModule) {
throw ConstantPool.noClassDefFoundError(classType + " is not a class because access_flag ACC_MODULE is set");
}
if (badConstantSeen != null) {
// https://bugs.openjdk.java.net/browse/JDK-8175383
throw ConstantPool.classFormatError(String.format("Unknown constant tag %s [in class file %s]", badConstantSeen, classfile));
}
verifyClassFlags(classFlags, majorVersion);
// this class
int thisKlassIndex = stream.readU2();
Symbol<Name> thisKlassName = pool.classAt(thisKlassIndex).getName(pool);
final boolean isInterface = Modifier.isInterface(classFlags);
// TODO(peterssen): Verify class names.
Symbol<Type> thisKlassType = context.getTypes().fromName(thisKlassName);
if (Types.isPrimitive(thisKlassType) || Types.isArray(thisKlassType)) {
throw ConstantPool.classFormatError(".this_class cannot be array nor primitive " + classType);
}
// Update classType which could be null previously to reflect the name in the constant pool.
classType = thisKlassType;
// Checks if name in class file matches requested name
if (requestedClassType != null && !classDefinitionInfo.isHidden() && !requestedClassType.equals(classType)) {
throw ConstantPool.noClassDefFoundError(classType + " (wrong name: " + requestedClassType + ")");
}
Symbol<Type> superKlass = parseSuperKlass();
if (!Type.java_lang_Object.equals(classType) && superKlass == null) {
throw ConstantPool.classFormatError("Class " + classType + " must have a superclass");
}
if (isInterface && !Type.java_lang_Object.equals(superKlass)) {
throw ConstantPool.classFormatError("Interface " + classType + " must extend java.lang.Object");
}
Symbol<Type>[] superInterfaces;
try (DebugCloseable closeable = PARSE_INTERFACES.scope(context.getTimers())) {
superInterfaces = parseInterfaces();
}
final ParserField[] fields;
try (DebugCloseable closeable = PARSE_FIELD.scope(context.getTimers())) {
fields = parseFields(isInterface);
}
final ParserMethod[] methods;
try (DebugCloseable closeable = PARSE_METHODS.scope(context.getTimers())) {
methods = parseMethods(isInterface);
}
final Attribute[] attributes;
try (DebugCloseable closeable = PARSE_CLASSATTR.scope(context.getTimers())) {
attributes = parseClassAttributes();
}
// Ensure there are no trailing bytes
stream.checkEndOfFile();
if (classDefinitionInfo.isHidden()) {
assert requestedClassType != null;
int futureKlassID = context.getNewKlassId();
classDefinitionInfo.initKlassID(futureKlassID);
thisKlassName = context.getNames().getOrCreate(Types.hiddenClassName(requestedClassType, futureKlassID));
thisKlassType = context.getTypes().fromName(thisKlassName);
pool = pool.patchForHiddenClass(thisKlassIndex, thisKlassName);
}
return new ParserKlass(pool, classDefinitionInfo.patchFlags(classFlags), thisKlassName, thisKlassType, superKlass, superInterfaces, methods, fields, attributes, thisKlassIndex);
}
Aggregations