@Override public void visitLineNumber(final int line, final Label start) { if (lineNumberTable == null) { lineNumberTable = new ByteVector(); } ++lineNumberTableLength; lineNumberTable.putShort(start.bytecodeOffset); lineNumberTable.putShort(line); }
@Override public final void visitSource(final String file, final String debug) { if (file != null) { sourceFileIndex = symbolTable.addConstantUtf8(file); } if (debug != null) { debugExtension = new ByteVector().encodeUtf8(debug, 0, Integer.MAX_VALUE); } }
/** * Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary. * * @param byteValue a byte. * @return this byte vector. */ public ByteVector putByte(final int byteValue) { int currentLength = length; if (currentLength + 1 > data.length) { enlarge(1); } data[currentLength++] = (byte) byteValue; length = currentLength; return this; }
/** * Puts this symbol table's constant_pool array in the given ByteVector, preceded by the * constant_pool_count value. * * @param output where the JVMS ClassFile's constant_pool array must be put. */ void putConstantPool(final ByteVector output) { output.putShort(constantPoolCount).putByteArray(constantPool.data, 0, constantPool.length); }
/** * Puts the type_path JVMS structure corresponding to the given TypePath into the given * ByteVector. * * @param typePath a TypePath instance, or {@literal null} for empty paths. * @param output where the type path must be put. */ static void put(final TypePath typePath, final ByteVector output) { if (typePath == null) { output.putByte(0); } else { int length = typePath.typePathContainer[typePath.typePathOffset] * 2 + 1; output.putByteArray(typePath.typePathContainer, typePath.typePathOffset, length); } } }
/** * Puts this symbol table's BootstrapMethods attribute in the given ByteVector. This includes the * 6 attribute header bytes and the num_bootstrap_methods value. * * @param output where the JVMS BootstrapMethods attribute must be put. */ void putBootstrapMethods(final ByteVector output) { if (bootstrapMethods != null) { output .putShort(addConstantUtf8(Constants.BOOTSTRAP_METHODS)) .putInt(bootstrapMethods.length + 2) .putShort(bootstrapMethodCount) .putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); } }
@Override public void visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label... labels) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. code.putByte(Opcodes.TABLESWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); dflt.put(code, lastBytecodeOffset, true); code.putInt(min).putInt(max); for (Label label : labels) { label.put(code, lastBytecodeOffset, true); } // If needed, update the maximum stack size and number of locals, and stack map frames. visitSwitchInsn(dflt, labels); }
@Override public void visitIincInsn(final int var, final int increment) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. if ((var > 255) || (increment > 127) || (increment < -128)) { code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment); } else { code.putByte(Opcodes.IINC).put11(var, increment); } // If needed, update the maximum stack size and number of locals, and stack map frames. if (currentBasicBlock != null && (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) { currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null); } if (compute != COMPUTE_NOTHING) { int currentMaxLocals = var + 1; if (currentMaxLocals > maxLocals) { maxLocals = currentMaxLocals; } } }
annotationWriter == null ? 0 : annotationWriter.computeAnnotationsSize(null) - 8; output.putShort(attributeNameIndex); output.putInt(attributeLength); output.putByte(annotableParameterCount); for (int i = 0; i < annotableParameterCount; ++i) { AnnotationWriter annotationWriter = annotationWriters[i]; annotationWriter = annotationWriter.previousAnnotation; output.putShort(numAnnotations); annotationWriter = firstAnnotation; while (annotationWriter != null) { output.putByteArray( annotationWriter.annotation.data, 0, annotationWriter.annotation.length); annotationWriter = annotationWriter.nextAnnotation;
@Override public void visitEnum(final String name, final String descriptor, final String value) { // Case of an element_value with an enum_const_value field. // See https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.16.1. ++numElementValuePairs; if (useNamedValues) { annotation.putShort(symbolTable.addConstantUtf8(name)); } annotation .put12('e', symbolTable.addConstantUtf8(descriptor)) .putShort(symbolTable.addConstantUtf8(value)); }
case METHOD_TYPE_PARAMETER: case METHOD_FORMAL_PARAMETER: output.putShort(targetTypeAndInfo >>> 16); break; case FIELD: case METHOD_RETURN: case METHOD_RECEIVER: output.putByte(targetTypeAndInfo >>> 24); break; case CAST: case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: case METHOD_REFERENCE_TYPE_ARGUMENT: output.putInt(targetTypeAndInfo); break; case CLASS_EXTENDS: case CONSTRUCTOR_REFERENCE: case METHOD_REFERENCE: output.put12(targetTypeAndInfo >>> 24, (targetTypeAndInfo & 0xFFFF00) >> 8); break; default:
@Override public void visitIntInsn(final int opcode, final int operand) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. if (opcode == Opcodes.SIPUSH) { code.put12(opcode, operand); } else { // BIPUSH or NEWARRAY code.put11(opcode, operand); } // If needed, update the maximum stack size and number of locals, and stack map frames. if (currentBasicBlock != null) { if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { currentBasicBlock.frame.execute(opcode, operand, null, null); } else if (opcode != Opcodes.NEWARRAY) { // The stack size delta is 1 for BIPUSH or SIPUSH, and 0 for NEWARRAY. int size = relativeStackSize + 1; if (size > maxRelativeStackSize) { maxRelativeStackSize = size; } relativeStackSize = size; } } }
ByteVector result = new ByteVector(size); result.putInt(0xCAFEBABE).putInt(version); symbolTable.putConstantPool(result); int mask = (version & 0xFFFF) < Opcodes.V1_5 ? Opcodes.ACC_SYNTHETIC : 0; result.putShort(accessFlags & ~mask).putShort(thisClass).putShort(superClass); result.putShort(interfaceCount); for (int i = 0; i < interfaceCount; ++i) { result.putShort(interfaces[i]); result.putShort(fieldsCount); fieldWriter = firstField; while (fieldWriter != null) { result.putShort(methodsCount); boolean hasFrames = false; boolean hasAsmInstructions = false; result.putShort(attributesCount); if (innerClasses != null) { result .putShort(symbolTable.addConstantUtf8(Constants.INNER_CLASSES)) .putInt(innerClasses.length + 2) .putShort(numberOfInnerClasses) .putByteArray(innerClasses.data, 0, innerClasses.length); .putShort(symbolTable.addConstantUtf8(Constants.ENCLOSING_METHOD)) .putInt(4) .putShort(enclosingClassIndex) .putShort(enclosingMethodIndex);
ByteVector output = new ByteVector(typePathLength); output.putByte(0); int typePathIndex = 0; while (typePathIndex < typePathLength) { char c = typePath.charAt(typePathIndex++); if (c == '[') { output.put11(ARRAY_ELEMENT, 0); } else if (c == '.') { output.put11(INNER_TYPE, 0); } else if (c == '*') { output.put11(WILDCARD_BOUND, 0); } else if (c >= '0' && c <= '9') { int typeArg = c - '0'; output.put11(TYPE_ARGUMENT, typeArg); } else { throw new IllegalArgumentException();
/** * Puts the given public API frame element type in {@link #stackMapTableEntries} , using the JVMS * verification_type_info format used in StackMapTable attributes. * * @param type a frame element type described using the same format as in {@link * MethodVisitor#visitFrame}, i.e. either {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, {@link * Opcodes#FLOAT}, {@link Opcodes#LONG}, {@link Opcodes#DOUBLE}, {@link Opcodes#NULL}, or * {@link Opcodes#UNINITIALIZED_THIS}, or the internal name of a class, or a Label designating * a NEW instruction (for uninitialized types). */ private void putFrameType(final Object type) { if (type instanceof Integer) { stackMapTableEntries.putByte(((Integer) type).intValue()); } else if (type instanceof String) { stackMapTableEntries .putByte(Frame.ITEM_OBJECT) .putShort(symbolTable.addConstantClass((String) type).index); } else { stackMapTableEntries .putByte(Frame.ITEM_UNINITIALIZED) .putShort(((Label) type).bytecodeOffset); } }
ByteVector typeAnnotation = new ByteVector(); typeAnnotation.putByte(typeRef >>> 24).putShort(start.length); for (int i = 0; i < start.length; ++i) { typeAnnotation .putShort(start[i].bytecodeOffset) .putShort(end[i].bytecodeOffset - start[i].bytecodeOffset) .putShort(index[i]); typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); if (visible) { return lastCodeRuntimeVisibleTypeAnnotation =
@Override public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { lastBytecodeOffset = code.length; // Add the instruction to the bytecode of the method. Symbol descSymbol = symbolTable.addConstantClass(descriptor); code.put12(Opcodes.MULTIANEWARRAY, descSymbol.index).putByte(numDimensions); // If needed, update the maximum stack size and number of locals, and stack map frames. if (currentBasicBlock != null) { if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { currentBasicBlock.frame.execute( Opcodes.MULTIANEWARRAY, numDimensions, descSymbol, symbolTable); } else { // No need to update maxRelativeStackSize (the stack size delta is always negative). relativeStackSize += 1 - numDimensions; } } }
/** * Adds a CONSTANT_Integer_info or CONSTANT_Float_info to the constant pool of this symbol table. * Does nothing if the constant pool already contains a similar item. * * @param tag one of {@link Symbol#CONSTANT_INTEGER_TAG} or {@link Symbol#CONSTANT_FLOAT_TAG}. * @param value an int or float. * @return a constant pool constant with the given tag and primitive values. */ private Symbol addConstantIntegerOrFloat(final int tag, final int value) { int hashCode = hash(tag, value); Entry entry = get(hashCode); while (entry != null) { if (entry.tag == tag && entry.hashCode == hashCode && entry.data == value) { return entry; } entry = entry.next; } constantPool.putByte(tag).putInt(value); return put(new Entry(constantPoolCount++, tag, value, hashCode)); }
/** * Puts a reference to this label in the bytecode of a method. If the bytecode offset of the label * is known, the relative bytecode offset between the label and the instruction referencing it is * computed and written directly. Otherwise, a null relative offset is written and a new forward * reference is declared for this label. * * @param code the bytecode of the method. This is where the reference is appended. * @param sourceInsnBytecodeOffset the bytecode offset of the instruction that contains the * reference to be appended. * @param wideReference whether the reference must be stored in 4 bytes (instead of 2 bytes). */ final void put( final ByteVector code, final int sourceInsnBytecodeOffset, final boolean wideReference) { if ((flags & FLAG_RESOLVED) == 0) { if (wideReference) { addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_WIDE, code.length); code.putInt(-1); } else { addForwardReference(sourceInsnBytecodeOffset, FORWARD_REFERENCE_TYPE_SHORT, code.length); code.putShort(-1); } } else { if (wideReference) { code.putInt(bytecodeOffset - sourceInsnBytecodeOffset); } else { code.putShort(bytecodeOffset - sourceInsnBytecodeOffset); } } }