1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.bytecode.d_BCValidate |
4 | |
5 | Copyright 1998, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.services.bytecode; |
22 | |
23 | import java.lang.reflect.*; |
24 | import org.apache.derby.iapi.services.classfile.VMOpcode; |
25 | import org.apache.derby.iapi.services.sanity.SanityManager; |
26 | import java.util.Hashtable; |
27 | import org.apache.derby.iapi.services.loader.*; |
28 | import org.apache.derby.iapi.services.context.*; |
29 | |
30 | /** |
31 | * Validate BC calls. |
32 | * |
33 | * @author jamie |
34 | */ |
35 | class d_BCValidate |
36 | { |
37 | |
38 | private static final String[] csPackages = { |
39 | "java", |
40 | "org.apache.derby.exe.", |
41 | "org.apache.derby.iapi.", |
42 | "org.apache.derby.jdbc.", |
43 | "org.apache.derby.iapi.", |
44 | "org.apache.derby.impl.", |
45 | "org.apache.derby.authentication.", |
46 | "org.apache.derby.catalog.", |
47 | "org.apache.derby.iapi.db.", |
48 | "org.apache.derby.iapi.types.", |
49 | "org.apache.derby.iapi.types.", |
50 | "org.apache.derby.catalog.types.", |
51 | }; |
52 | |
53 | |
54 | private static final Class[] NO_PARAMS = new Class[0]; |
55 | |
56 | static void checkMethod(short opcode, Type dt, String methodName, String[] debugParameterTypes, Type rt) { |
57 | |
58 | |
59 | if (SanityManager.DEBUG) { |
60 | String reason = null; |
61 | try { |
62 | |
63 | String declaringClass = dt.javaName(); |
64 | if (declaringClass.startsWith("org.apache.derby.exe.")) |
65 | return; |
66 | |
67 | // only validate against Cloudscape engine or Java classes. Not user defined classes |
68 | int p; |
69 | for (p = 0; p < csPackages.length; p++) { |
70 | if (declaringClass.startsWith(csPackages[p])) |
71 | break; |
72 | } |
73 | if (p == csPackages.length) |
74 | return; |
75 | |
76 | Class[] params = NO_PARAMS; |
77 | |
78 | Class declaring = loadClass(declaringClass); |
79 | |
80 | if (debugParameterTypes != null) { |
81 | params = new Class[debugParameterTypes.length]; |
82 | for (int i = 0; i < debugParameterTypes.length; i++) { |
83 | params[i] = loadClass(debugParameterTypes[i]); |
84 | } |
85 | |
86 | } |
87 | |
88 | // If the class is not in the same class loader then it |
89 | // it must be a non-Derby class. In that case any method etc. |
90 | // being accessed must be public, so don't use the getDeclared |
91 | // methods. Default SecurityManager behaviour is to grant access to public members |
92 | // and members from classes loaded by the same class loader. Thus |
93 | // we try to fall into these categories to avoid having to grant |
94 | // permissions to derby jars for the function tests. |
95 | |
96 | ClassLoader declareLoader = declaring.getClassLoader(); |
97 | ClassLoader myLoader = d_BCValidate.class.getClassLoader(); |
98 | |
99 | boolean sameClassLoader = false; |
100 | if (declareLoader == myLoader) |
101 | sameClassLoader = true; |
102 | else if (declareLoader != null) |
103 | sameClassLoader = declareLoader.equals(myLoader); |
104 | |
105 | String actualReturnType; |
106 | |
107 | if (methodName.equals("<init>")) { |
108 | Constructor c; |
109 | |
110 | if (sameClassLoader) |
111 | { |
112 | c = declaring.getDeclaredConstructor(params); |
113 | } |
114 | else |
115 | { |
116 | c = declaring.getConstructor(params); |
117 | |
118 | // check this construct is declared by this |
119 | // class, has to be, right? But no harm checking. |
120 | if (!c.getDeclaringClass().equals(declaring)) |
121 | { |
122 | reason = "constructor " + c.toString() + " declared on " + c.getDeclaringClass() + " expected " + declaring; |
123 | } |
124 | } |
125 | |
126 | actualReturnType = "void"; |
127 | } else { |
128 | Method m; |
129 | |
130 | if (sameClassLoader) |
131 | { |
132 | m = declaring.getDeclaredMethod(methodName, params); |
133 | } |
134 | else |
135 | { |
136 | m = declaring.getMethod(methodName, params); |
137 | |
138 | // check this method is declared by this |
139 | // class? But no harm checking. |
140 | if (!m.getDeclaringClass().equals(declaring)) |
141 | { |
142 | reason = "method " + m.toString() + " declared on " + m.getDeclaringClass() + " expected " + declaring; |
143 | } |
144 | } |
145 | |
146 | actualReturnType = m.getReturnType().getName(); |
147 | } |
148 | |
149 | // do we already have a problem? |
150 | if (reason == null) |
151 | { |
152 | |
153 | Class requestedReturnType = loadClass(rt.javaName()); |
154 | |
155 | // check the return type |
156 | if (actualReturnType.equals(requestedReturnType.getName())) { |
157 | |
158 | // check the inteface match |
159 | if (opcode != VMOpcode.INVOKEINTERFACE) |
160 | return; |
161 | |
162 | if (declaring.isInterface()) |
163 | return; |
164 | |
165 | reason = "declaring class is not an interface"; |
166 | |
167 | } else { |
168 | reason = "return type is " + actualReturnType; |
169 | } |
170 | } |
171 | |
172 | |
173 | } catch (Exception e) { |
174 | reason = e.toString(); |
175 | e.printStackTrace(System.out); |
176 | } |
177 | |
178 | String sig = dt.javaName() + " >> " + rt.javaName() + " " + methodName + "("; |
179 | if (debugParameterTypes != null) { |
180 | for (int i = 0; i < debugParameterTypes.length; i++) { |
181 | if (i != 0) |
182 | sig = sig + ", "; |
183 | sig = sig + debugParameterTypes[i]; |
184 | } |
185 | } |
186 | sig = sig + ")"; |
187 | |
188 | String msg = "Invalid method " + sig + " because " + reason; |
189 | |
190 | System.out.println(msg); |
191 | SanityManager.THROWASSERT(msg); |
192 | } |
193 | } |
194 | |
195 | private static Hashtable primitives; |
196 | |
197 | static { |
198 | if (SanityManager.DEBUG) { |
199 | primitives = new Hashtable(); |
200 | primitives.put("boolean", Boolean.TYPE); |
201 | primitives.put("byte", Byte.TYPE); |
202 | primitives.put("char", Character.TYPE); |
203 | primitives.put("double", Double.TYPE); |
204 | primitives.put("float", Float.TYPE); |
205 | primitives.put("int", Integer.TYPE); |
206 | primitives.put("long", Long.TYPE); |
207 | primitives.put("short", Short.TYPE); |
208 | primitives.put("void", Void.TYPE); |
209 | } |
210 | |
211 | } |
212 | |
213 | |
214 | private static Class loadClass(String name) throws ClassNotFoundException { |
215 | |
216 | if (SanityManager.DEBUG) { |
217 | |
218 | Class c = (Class) primitives.get(name); |
219 | if (c != null) |
220 | return c; |
221 | |
222 | if (name.endsWith("[]")) { |
223 | Class baseClass = loadClass(name.substring(0, name.length() - 2)); |
224 | return Array.newInstance(baseClass, 0).getClass(); |
225 | } |
226 | |
227 | return Class.forName(name); |
228 | } |
229 | |
230 | return null; |
231 | } |
232 | } |