001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Igor Bukanov
026: * Felix Meschberger
027: * Norris Boyd
028: * Ulrike Mueller <umueller@demandware.com>
029: *
030: * Alternatively, the contents of this file may be used under the terms of
031: * the GNU General Public License Version 2 or later (the "GPL"), in which
032: * case the provisions of the GPL are applicable instead of those above. If
033: * you wish to allow use of your version of this file only under the terms of
034: * the GPL and not to allow others to use your version of this file under the
035: * MPL, indicate your decision by deleting the provisions above and replacing
036: * them with the notice and other provisions required by the GPL. If you do
037: * not delete the provisions above, a recipient may use your version of this
038: * file under either the MPL or the GPL.
039: *
040: * ***** END LICENSE BLOCK ***** */
041:
042: package org.mozilla.javascript;
043:
044: import java.lang.reflect.*;
045: import java.io.*;
046:
047: /**
048: * Wrappper class for Method and Constructor instances to cache
049: * getParameterTypes() results, recover from IllegalAccessException
050: * in some cases and provide serialization support.
051: *
052: * @author Igor Bukanov
053: */
054:
055: final class MemberBox implements Serializable {
056: static final long serialVersionUID = 6358550398665688245L;
057:
058: private transient Member memberObject;
059: transient Class[] argTypes;
060: transient Object delegateTo;
061: transient boolean vararg;
062:
063: MemberBox(Method method) {
064: init(method);
065: }
066:
067: MemberBox(Constructor constructor) {
068: init(constructor);
069: }
070:
071: private void init(Method method) {
072: this .memberObject = method;
073: this .argTypes = method.getParameterTypes();
074: this .vararg = VMBridge.instance.isVarArgs(method);
075: }
076:
077: private void init(Constructor constructor) {
078: this .memberObject = constructor;
079: this .argTypes = constructor.getParameterTypes();
080: this .vararg = VMBridge.instance.isVarArgs(constructor);
081: }
082:
083: Method method() {
084: return (Method) memberObject;
085: }
086:
087: Constructor ctor() {
088: return (Constructor) memberObject;
089: }
090:
091: Member member() {
092: return memberObject;
093: }
094:
095: boolean isMethod() {
096: return memberObject instanceof Method;
097: }
098:
099: boolean isCtor() {
100: return memberObject instanceof Constructor;
101: }
102:
103: boolean isStatic() {
104: return Modifier.isStatic(memberObject.getModifiers());
105: }
106:
107: String getName() {
108: return memberObject.getName();
109: }
110:
111: Class getDeclaringClass() {
112: return memberObject.getDeclaringClass();
113: }
114:
115: String toJavaDeclaration() {
116: StringBuffer sb = new StringBuffer();
117: if (isMethod()) {
118: Method method = method();
119: sb.append(method.getReturnType());
120: sb.append(' ');
121: sb.append(method.getName());
122: } else {
123: Constructor ctor = ctor();
124: String name = ctor.getDeclaringClass().getName();
125: int lastDot = name.lastIndexOf('.');
126: if (lastDot >= 0) {
127: name = name.substring(lastDot + 1);
128: }
129: sb.append(name);
130: }
131: sb.append(JavaMembers.liveConnectSignature(argTypes));
132: return sb.toString();
133: }
134:
135: public String toString() {
136: return memberObject.toString();
137: }
138:
139: Object invoke(Object target, Object[] args) {
140: Method method = method();
141: try {
142: try {
143: return method.invoke(target, args);
144: } catch (IllegalAccessException ex) {
145: Method accessible = searchAccessibleMethod(method,
146: argTypes);
147: if (accessible != null) {
148: memberObject = accessible;
149: method = accessible;
150: } else {
151: if (!VMBridge.instance.tryToMakeAccessible(method)) {
152: throw Context.throwAsScriptRuntimeEx(ex);
153: }
154: }
155: // Retry after recovery
156: return method.invoke(target, args);
157: }
158: } catch (Exception ex) {
159: throw Context.throwAsScriptRuntimeEx(ex);
160: }
161: }
162:
163: Object newInstance(Object[] args) {
164: Constructor ctor = ctor();
165: try {
166: try {
167: return ctor.newInstance(args);
168: } catch (IllegalAccessException ex) {
169: if (!VMBridge.instance.tryToMakeAccessible(ctor)) {
170: throw Context.throwAsScriptRuntimeEx(ex);
171: }
172: }
173: return ctor.newInstance(args);
174: } catch (Exception ex) {
175: throw Context.throwAsScriptRuntimeEx(ex);
176: }
177: }
178:
179: private static Method searchAccessibleMethod(Method method,
180: Class[] params) {
181: int modifiers = method.getModifiers();
182: if (Modifier.isPublic(modifiers)
183: && !Modifier.isStatic(modifiers)) {
184: Class c = method.getDeclaringClass();
185: if (!Modifier.isPublic(c.getModifiers())) {
186: String name = method.getName();
187: Class[] intfs = c.getInterfaces();
188: for (int i = 0, N = intfs.length; i != N; ++i) {
189: Class intf = intfs[i];
190: if (Modifier.isPublic(intf.getModifiers())) {
191: try {
192: return intf.getMethod(name, params);
193: } catch (NoSuchMethodException ex) {
194: } catch (SecurityException ex) {
195: }
196: }
197: }
198: for (;;) {
199: c = c.getSuperclass();
200: if (c == null) {
201: break;
202: }
203: if (Modifier.isPublic(c.getModifiers())) {
204: try {
205: Method m = c.getMethod(name, params);
206: int mModifiers = m.getModifiers();
207: if (Modifier.isPublic(mModifiers)
208: && !Modifier.isStatic(mModifiers)) {
209: return m;
210: }
211: } catch (NoSuchMethodException ex) {
212: } catch (SecurityException ex) {
213: }
214: }
215: }
216: }
217: }
218: return null;
219: }
220:
221: private void readObject(ObjectInputStream in) throws IOException,
222: ClassNotFoundException {
223: in.defaultReadObject();
224: Member member = readMember(in);
225: if (member instanceof Method) {
226: init((Method) member);
227: } else {
228: init((Constructor) member);
229: }
230: }
231:
232: private void writeObject(ObjectOutputStream out) throws IOException {
233: out.defaultWriteObject();
234: writeMember(out, memberObject);
235: }
236:
237: /**
238: * Writes a Constructor or Method object.
239: *
240: * Methods and Constructors are not serializable, so we must serialize
241: * information about the class, the name, and the parameters and
242: * recreate upon deserialization.
243: */
244: private static void writeMember(ObjectOutputStream out,
245: Member member) throws IOException {
246: if (member == null) {
247: out.writeBoolean(false);
248: return;
249: }
250: out.writeBoolean(true);
251: if (!(member instanceof Method || member instanceof Constructor))
252: throw new IllegalArgumentException(
253: "not Method or Constructor");
254: out.writeBoolean(member instanceof Method);
255: out.writeObject(member.getName());
256: out.writeObject(member.getDeclaringClass());
257: if (member instanceof Method) {
258: writeParameters(out, ((Method) member).getParameterTypes());
259: } else {
260: writeParameters(out, ((Constructor) member)
261: .getParameterTypes());
262: }
263: }
264:
265: /**
266: * Reads a Method or a Constructor from the stream.
267: */
268: private static Member readMember(ObjectInputStream in)
269: throws IOException, ClassNotFoundException {
270: if (!in.readBoolean())
271: return null;
272: boolean isMethod = in.readBoolean();
273: String name = (String) in.readObject();
274: Class declaring = (Class) in.readObject();
275: Class[] parms = readParameters(in);
276: try {
277: if (isMethod) {
278: return declaring.getMethod(name, parms);
279: } else {
280: return declaring.getConstructor(parms);
281: }
282: } catch (NoSuchMethodException e) {
283: throw new IOException("Cannot find member: " + e);
284: }
285: }
286:
287: private static final Class[] primitives = { Boolean.TYPE,
288: Byte.TYPE, Character.TYPE, Double.TYPE, Float.TYPE,
289: Integer.TYPE, Long.TYPE, Short.TYPE, Void.TYPE };
290:
291: /**
292: * Writes an array of parameter types to the stream.
293: *
294: * Requires special handling because primitive types cannot be
295: * found upon deserialization by the default Java implementation.
296: */
297: private static void writeParameters(ObjectOutputStream out,
298: Class[] parms) throws IOException {
299: out.writeShort(parms.length);
300: outer: for (int i = 0; i < parms.length; i++) {
301: Class parm = parms[i];
302: boolean primitive = parm.isPrimitive();
303: out.writeBoolean(primitive);
304: if (!primitive) {
305: out.writeObject(parm);
306: continue;
307: }
308: for (int j = 0; j < primitives.length; j++) {
309: if (parm.equals(primitives[j])) {
310: out.writeByte(j);
311: continue outer;
312: }
313: }
314: throw new IllegalArgumentException("Primitive " + parm
315: + " not found");
316: }
317: }
318:
319: /**
320: * Reads an array of parameter types from the stream.
321: */
322: private static Class[] readParameters(ObjectInputStream in)
323: throws IOException, ClassNotFoundException {
324: Class[] result = new Class[in.readShort()];
325: for (int i = 0; i < result.length; i++) {
326: if (!in.readBoolean()) {
327: result[i] = (Class) in.readObject();
328: continue;
329: }
330: result[i] = primitives[in.readByte()];
331: }
332: return result;
333: }
334: }
|