Search in sources :

Example 1 with ParserMethod

use of com.oracle.truffle.espresso.impl.ParserMethod 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);
}
Also used : ClassfileStream(com.oracle.truffle.espresso.classfile.ClassfileStream) Matcher(java.util.regex.Matcher) ParserMethod(com.oracle.truffle.espresso.impl.ParserMethod) NameAndTypeConstant(com.oracle.truffle.espresso.classfile.constantpool.NameAndTypeConstant) ParserField(com.oracle.truffle.espresso.impl.ParserField) Type(com.oracle.truffle.espresso.descriptors.Symbol.Type) ConstantPool(com.oracle.truffle.espresso.classfile.ConstantPool) ParserKlass(com.oracle.truffle.espresso.impl.ParserKlass) EnclosingMethodAttribute(com.oracle.truffle.espresso.classfile.attributes.EnclosingMethodAttribute)

Example 2 with ParserMethod

use of com.oracle.truffle.espresso.impl.ParserMethod 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;
}
Also used : HashMap(java.util.HashMap) Matcher(java.util.regex.Matcher) ArrayList(java.util.ArrayList) ObjectKlass(com.oracle.truffle.espresso.impl.ObjectKlass) RedefineAddedField(com.oracle.truffle.espresso.impl.RedefineAddedField) Field(com.oracle.truffle.espresso.impl.Field) ParserField(com.oracle.truffle.espresso.impl.ParserField) ParserKlass(com.oracle.truffle.espresso.impl.ParserKlass) ParserMethod(com.oracle.truffle.espresso.impl.ParserMethod) ParserMethod(com.oracle.truffle.espresso.impl.ParserMethod) Method(com.oracle.truffle.espresso.impl.Method) ParserField(com.oracle.truffle.espresso.impl.ParserField) Klass(com.oracle.truffle.espresso.impl.Klass) ObjectKlass(com.oracle.truffle.espresso.impl.ObjectKlass) ParserKlass(com.oracle.truffle.espresso.impl.ParserKlass) HashMap(java.util.HashMap) EconomicMap(org.graalvm.collections.EconomicMap) Map(java.util.Map)

Example 3 with ParserMethod

use of com.oracle.truffle.espresso.impl.ParserMethod in project graal by oracle.

the class ClassfileParser method parseMethods.

private ParserMethod[] parseMethods(boolean isInterface) {
    int methodCount = stream.readU2();
    if (methodCount == 0) {
        return ParserMethod.EMPTY_ARRAY;
    }
    ParserMethod[] methods = new ParserMethod[methodCount];
    /*
         * Classes in general have lots of methods: use a hash set rather than array lookup for dup
         * check.
         */
    final HashSet<MethodKey> dup = new HashSet<>(methodCount);
    for (int i = 0; i < methodCount; ++i) {
        ParserMethod method;
        try (DebugCloseable closeable = PARSE_SINGLE_METHOD.scope(context.getTimers())) {
            method = parseMethod(isInterface);
        }
        methods[i] = method;
        try (DebugCloseable closeable = NO_DUP_CHECK.scope(context.getTimers())) {
            if (!dup.add(new MethodKey(method))) {
                throw ConstantPool.classFormatError("Duplicate method name and signature: " + method.getName() + " " + method.getSignature());
            }
        }
    }
    return methods;
}
Also used : ParserMethod(com.oracle.truffle.espresso.impl.ParserMethod) DebugCloseable(com.oracle.truffle.espresso.perf.DebugCloseable) HashSet(java.util.HashSet)

Example 4 with ParserMethod

