@Override public void visit( int version, int access, String name, String signature, String superName, String[] interfaces) { this.className = name; this.classEntry = db.getOrCreateClassEntry(className, superName); classEntry.setInterfaces(interfaces); forceInstrumentation |= classEntry.requiresInstrumentation(); // need atleast 1.5 for annotations to work if (version < Opcodes.V1_5) version = Opcodes.V1_5; // When Java allows adding interfaces in retransformation, we can mark the class with an // interface, which makes checking whether it's instrumented faster (with instanceof) // if(classEntry.requiresInstrumentation() && !contains(interfaces, SUSPENDABLE_NAME)) { // System.out.println("XX: Marking " + className + " as " + SUSPENDABLE_NAME); // interfaces = add(interfaces, SUSPENDABLE_NAME); // } super.visit(version, access, name, signature, superName, interfaces); }
@Override @SuppressWarnings("CallToThreadDumpStack") public void visitEnd() { classEntry.setRequiresInstrumentation(false); db.recordSuspendableMethods(className, classEntry); if (methods != null && !methods.isEmpty()) { if (alreadyInstrumented && !forceInstrumentation) { for (MethodNode mn : methods) mn.accept(makeOutMV(mn)); } else { if (!alreadyInstrumented) { super.visitAnnotation(ALREADY_INSTRUMENTED_NAME, true); classEntry.setInstrumented(true); } for (MethodNode mn : methods) { final MethodVisitor outMV = makeOutMV(mn); try { InstrumentMethod im = new InstrumentMethod(db, className, mn); if (db.isDebug()) db.log( LogLevel.INFO, "About to instrument method %s#%s%s", className, mn.name, mn.desc); if (im.collectCodeBlocks()) { if (mn.name.charAt(0) == '<') throw new UnableToInstrumentException( "special method", className, mn.name, mn.desc); im.accept(outMV, hasAnnotation(mn)); } else mn.accept(outMV); } catch (AnalyzerException ex) { ex.printStackTrace(); throw new InternalError(ex.getMessage()); } } } } else { // if we don't have any suspendable methods, but our superclass is instrumented, we mark this // class as instrumented, too. if (!alreadyInstrumented && classEntry.getSuperName() != null) { ClassEntry superClass = db.getClassEntry(classEntry.getSuperName()); if (superClass != null && superClass.isInstrumented()) { super.visitAnnotation(ALREADY_INSTRUMENTED_NAME, true); classEntry.setInstrumented(true); } } } super.visitEnd(); }
@Override public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { final SuspendableType markedSuspendable = SuspendableClassifierService.isSuspendable( className, classEntry, name, desc, signature, exceptions); final SuspendableType setSuspendable = classEntry.check(name, desc); if (setSuspendable == null) classEntry.set( name, desc, markedSuspendable != null ? markedSuspendable : SuspendableType.NON_SUSPENDABLE); final boolean suspendable = markedSuspendable == SuspendableType.SUSPENDABLE | setSuspendable == SuspendableType.SUSPENDABLE; if (checkAccess(access) && !isYieldMethod(className, name)) { if (methods == null) methods = new ArrayList<MethodNode>(); final MethodNode mn = new MethodNode(access, name, desc, signature, exceptions); if (suspendable) { if (db.isDebug()) db.log( LogLevel.INFO, "Method %s#%s suspendable: %s (markedSuspendable: %s setSuspendable: %s)", className, name, suspendable, markedSuspendable, setSuspendable); methods.add(mn); return mn; // this causes the mn to be initialized } else { // look for @Suspendable annotation return new MethodVisitor(Opcodes.ASM4, mn) { private boolean susp = false; private boolean commited = false; @Override public AnnotationVisitor visitAnnotation(String adesc, boolean visible) { if (adesc.equals(ANNOTATION_DESC)) susp = true; return super.visitAnnotation(adesc, visible); } @Override public void visitCode() { commit(); super.visitCode(); } @Override public void visitEnd() { commit(); super.visitEnd(); } private void commit() { if (commited) return; commited = true; if (db.isDebug()) db.log( LogLevel.INFO, "Method %s#%s suspendable: %s (markedSuspendable: %s setSuspendable: %s)", className, name, susp, susp, false); classEntry.set( name, desc, susp ? SuspendableType.SUSPENDABLE : SuspendableType.NON_SUSPENDABLE); if (susp) methods.add(mn); else { MethodVisitor _mv = makeOutMV(mn); _mv = new JSRInlinerAdapter(_mv, access, name, desc, signature, exceptions); mn.accept( new MethodVisitor(Opcodes.ASM4, _mv) { @Override public void visitEnd() { // don't call visitEnd on MV } }); // write method as-is this.mv = _mv; } } }; } } return super.visitMethod(access, name, desc, signature, exceptions); }