سؤال

أنا أستخدم مكتبة ASM Java لتحل محل بعض الانعكاس. أقوم بإنشاء جسم هذه الطريقة:

void set(Object object, int fieldIndex, Object value);

باستخدام هذه الطريقة التي تم إنشاؤها ، يمكنني تعيين الحقول على كائن في وقت التشغيل دون استخدام التفكير. إنه يعمل بشكل رائع. ومع ذلك ، وجدت أنه فشل في الحقول البدائية. هنا هو الجزء ذي الصلة من طريقة المجموعة الخاصة بي:

for (int i = 0, n = cachedFields.length; i < n; i++) {
    mv.visitLabel(labels[i]);
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
    mv.visitVarInsn(ALOAD, 1);
    mv.visitTypeInsn(CHECKCAST, targetClassName);
    mv.visitVarInsn(ALOAD, 3);
    Field field = cachedFields[i].field;
    Type fieldType = Type.getType(field.getType());
    mv.visitFieldInsn(PUTFIELD, targetClassName, field.getName(), fieldType.getDescriptor());
    mv.visitInsn(RETURN);
}

يقوم هذا الرمز بإنشاء ملصقات للحالة للاختيار. إنه يعمل بشكل رائع للكائنات ولكن بالنسبة للبدائل ، أحصل على هذا الخطأ:

نتوقع العثور على تعويم على المكدس

حسنًا ، هذا أمر منطقي ، أحتاج إلى القيام بفصل الصناديق بنفسي. لقد قمت بتنفيذ ما يلي:

for (int i = 0, n = cachedFields.length; i < n; i++) {
    mv.visitLabel(labels[i]);
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
    mv.visitVarInsn(ALOAD, 1);
    mv.visitTypeInsn(CHECKCAST, targetClassName);
    mv.visitVarInsn(ALOAD, 3);

    Field field = cachedFields[i].field;
    Type fieldType = Type.getType(field.getType());
    switch (fieldType.getSort()) {
    case Type.BOOLEAN:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        break;
    case Type.BYTE:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
        break;
    case Type.CHAR:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
        break;
    case Type.SHORT:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
        break;
    case Type.INT:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
        break;
    case Type.FLOAT:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
        break;
    case Type.LONG:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
        break;
    case Type.DOUBLE:
        mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
        break;
    case Type.ARRAY:
        mv.visitTypeInsn(CHECKCAST, fieldType.getDescriptor());
        break;
    case Type.OBJECT:
        mv.visitTypeInsn(CHECKCAST, fieldType.getInternalName());
        break;
    }

    mv.visitFieldInsn(PUTFIELD, targetClassName, field.getName(), fieldType.getDescriptor());
    mv.visitInsn(RETURN);
}

لقد تتبعت ، وهو بالتأكيد يذهب إلى "نوع CASE.FLOAT" للحقل المناسب ، ومع ذلك ، أحصل على هذا الخطأ:

توقع العثور على كائن/صفيف على المكدس

هذا هو المكان الذي عالق. من أجل حياتي ، لا يمكنني معرفة سبب عدم عمل إلغاء التفاح. يضع "Aload ، 3" المعلمة الثالثة لطريقة المجموعة على المكدس ، والتي يجب أن تكون تعويمًا. أيه أفكار؟

لقد وجدت أن مكتبة ASM-Commons لديها فئة GeneratorAdAdapter التي لديها طريقة unbox. ومع ذلك ، لا أريد حقًا تضمين جرة أخرى لشيء يجب أن يكون بسيطًا جدًا. نظرت إلى مصدر المولد فيرافير وهو يفعل شيئًا مشابهًا جدًا. حاولت تعديل الكود الخاص بي لاستخدام GeneratorAdAdapter ، فقط لمعرفة ما إذا كان يعمل ، لكن لم أجده من السهل تحويله على الإطلاق.

هل كانت مفيدة؟

المحلول

تبين أن unboxing أعلاه كان يعمل بشكل صحيح. كان لدي رمز كان يحصل على الحصول على الملاكمة قبل محاولة إعادته ككائن. خطأي لعدم اختبار أبسط!

في حالة حاجة شخص آخر ، إليك الكود المناسب للملاكمة:

Type fieldType = Type.getType(...);
switch (fieldType.getSort()) {
case Type.BOOLEAN:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
    break;
case Type.BYTE:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
    break;
case Type.CHAR:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
    break;
case Type.SHORT:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
    break;
case Type.INT:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
    break;
case Type.FLOAT:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
    break;
case Type.LONG:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
    break;
case Type.DOUBLE:
    mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
    break;
}

نصائح أخرى

استخدم GeneratorAdAdapter يجب أن يكون أكثر نظافة من MethodVisitor ولديه unbox () الذي يمنح الاتصال اليمين primitive.valueof () استدعاء طريقة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top