use of com.oracle.truffle.espresso.impl.ParserMethod 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);
}
Also used : ParserMethod(com.oracle.truffle.espresso.impl.ParserMethod) BootstrapMethodsAttribute(com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute) EnclosingMethodAttribute(com.oracle.truffle.espresso.classfile.attributes.EnclosingMethodAttribute) StackMapTableAttribute(com.oracle.truffle.espresso.classfile.attributes.StackMapTableAttribute) NestHostAttribute(com.oracle.truffle.espresso.classfile.attributes.NestHostAttribute) SourceFileAttribute(com.oracle.truffle.espresso.classfile.attributes.SourceFileAttribute) SourceDebugExtensionAttribute(com.oracle.truffle.espresso.classfile.attributes.SourceDebugExtensionAttribute) PermittedSubclassesAttribute(com.oracle.truffle.espresso.classfile.attributes.PermittedSubclassesAttribute) LineNumberTableAttribute(com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute) ExceptionsAttribute(com.oracle.truffle.espresso.classfile.attributes.ExceptionsAttribute) CodeAttribute(com.oracle.truffle.espresso.classfile.attributes.CodeAttribute) NestMembersAttribute(com.oracle.truffle.espresso.classfile.attributes.NestMembersAttribute) ConstantValueAttribute(com.oracle.truffle.espresso.classfile.attributes.ConstantValueAttribute) MethodParametersAttribute(com.oracle.truffle.espresso.classfile.attributes.MethodParametersAttribute) Attribute(com.oracle.truffle.espresso.runtime.Attribute) InnerClassesAttribute(com.oracle.truffle.espresso.classfile.attributes.InnerClassesAttribute) RecordAttribute(com.oracle.truffle.espresso.classfile.attributes.RecordAttribute) SignatureAttribute(com.oracle.truffle.espresso.classfile.attributes.SignatureAttribute) Symbol(com.oracle.truffle.espresso.descriptors.Symbol) ParserField(com.oracle.truffle.espresso.impl.ParserField) Name(com.oracle.truffle.espresso.descriptors.Symbol.Name) Type(com.oracle.truffle.espresso.descriptors.Symbol.Type) DebugCloseable(com.oracle.truffle.espresso.perf.DebugCloseable) ParserKlass(com.oracle.truffle.espresso.impl.ParserKlass)

Aggregations

ParserMethod (com.oracle.truffle.espresso.impl.ParserMethod)4 ParserField (com.oracle.truffle.espresso.impl.ParserField)3 ParserKlass (com.oracle.truffle.espresso.impl.ParserKlass)3 EnclosingMethodAttribute (com.oracle.truffle.espresso.classfile.attributes.EnclosingMethodAttribute)2 Type (com.oracle.truffle.espresso.descriptors.Symbol.Type)2 DebugCloseable (com.oracle.truffle.espresso.perf.DebugCloseable)2 Matcher (java.util.regex.Matcher)2 ClassfileStream (com.oracle.truffle.espresso.classfile.ClassfileStream)1 ConstantPool (com.oracle.truffle.espresso.classfile.ConstantPool)1 BootstrapMethodsAttribute (com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute)1 CodeAttribute (com.oracle.truffle.espresso.classfile.attributes.CodeAttribute)1 ConstantValueAttribute (com.oracle.truffle.espresso.classfile.attributes.ConstantValueAttribute)1 ExceptionsAttribute (com.oracle.truffle.espresso.classfile.attributes.ExceptionsAttribute)1 InnerClassesAttribute (com.oracle.truffle.espresso.classfile.attributes.InnerClassesAttribute)1 LineNumberTableAttribute (com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute)1 MethodParametersAttribute (com.oracle.truffle.espresso.classfile.attributes.MethodParametersAttribute)1 NestHostAttribute (com.oracle.truffle.espresso.classfile.attributes.NestHostAttribute)1 NestMembersAttribute (com.oracle.truffle.espresso.classfile.attributes.NestMembersAttribute)1 PermittedSubclassesAttribute (com.oracle.truffle.espresso.classfile.attributes.PermittedSubclassesAttribute)1 RecordAttribute (com.oracle.truffle.espresso.classfile.attributes.RecordAttribute)1