Make the NotNull instrumenter work with stock asm
[fedora-idea.git] / java / compiler / notNull / src / com / intellij / compiler / notNullVerification / NotNullVerifyingInstrumenter.java
blob0839f635753b1b8952ef636c029ea818074b5b58
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.compiler.notNullVerification;
18 import org.objectweb.asm.*;
20 import java.util.ArrayList;
22 /**
23 * @author ven
24 * @noinspection HardCodedStringLiteral
26 public class NotNullVerifyingInstrumenter extends ClassAdapter {
28 private boolean myIsModification = false;
29 private boolean myIsNotStaticInner = false;
30 private String myClassName;
31 private String mySuperName;
32 private static final String ENUM_CLASS_NAME = "java/lang/Enum";
33 private static final String CONSTRUCTOR_NAME = "<init>";
35 public NotNullVerifyingInstrumenter(final ClassVisitor classVisitor) {
36 super(classVisitor);
39 public boolean isModification() {
40 return myIsModification;
43 public void visit(final int version,
44 final int access,
45 final String name,
46 final String signature,
47 final String superName,
48 final String[] interfaces) {
49 super.visit(version, access, name, signature, superName, interfaces);
50 myClassName = name;
51 mySuperName = superName;
54 public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {
55 super.visitInnerClass(name, outerName, innerName, access);
56 if (myClassName.equals(name)) {
57 myIsNotStaticInner = (access & Opcodes.ACC_STATIC) == 0;
61 public MethodVisitor visitMethod(
62 final int access,
63 final String name,
64 final String desc,
65 final String signature,
66 final String[] exceptions) {
67 final Type[] args = Type.getArgumentTypes(desc);
68 final Type returnType = Type.getReturnType(desc);
69 final int startParameter = getStartParameterIndex(name);
70 MethodVisitor v = cv.visitMethod(access,
71 name,
72 desc,
73 signature,
74 exceptions);
75 return new MethodAdapter(v) {
77 private final ArrayList myNotNullParams = new ArrayList();
78 private int synthetics = 0;
79 private boolean myIsNotNull = false;
80 public Label myThrowLabel;
81 private Label myStartGeneratedCodeLabel;
83 public AnnotationVisitor visitParameterAnnotation(
84 final int parameter,
85 final String anno,
86 final boolean visible) {
87 AnnotationVisitor av;
88 av = mv.visitParameterAnnotation(parameter,
89 anno,
90 visible);
91 if (isReferenceType(args[parameter])) {
92 if (anno.equals("Lorg/jetbrains/annotations/NotNull;")) {
93 myNotNullParams.add(new Integer(parameter));
94 } else if (anno.equals("Ljava/lang/Synthetic;")) {
95 // See asm r1278 for what we do this,
96 // http://forge.objectweb.org/tracker/index.php?func=detail&aid=307392&group_id=23&atid=100023
97 synthetics++;
100 return av;
103 public AnnotationVisitor visitAnnotation(String anno,
104 boolean isRuntime) {
105 final AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
106 if (isReferenceType(returnType) &&
107 anno.equals("Lorg/jetbrains/annotations/NotNull;")) {
108 myIsNotNull = true;
111 return av;
114 public void visitCode() {
115 if (myNotNullParams.size() > 0) {
116 myStartGeneratedCodeLabel = new Label();
117 mv.visitLabel(myStartGeneratedCodeLabel);
119 for (int p = 0; p < myNotNullParams.size(); ++p) {
120 int var = ((access & Opcodes.ACC_STATIC) == 0) ? 1 : 0;
121 int param = ((Integer)myNotNullParams.get(p)).intValue() - synthetics;
122 for (int i = 0; i < param + startParameter; ++i) {
123 var += args[i].getSize();
125 mv.visitVarInsn(Opcodes.ALOAD, var);
127 Label end = new Label();
128 mv.visitJumpInsn(Opcodes.IFNONNULL, end);
130 generateThrow("java/lang/IllegalArgumentException",
131 "Argument " + param + " for @NotNull parameter of " + myClassName + "." + name + " must not be null", end);
135 public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end,
136 final int index) {
137 final boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
138 final boolean isParameter = isStatic ? index < args.length : index <= args.length;
139 mv.visitLocalVariable(name, desc, signature, (isParameter && myStartGeneratedCodeLabel != null) ? myStartGeneratedCodeLabel : start, end, index);
142 public void visitInsn(int opcode) {
143 if (opcode == Opcodes.ARETURN && myIsNotNull) {
144 mv.visitInsn(Opcodes.DUP);
145 /*generateConditionalThrow("@NotNull method " + myClassName + "." + name + " must not return null",
146 "java/lang/IllegalStateException");*/
147 if (myThrowLabel == null) {
148 Label skipLabel = new Label();
149 mv.visitJumpInsn(Opcodes.IFNONNULL, skipLabel);
150 myThrowLabel = new Label();
151 mv.visitLabel(myThrowLabel);
152 generateThrow("java/lang/IllegalStateException", "@NotNull method " + myClassName + "." + name + " must not return null",
153 skipLabel);
155 else {
156 mv.visitJumpInsn(Opcodes.IFNULL, myThrowLabel);
160 mv.visitInsn(opcode);
163 private void generateThrow(final String exceptionClass, final String descr, final Label end) {
164 String exceptionParamClass = "(Ljava/lang/String;)V";
165 mv.visitTypeInsn(Opcodes.NEW, exceptionClass);
166 mv.visitInsn(Opcodes.DUP);
167 mv.visitLdcInsn(descr);
168 mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
169 exceptionClass,
170 CONSTRUCTOR_NAME,
171 exceptionParamClass);
172 mv.visitInsn(Opcodes.ATHROW);
173 mv.visitLabel(end);
175 myIsModification = true;
178 public void visitMaxs(final int maxStack, final int maxLocals) {
179 try {
180 super.visitMaxs(maxStack, maxLocals);
182 catch (ArrayIndexOutOfBoundsException e) {
183 throw new ArrayIndexOutOfBoundsException("maxs processing failed for method " + name + ": " + e.getMessage());
189 private int getStartParameterIndex(final String name) {
190 int result = 0;
191 if (CONSTRUCTOR_NAME.equals(name)) {
192 if (mySuperName.equals(ENUM_CLASS_NAME)) {
193 result += 2;
195 if (myIsNotStaticInner) {
196 result += 1;
199 return result;
202 private static boolean isReferenceType(final Type type) {
203 return type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY;