James, > 3. I want to know how the library works internally. > > The third use-case is the only time literate programming makes sense, but > it's also the least used of the three. I'm also not hugely convinced it's > actually much use - whenever I read literate programs in Clojure I find > myself skipping down to the code, as it's actually easier for me to read > the code than the English text accompanying it!
I've attached a piece of code from Clojure, including the terse "documentation". I presume one of the early Clojure authors wrote it so I assume it is "best case, production code" by Java experts. I am currently writing a literate version of Clojure and I'm trying to explain this method. I can read the code and I've been programming in Java since it was called Oak but I must say I still find this quite opaque and hard to explain. I'm also familiar with the visitor pattern. This code has low-level, semantically void operations like "v + 2" operations on indices, similar in style to pointer operations in C. There is flag testing (e.g. dann != 0) on variables whose names are not exactly indicitive of the semantics. There are unexplained "options flags" which "can be used to modify the default behavior of the class". There is endless nested testing of conditions. If I have to add a new attrName what contracts do I need to fulfill? What invariants must I maintain? Should w be v+6 or anns be v+6? This is code Clojure programmers depend on to work. Are you suggesting that it is easier to read this code than a few paragraphs of natural language? I must say I really find it puzzling that there is so much resistance to writing words. It's not that hard. /** * Makes the given visitor visit the Java class of this * {@link ClassReader}. This class is the one specified in the * constructor (see {@link #ClassReader(byte[]) ClassReader}). * * @param classVisitor the visitor that must visit this class. * @param attrs prototypes of the attributes that must be parsed * during the visit of the class. Any attribute whose * type is not equal to the type of one the * prototypes will not be parsed: its byte array * value will be passed unchanged to the ClassWriter. * <i>This may corrupt it if this value contains * references to the constant pool, or has syntactic * or semantic links with a class element that has * been transformed by a class adapter between the * reader and the writer</i>. * @param flags option flags that can be used to modify the * default behavior of this class. See * {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. */ public void accept( final ClassVisitor classVisitor, final Attribute[] attrs, final int flags){ byte[] b = this.b; // the bytecode array char[] c = new char[maxStringLength]; // buffer used to read strings int i, j, k; // loop variables int u, v, w; // indexes in b Attribute attr; int access; String name; String desc; String attrName; String signature; int anns = 0; int ianns = 0; Attribute cattrs = null; // visits the header u = header; access = readUnsignedShort(u); name = readClass(u + 2, c); v = items[readUnsignedShort(u + 4)]; String superClassName = v == 0 ? null : readUTF8(v, c); String[] implementedItfs = new String[readUnsignedShort(u + 6)]; w = 0; u += 8; for(i = 0; i < implementedItfs.length; ++i) { implementedItfs[i] = readClass(u, c); u += 2; } boolean skipCode = (flags & SKIP_CODE) != 0; boolean skipDebug = (flags & SKIP_DEBUG) != 0; boolean unzip = (flags & EXPAND_FRAMES) != 0; // skips fields and methods v = u; i = readUnsignedShort(v); v += 2; for(; i > 0; --i) { j = readUnsignedShort(v + 6); v += 8; for(; j > 0; --j) { v += 6 + readInt(v + 2); } } i = readUnsignedShort(v); v += 2; for(; i > 0; --i) { j = readUnsignedShort(v + 6); v += 8; for(; j > 0; --j) { v += 6 + readInt(v + 2); } } // reads the class's attributes signature = null; String sourceFile = null; String sourceDebug = null; String enclosingOwner = null; String enclosingName = null; String enclosingDesc = null; i = readUnsignedShort(v); v += 2; for(; i > 0; --i) { attrName = readUTF8(v, c); // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if(attrName.equals("SourceFile")) { sourceFile = readUTF8(v + 6, c); } else if(attrName.equals("InnerClasses")) { w = v + 6; } else if(attrName.equals("EnclosingMethod")) { enclosingOwner = readClass(v + 6, c); int item = readUnsignedShort(v + 8); if(item != 0) { enclosingName = readUTF8(items[item], c); enclosingDesc = readUTF8(items[item] + 2, c); } } else if(attrName.equals("Signature")) { signature = readUTF8(v + 6, c); } else if(attrName.equals("RuntimeVisibleAnnotations")) { anns = v + 6; } else if(attrName.equals("Deprecated")) { access |= Opcodes.ACC_DEPRECATED; } else if(attrName.equals("Synthetic")) { access |= Opcodes.ACC_SYNTHETIC; } else if(attrName.equals("SourceDebugExtension")) { int len = readInt(v + 2); sourceDebug = readUTF(v + 6, len, new char[len]); } else if(attrName.equals("RuntimeInvisibleAnnotations")) { ianns = v + 6; } else { attr = readAttribute(attrs, attrName, v + 6, readInt(v + 2), c, -1, null); if(attr != null) { attr.next = cattrs; cattrs = attr; } } v += 6 + readInt(v + 2); } // calls the visit method classVisitor.visit(readInt(4), access, name, signature, superClassName, implementedItfs); // calls the visitSource method if(!skipDebug && (sourceFile != null || sourceDebug != null)) { classVisitor.visitSource(sourceFile, sourceDebug); } // calls the visitOuterClass method if(enclosingOwner != null) { classVisitor.visitOuterClass(enclosingOwner, enclosingName, enclosingDesc); } // visits the class annotations for(i = 1; i >= 0; --i) { v = i == 0 ? ianns : anns; if(v != 0) { j = readUnsignedShort(v); v += 2; for(; j > 0; --j) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); } } } // visits the class attributes while(cattrs != null) { attr = cattrs.next; cattrs.next = null; classVisitor.visitAttribute(cattrs); cattrs = attr; } // calls the visitInnerClass method if(w != 0) { i = readUnsignedShort(w); w += 2; for(; i > 0; --i) { classVisitor.visitInnerClass( readUnsignedShort(w) == 0 ? null : readClass(w, c), readUnsignedShort(w + 2) == 0 ? null : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 ? null : readUTF8(w + 4, c), readUnsignedShort(w + 6)); w += 8; } } // visits the fields i = readUnsignedShort(u); u += 2; for(; i > 0; --i) { access = readUnsignedShort(u); name = readUTF8(u + 2, c); desc = readUTF8(u + 4, c); // visits the field's attributes and looks for a ConstantValue // attribute int fieldValueItem = 0; signature = null; anns = 0; ianns = 0; cattrs = null; j = readUnsignedShort(u + 6); u += 8; for(; j > 0; --j) { attrName = readUTF8(u, c); // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if(attrName.equals("ConstantValue")) { fieldValueItem = readUnsignedShort(u + 6); } else if(attrName.equals("Signature")) { signature = readUTF8(u + 6, c); } else if(attrName.equals("Deprecated")) { access |= Opcodes.ACC_DEPRECATED; } else if(attrName.equals("Synthetic")) { access |= Opcodes.ACC_SYNTHETIC; } else if(attrName.equals("RuntimeVisibleAnnotations")) { anns = u + 6; } else if(attrName.equals("RuntimeInvisibleAnnotations")) { ianns = u + 6; } else { attr = readAttribute(attrs, attrName, u + 6, readInt(u + 2), c, -1, null); if(attr != null) { attr.next = cattrs; cattrs = attr; } } u += 6 + readInt(u + 2); } // visits the field FieldVisitor fv = classVisitor.visitField(access, name, desc, signature, fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); // visits the field annotations and attributes if(fv != null) { for(j = 1; j >= 0; --j) { v = j == 0 ? ianns : anns; if(v != 0) { k = readUnsignedShort(v); v += 2; for(; k > 0; --k) { v = readAnnotationValues(v + 2, c, true, fv.visitAnnotation(readUTF8(v, c), j != 0)); } } } while(cattrs != null) { attr = cattrs.next; cattrs.next = null; fv.visitAttribute(cattrs); cattrs = attr; } fv.visitEnd(); } } // visits the methods i = readUnsignedShort(u); u += 2; for(; i > 0; --i) { int u0 = u + 6; access = readUnsignedShort(u); name = readUTF8(u + 2, c); desc = readUTF8(u + 4, c); signature = null; anns = 0; ianns = 0; int dann = 0; int mpanns = 0; int impanns = 0; cattrs = null; v = 0; w = 0; // looks for Code and Exceptions attributes j = readUnsignedShort(u + 6); u += 8; for(; j > 0; --j) { attrName = readUTF8(u, c); int attrSize = readInt(u + 2); u += 6; // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if(attrName.equals("Code")) { if(!skipCode) { v = u; } } else if(attrName.equals("Exceptions")) { w = u; } else if(attrName.equals("Signature")) { signature = readUTF8(u, c); } else if(attrName.equals("Deprecated")) { access |= Opcodes.ACC_DEPRECATED; } else if(attrName.equals("RuntimeVisibleAnnotations")) { anns = u; } else if(attrName.equals("AnnotationDefault")) { dann = u; } else if(attrName.equals("Synthetic")) { access |= Opcodes.ACC_SYNTHETIC; } else if(attrName.equals("RuntimeInvisibleAnnotations")) { ianns = u; } else if(attrName.equals( "RuntimeVisibleParameterAnnotations")) { mpanns = u; } else if(attrName.equals( "RuntimeInvisibleParameterAnnotations")) { impanns = u; } else { attr = readAttribute(attrs, attrName, u, attrSize, c, -1, null); if(attr != null) { attr.next = cattrs; cattrs = attr; } } u += attrSize; } // reads declared exceptions String[] exceptions; if(w == 0) { exceptions = null; } else { exceptions = new String[readUnsignedShort(w)]; w += 2; for(j = 0; j < exceptions.length; ++j) { exceptions[j] = readClass(w, c); w += 2; } } // visits the method's code, if any MethodVisitor mv = classVisitor.visitMethod(access, name, desc, signature, exceptions); if(mv != null) { /* * if the returned MethodVisitor is in fact a MethodWriter, it * means there is no method adapter between the reader and the * writer. If, in addition, the writer's constant pool was * copied from this reader (mw.cw.cr == this), and the * signature and exceptions of the method have not been * changed, then it is possible to skip all visit events and * just copy the original code of the method to the writer * (the access, name and descriptor can have been changed, * this is not important since they are not copied as is from * the reader). */ if(mv instanceof MethodWriter) { MethodWriter mw = (MethodWriter) mv; if(mw.cw.cr == this) { if(signature == mw.signature) { boolean sameExceptions = false; if(exceptions == null) { sameExceptions = mw.exceptionCount == 0; } else { if(exceptions.length == mw.exceptionCount) { sameExceptions = true; for(j = exceptions.length - 1; j >= 0; --j) { w -= 2; if(mw.exceptions[j] != readUnsignedShort(w)) { sameExceptions = false; break; } } } } if(sameExceptions) { /* * we do not copy directly the code into * MethodWriter to save a byte array copy * operation. The real copy will be done in * ClassWriter.toByteArray(). */ mw.classReaderOffset = u0; mw.classReaderLength = u - u0; continue; } } } } if(dann != 0) { AnnotationVisitor dv = mv.visitAnnotationDefault(); readAnnotationValue(dann, c, null, dv); if(dv != null) { dv.visitEnd(); } } for(j = 1; j >= 0; --j) { w = j == 0 ? ianns : anns; if(w != 0) { k = readUnsignedShort(w); w += 2; for(; k > 0; --k) { w = readAnnotationValues(w + 2, c, true, mv.visitAnnotation(readUTF8(w, c), j != 0)); } } } if(mpanns != 0) { readParameterAnnotations(mpanns, c, true, mv); } if(impanns != 0) { readParameterAnnotations(impanns, c, false, mv); } while(cattrs != null) { attr = cattrs.next; cattrs.next = null; mv.visitAttribute(cattrs); cattrs = attr; } } if(mv != null && v != 0) { int maxStack = readUnsignedShort(v); int maxLocals = readUnsignedShort(v + 2); int codeLength = readInt(v + 4); v += 8; int codeStart = v; int codeEnd = v + codeLength; mv.visitCode(); // 1st phase: finds the labels int label; Label[] labels = new Label[codeLength + 1]; while(v < codeEnd) { int opcode = b[v] & 0xFF; switch(ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: case ClassWriter.IMPLVAR_INSN: v += 1; break; case ClassWriter.LABEL_INSN: label = v - codeStart + readShort(v + 1); if(labels[label] == null) { labels[label] = new Label(); } v += 3; break; case ClassWriter.LABELW_INSN: label = v - codeStart + readInt(v + 1); if(labels[label] == null) { labels[label] = new Label(); } v += 5; break; case ClassWriter.WIDE_INSN: opcode = b[v + 1] & 0xFF; if(opcode == Opcodes.IINC) { v += 6; } else { v += 4; } break; case ClassWriter.TABL_INSN: // skips 0 to 3 padding bytes w = v - codeStart; v = v + 4 - (w & 3); // reads instruction label = w + readInt(v); if(labels[label] == null) { labels[label] = new Label(); } j = readInt(v + 8) - readInt(v + 4) + 1; v += 12; for(; j > 0; --j) { label = w + readInt(v); v += 4; if(labels[label] == null) { labels[label] = new Label(); } } break; case ClassWriter.LOOK_INSN: // skips 0 to 3 padding bytes w = v - codeStart; v = v + 4 - (w & 3); // reads instruction label = w + readInt(v); if(labels[label] == null) { labels[label] = new Label(); } j = readInt(v + 4); v += 8; for(; j > 0; --j) { label = w + readInt(v + 4); v += 8; if(labels[label] == null) { labels[label] = new Label(); } } break; case ClassWriter.VAR_INSN: case ClassWriter.SBYTE_INSN: case ClassWriter.LDC_INSN: v += 2; break; case ClassWriter.SHORT_INSN: case ClassWriter.LDCW_INSN: case ClassWriter.FIELDORMETH_INSN: case ClassWriter.TYPE_INSN: case ClassWriter.IINC_INSN: v += 3; break; case ClassWriter.ITFMETH_INSN: v += 5; break; // case MANA_INSN: default: v += 4; break; } } // parses the try catch entries j = readUnsignedShort(v); v += 2; for(; j > 0; --j) { label = readUnsignedShort(v); Label start = labels[label]; if(start == null) { labels[label] = start = new Label(); } label = readUnsignedShort(v + 2); Label end = labels[label]; if(end == null) { labels[label] = end = new Label(); } label = readUnsignedShort(v + 4); Label handler = labels[label]; if(handler == null) { labels[label] = handler = new Label(); } int type = readUnsignedShort(v + 6); if(type == 0) { mv.visitTryCatchBlock(start, end, handler, null); } else { mv.visitTryCatchBlock(start, end, handler, readUTF8(items[type], c)); } v += 8; } // parses the local variable, line number tables, and code // attributes int varTable = 0; int varTypeTable = 0; int stackMap = 0; int frameCount = 0; int frameMode = 0; int frameOffset = 0; int frameLocalCount = 0; int frameLocalDiff = 0; int frameStackCount = 0; Object[] frameLocal = null; Object[] frameStack = null; boolean zip = true; cattrs = null; j = readUnsignedShort(v); v += 2; for(; j > 0; --j) { attrName = readUTF8(v, c); if(attrName.equals("LocalVariableTable")) { if(!skipDebug) { varTable = v + 6; k = readUnsignedShort(v + 6); w = v + 8; for(; k > 0; --k) { label = readUnsignedShort(w); if(labels[label] == null) { labels[label] = new Label(true); } label += readUnsignedShort(w + 2); if(labels[label] == null) { labels[label] = new Label(true); } w += 10; } } } else if(attrName.equals("LocalVariableTypeTable")) { varTypeTable = v + 6; } else if(attrName.equals("LineNumberTable")) { if(!skipDebug) { k = readUnsignedShort(v + 6); w = v + 8; for(; k > 0; --k) { label = readUnsignedShort(w); if(labels[label] == null) { labels[label] = new Label(true); } labels[label].line=readUnsignedShort(w + 2); w += 4; } } } else if(attrName.equals("StackMapTable")) { if((flags & SKIP_FRAMES) == 0) { stackMap = v + 8; frameCount = readUnsignedShort(v + 6); } /* * here we do not extract the labels corresponding to * the attribute content. This would require a full * parsing of the attribute, which would need to be * repeated in the second phase (see below). Instead the * content of the attribute is read one frame at a time * (i.e. after a frame has been visited, the next frame * is read), and the labels it contains are also * extracted one frame at a time. Thanks to the ordering * of frames, having only a "one frame lookahead" is not * a problem, i.e. it is not possible to see an offset * smaller than the offset of the current insn and for * which no Label exist. */ // TODO true for frame offsets, // but for UNINITIALIZED type offsets? } else if(attrName.equals("StackMap")) { if((flags & SKIP_FRAMES) == 0) { stackMap = v + 8; frameCount = readUnsignedShort(v + 6); zip = false; } /* * IMPORTANT! here we assume that the frames are * ordered, as in the StackMapTable attribute, * although this is not guaranteed by the * attribute format. */ } else { for(k = 0; k < attrs.length; ++k) { if(attrs[k].type.equals(attrName)) { attr = attrs[k].read(this, v + 6, readInt(v + 2), c, codeStart - 8, labels); if(attr != null) { attr.next = cattrs; cattrs = attr; } } } } v += 6 + readInt(v + 2); } // 2nd phase: visits each instruction if(stackMap != 0) { // creates the very first (implicit) frame from the // method descriptor frameLocal = new Object[maxLocals]; frameStack = new Object[maxStack]; if(unzip) { int local = 0; if((access & Opcodes.ACC_STATIC) == 0) { if(name.equals("<init>")) { frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; } else { frameLocal[local++] = readClass(header + 2, c); } } j = 1; loop: while(true) { k = j; switch(desc.charAt(j++)) { case'Z': case'C': case'B': case'S': case'I': frameLocal[local++] = Opcodes.INTEGER; break; case'F': frameLocal[local++] = Opcodes.FLOAT; break; case'J': frameLocal[local++] = Opcodes.LONG; break; case'D': frameLocal[local++] = Opcodes.DOUBLE; break; case'[': while(desc.charAt(j) == '[') { ++j; } if(desc.charAt(j) == 'L') { ++j; while(desc.charAt(j) != ';') { ++j; } } frameLocal[local++] = desc.substring(k, ++j); break; case'L': while(desc.charAt(j) != ';') { ++j; } frameLocal[local++] = desc.substring(k + 1, j++); break; default: break loop; } } frameLocalCount = local; } /* * for the first explicit frame the offset is not * offset_delta + 1 but only offset_delta; setting the * implicit frame offset to -1 allow the use of the * "offset_delta + 1" rule in all cases */ frameOffset = -1; } v = codeStart; Label l; while(v < codeEnd) { w = v - codeStart; l = labels[w]; if(l != null) { mv.visitLabel(l); if(!skipDebug && l.line > 0) { mv.visitLineNumber(l.line, l); } } while(frameLocal != null && (frameOffset == w || frameOffset == -1)) { // if there is a frame for this offset, // makes the visitor visit it, // and reads the next frame if there is one. if(!zip || unzip) { mv.visitFrame(Opcodes.F_NEW, frameLocalCount, frameLocal, frameStackCount, frameStack); } else if(frameOffset != -1) { mv.visitFrame(frameMode, frameLocalDiff, frameLocal, frameStackCount, frameStack); } if(frameCount > 0) { int tag, delta, n; if(zip) { tag = b[stackMap++] & 0xFF; } else { tag = MethodWriter.FULL_FRAME; frameOffset = -1; } frameLocalDiff = 0; if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { delta = tag; frameMode = Opcodes.F_SAME; frameStackCount = 0; } else if(tag < MethodWriter.RESERVED) { delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; stackMap = readFrameType(frameStack, 0, stackMap, c, labels); frameMode = Opcodes.F_SAME1; frameStackCount = 1; } else { delta = readUnsignedShort(stackMap); stackMap += 2; if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { stackMap = readFrameType(frameStack, 0, stackMap, c, labels); frameMode = Opcodes.F_SAME1; frameStackCount = 1; } else if(tag >= MethodWriter.CHOP_FRAME && tag < MethodWriter.SAME_FRAME_EXTENDED) { frameMode = Opcodes.F_CHOP; frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; frameLocalCount -= frameLocalDiff; frameStackCount = 0; } else if(tag == MethodWriter.SAME_FRAME_EXTENDED) { frameMode = Opcodes.F_SAME; frameStackCount = 0; } else if(tag < MethodWriter.FULL_FRAME) { j = unzip ? frameLocalCount : 0; for(k = tag - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) { stackMap = readFrameType(frameLocal, j++, stackMap, c, labels); } frameMode = Opcodes.F_APPEND; frameLocalDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; frameLocalCount += frameLocalDiff; frameStackCount = 0; } else { // if (tag == FULL_FRAME) { frameMode = Opcodes.F_FULL; n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); stackMap += 2; for(j = 0; n > 0; n--) { stackMap = readFrameType(frameLocal, j++, stackMap, c, labels); } n = frameStackCount = readUnsignedShort(stackMap); stackMap += 2; for(j = 0; n > 0; n--) { stackMap = readFrameType(frameStack, j++, stackMap, c, labels); } } } frameOffset += delta + 1; if(labels[frameOffset] == null) { labels[frameOffset] = new Label(); } --frameCount; } else { frameLocal = null; } } int opcode = b[v] & 0xFF; switch(ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: mv.visitInsn(opcode); v += 1; break; case ClassWriter.IMPLVAR_INSN: if(opcode > Opcodes.ISTORE) { opcode -= 59; // ISTORE_0 mv.visitVarInsn( Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); } else { opcode -= 26; // ILOAD_0 mv.visitVarInsn( Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); } v += 1; break; case ClassWriter.LABEL_INSN: mv.visitJumpInsn(opcode, labels[w + readShort(v + 1)]); v += 3; break; case ClassWriter.LABELW_INSN: mv.visitJumpInsn(opcode - 33, labels[w + readInt(v + 1)]); v += 5; break; case ClassWriter.WIDE_INSN: opcode = b[v + 1] & 0xFF; if(opcode == Opcodes.IINC) { mv.visitIincInsn(readUnsignedShort(v + 2), readShort(v + 4)); v += 6; } else { mv.visitVarInsn(opcode, readUnsignedShort(v + 2)); v += 4; } break; case ClassWriter.TABL_INSN: // skips 0 to 3 padding bytes v = v + 4 - (w & 3); // reads instruction label = w + readInt(v); int min = readInt(v + 4); int max = readInt(v + 8); v += 12; Label[] table = new Label[max - min + 1]; for(j = 0; j < table.length; ++j) { table[j] = labels[w + readInt(v)]; v += 4; } mv.visitTableSwitchInsn(min, max, labels[label], table); break; case ClassWriter.LOOK_INSN: // skips 0 to 3 padding bytes v = v + 4 - (w & 3); // reads instruction label = w + readInt(v); j = readInt(v + 4); v += 8; int[] keys = new int[j]; Label[] values = new Label[j]; for(j = 0; j < keys.length; ++j) { keys[j] = readInt(v); values[j] = labels[w + readInt(v + 4)]; v += 8; } mv.visitLookupSwitchInsn(labels[label], keys, values); break; case ClassWriter.VAR_INSN: mv.visitVarInsn(opcode, b[v + 1] & 0xFF); v += 2; break; case ClassWriter.SBYTE_INSN: mv.visitIntInsn(opcode, b[v + 1]); v += 2; break; case ClassWriter.SHORT_INSN: mv.visitIntInsn(opcode, readShort(v + 1)); v += 3; break; case ClassWriter.LDC_INSN: mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); v += 2; break; case ClassWriter.LDCW_INSN: mv.visitLdcInsn( readConst(readUnsignedShort(v + 1), c)); v += 3; break; case ClassWriter.FIELDORMETH_INSN: case ClassWriter.ITFMETH_INSN: int cpIndex = items[readUnsignedShort(v + 1)]; String iowner = readClass(cpIndex, c); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); String idesc = readUTF8(cpIndex + 2, c); if(opcode < Opcodes.INVOKEVIRTUAL) { mv.visitFieldInsn(opcode,iowner,iname,idesc); } else { mv.visitMethodInsn(opcode,iowner,iname,idesc); } if(opcode == Opcodes.INVOKEINTERFACE) { v += 5; } else { v += 3; } break; case ClassWriter.TYPE_INSN: mv.visitTypeInsn(opcode, readClass(v + 1, c)); v += 3; break; case ClassWriter.IINC_INSN: mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); v += 3; break; // case MANA_INSN: default: mv.visitMultiANewArrayInsn(readClass(v + 1, c), b[v + 3] & 0xFF); v += 4; break; } } l = labels[codeEnd - codeStart]; if(l != null) { mv.visitLabel(l); } // visits the local variable tables if(!skipDebug && varTable != 0) { int[] typeTable = null; if(varTypeTable != 0) { k = readUnsignedShort(varTypeTable) * 3; w = varTypeTable + 2; typeTable = new int[k]; while(k > 0) { typeTable[--k]= w + 6; // signature typeTable[--k]= readUnsignedShort(w + 8); //index typeTable[--k]= readUnsignedShort(w); // start w += 10; } } k = readUnsignedShort(varTable); w = varTable + 2; for(; k > 0; --k) { int start = readUnsignedShort(w); int length = readUnsignedShort(w + 2); int index = readUnsignedShort(w + 8); String vsignature = null; if(typeTable != null) { for(int a = 0; a < typeTable.length; a += 3) { if(typeTable[a] == start && typeTable[a + 1] == index) { vsignature=readUTF8(typeTable[a + 2], c); break; } } } mv.visitLocalVariable(readUTF8(w + 4, c), readUTF8(w + 6, c), vsignature, labels[start], labels[start + length], index); w += 10; } } // visits the other attributes while(cattrs != null) { attr = cattrs.next; cattrs.next = null; mv.visitAttribute(cattrs); cattrs = attr; } // visits the max stack and max locals values mv.visitMaxs(maxStack, maxLocals); } if(mv != null) { mv.visitEnd(); } } // visits the end of the class classVisitor.visitEnd(); } -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.