ee4c4debf9e1c6cc1ffffa017f0148e52ef430c1
[fedora-idea.git] / java / compiler / notNull / src / com / intellij / compiler / notNullVerification / NotNullVerifyingInstrumenter.java
blobee4c4debf9e1c6cc1ffffa017f0148e52ef430c1
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 boolean myIsNotNull = false;
79 public Label myThrowLabel;
80 private Label myStartGeneratedCodeLabel;
82 public AnnotationVisitor visitParameterAnnotation(
83 final int parameter,
84 final String anno,
85 final boolean visible) {
86 AnnotationVisitor av;
87 av = mv.visitParameterAnnotation(parameter,
88 anno,
89 visible);
90 if (isReferenceType(args[parameter]) &&
91 anno.equals("Lorg/jetbrains/annotations/NotNull;")) {
92 myNotNullParams.add(new Integer(parameter));
94 return av;
97 public AnnotationVisitor visitAnnotation(String anno,
98 boolean isRuntime) {
99 final AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
100 if (isReferenceType(returnType) &&
101 anno.equals("Lorg/jetbrains/annotations/NotNull;")) {
102 myIsNotNull = true;
105 return av;
108 public void visitCode() {
109 if (myNotNullParams.size() > 0) {
110 myStartGeneratedCodeLabel = new Label();
111 mv.visitLabel(myStartGeneratedCodeLabel);
113 for (int p = 0; p < myNotNullParams.size(); ++p) {
114 int var = ((access & Opcodes.ACC_STATIC) == 0) ? 1 : 0;
115 int param = ((Integer)myNotNullParams.get(p)).intValue();
116 for (int i = 0; i < param + startParameter; ++i) {
117 var += args[i].getSize();
119 mv.visitVarInsn(Opcodes.ALOAD, var);
121 Label end = new Label();
122 mv.visitJumpInsn(Opcodes.IFNONNULL, end);
124 generateThrow("java/lang/IllegalArgumentException",
125 "Argument " + param + " for @NotNull parameter of " + myClassName + "." + name + " must not be null", end);
129 public void visitLocalVariable(final String name, final String desc, final String signature, final Label start, final Label end,
130 final int index) {
131 final boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
132 final boolean isParameter = isStatic ? index < args.length : index <= args.length;
133 mv.visitLocalVariable(name, desc, signature, (isParameter && myStartGeneratedCodeLabel != null) ? myStartGeneratedCodeLabel : start, end, index);
136 public void visitInsn(int opcode) {
137 if (opcode == Opcodes.ARETURN && myIsNotNull) {
138 mv.visitInsn(Opcodes.DUP);
139 /*generateConditionalThrow("@NotNull method " + myClassName + "." + name + " must not return null",
140 "java/lang/IllegalStateException");*/
141 if (myThrowLabel == null) {
142 Label skipLabel = new Label();
143 mv.visitJumpInsn(Opcodes.IFNONNULL, skipLabel);
144 myThrowLabel = new Label();
145 mv.visitLabel(myThrowLabel);
146 generateThrow("java/lang/IllegalStateException", "@NotNull method " + myClassName + "." + name + " must not return null",
147 skipLabel);
149 else {
150 mv.visitJumpInsn(Opcodes.IFNULL, myThrowLabel);
154 mv.visitInsn(opcode);
157 private void generateThrow(final String exceptionClass, final String descr, final Label end) {
158 String exceptionParamClass = "(Ljava/lang/String;)V";
159 mv.visitTypeInsn(Opcodes.NEW, exceptionClass);
160 mv.visitInsn(Opcodes.DUP);
161 mv.visitLdcInsn(descr);
162 mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
163 exceptionClass,
164 CONSTRUCTOR_NAME,
165 exceptionParamClass);
166 mv.visitInsn(Opcodes.ATHROW);
167 mv.visitLabel(end);
169 myIsModification = true;
172 public void visitMaxs(final int maxStack, final int maxLocals) {
173 try {
174 super.visitMaxs(maxStack, maxLocals);
176 catch (ArrayIndexOutOfBoundsException e) {
177 throw new ArrayIndexOutOfBoundsException("maxs processing failed for method " + name + ": " + e.getMessage());
183 private int getStartParameterIndex(final String name) {
184 int result = 0;
185 if (CONSTRUCTOR_NAME.equals(name)) {
186 if (mySuperName.equals(ENUM_CLASS_NAME)) {
187 result += 2;
189 if (myIsNotStaticInner) {
190 result += 1;
193 return result;
196 private static boolean isReferenceType(final Type type) {
197 return type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY;