org.mozilla.javascript.MemberBox Maven / Gradle / Ivy
The newest version!
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.javascript;
import java.lang.reflect.*;
import java.io.*;
/**
* Wrappper class for Method and Constructor instances to cache
* getParameterTypes() results, recover from IllegalAccessException
* in some cases and provide serialization support.
*
* @author Igor Bukanov
*/
final class MemberBox implements Serializable
{
static final long serialVersionUID = 6358550398665688245L;
private transient Member memberObject;
transient Class>[] argTypes;
transient Object delegateTo;
transient boolean vararg;
MemberBox(Method method)
{
init(method);
}
MemberBox(Constructor> constructor)
{
init(constructor);
}
private void init(Method method)
{
this.memberObject = method;
this.argTypes = method.getParameterTypes();
this.vararg = VMBridge.instance.isVarArgs(method);
}
private void init(Constructor> constructor)
{
this.memberObject = constructor;
this.argTypes = constructor.getParameterTypes();
this.vararg = VMBridge.instance.isVarArgs(constructor);
}
Method method()
{
return (Method)memberObject;
}
Constructor> ctor()
{
return (Constructor>)memberObject;
}
Member member()
{
return memberObject;
}
boolean isMethod()
{
return memberObject instanceof Method;
}
boolean isCtor()
{
return memberObject instanceof Constructor;
}
boolean isStatic()
{
return Modifier.isStatic(memberObject.getModifiers());
}
String getName()
{
return memberObject.getName();
}
Class> getDeclaringClass()
{
return memberObject.getDeclaringClass();
}
String toJavaDeclaration()
{
StringBuilder sb = new StringBuilder();
if (isMethod()) {
Method method = method();
sb.append(method.getReturnType());
sb.append(' ');
sb.append(method.getName());
} else {
Constructor> ctor = ctor();
String name = ctor.getDeclaringClass().getName();
int lastDot = name.lastIndexOf('.');
if (lastDot >= 0) {
name = name.substring(lastDot + 1);
}
sb.append(name);
}
sb.append(JavaMembers.liveConnectSignature(argTypes));
return sb.toString();
}
@Override
public String toString()
{
return memberObject.toString();
}
Object invoke(Object target, Object[] args)
{
Method method = method();
try {
try {
return method.invoke(target, args);
} catch (IllegalAccessException ex) {
Method accessible = searchAccessibleMethod(method, argTypes);
if (accessible != null) {
memberObject = accessible;
method = accessible;
} else {
if (!VMBridge.instance.tryToMakeAccessible(method)) {
throw Context.throwAsScriptRuntimeEx(ex);
}
}
// Retry after recovery
return method.invoke(target, args);
}
} catch (InvocationTargetException ite) {
// Must allow ContinuationPending exceptions to propagate unhindered
Throwable e = ite;
do {
e = ((InvocationTargetException) e).getTargetException();
} while ((e instanceof InvocationTargetException));
if (e instanceof ContinuationPending)
throw (ContinuationPending) e;
throw Context.throwAsScriptRuntimeEx(e);
} catch (Exception ex) {
throw Context.throwAsScriptRuntimeEx(ex);
}
}
Object newInstance(Object[] args)
{
Constructor> ctor = ctor();
try {
try {
return ctor.newInstance(args);
} catch (IllegalAccessException ex) {
if (!VMBridge.instance.tryToMakeAccessible(ctor)) {
throw Context.throwAsScriptRuntimeEx(ex);
}
}
return ctor.newInstance(args);
} catch (Exception ex) {
throw Context.throwAsScriptRuntimeEx(ex);
}
}
private static Method searchAccessibleMethod(Method method, Class>[] params)
{
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers)) {
Class> c = method.getDeclaringClass();
if (!Modifier.isPublic(c.getModifiers())) {
String name = method.getName();
Class>[] intfs = c.getInterfaces();
for (int i = 0, N = intfs.length; i != N; ++i) {
Class> intf = intfs[i];
if (Modifier.isPublic(intf.getModifiers())) {
try {
return intf.getMethod(name, params);
} catch (NoSuchMethodException ex) {
} catch (SecurityException ex) { }
}
}
for (;;) {
c = c.getSuperclass();
if (c == null) { break; }
if (Modifier.isPublic(c.getModifiers())) {
try {
Method m = c.getMethod(name, params);
int mModifiers = m.getModifiers();
if (Modifier.isPublic(mModifiers)
&& !Modifier.isStatic(mModifiers))
{
return m;
}
} catch (NoSuchMethodException ex) {
} catch (SecurityException ex) { }
}
}
}
}
return null;
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
in.defaultReadObject();
Member member = readMember(in);
if (member instanceof Method) {
init((Method)member);
} else {
init((Constructor>)member);
}
}
private void writeObject(ObjectOutputStream out)
throws IOException
{
out.defaultWriteObject();
writeMember(out, memberObject);
}
/**
* Writes a Constructor or Method object.
*
* Methods and Constructors are not serializable, so we must serialize
* information about the class, the name, and the parameters and
* recreate upon deserialization.
*/
private static void writeMember(ObjectOutputStream out, Member member)
throws IOException
{
if (member == null) {
out.writeBoolean(false);
return;
}
out.writeBoolean(true);
if (!(member instanceof Method || member instanceof Constructor))
throw new IllegalArgumentException("not Method or Constructor");
out.writeBoolean(member instanceof Method);
out.writeObject(member.getName());
out.writeObject(member.getDeclaringClass());
if (member instanceof Method) {
writeParameters(out, ((Method) member).getParameterTypes());
} else {
writeParameters(out, ((Constructor>) member).getParameterTypes());
}
}
/**
* Reads a Method or a Constructor from the stream.
*/
private static Member readMember(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
if (!in.readBoolean())
return null;
boolean isMethod = in.readBoolean();
String name = (String) in.readObject();
Class> declaring = (Class>) in.readObject();
Class>[] parms = readParameters(in);
try {
if (isMethod) {
return declaring.getMethod(name, parms);
} else {
return declaring.getConstructor(parms);
}
} catch (NoSuchMethodException e) {
throw new IOException("Cannot find member: " + e);
}
}
private static final Class>[] primitives = {
Boolean.TYPE,
Byte.TYPE,
Character.TYPE,
Double.TYPE,
Float.TYPE,
Integer.TYPE,
Long.TYPE,
Short.TYPE,
Void.TYPE
};
/**
* Writes an array of parameter types to the stream.
*
* Requires special handling because primitive types cannot be
* found upon deserialization by the default Java implementation.
*/
private static void writeParameters(ObjectOutputStream out, Class>[] parms)
throws IOException
{
out.writeShort(parms.length);
outer:
for (int i=0; i < parms.length; i++) {
Class> parm = parms[i];
boolean primitive = parm.isPrimitive();
out.writeBoolean(primitive);
if (!primitive) {
out.writeObject(parm);
continue;
}
for (int j=0; j < primitives.length; j++) {
if (parm.equals(primitives[j])) {
out.writeByte(j);
continue outer;
}
}
throw new IllegalArgumentException("Primitive " + parm +
" not found");
}
}
/**
* Reads an array of parameter types from the stream.
*/
private static Class>[] readParameters(ObjectInputStream in)
throws IOException, ClassNotFoundException
{
Class>[] result = new Class[in.readShort()];
for (int i=0; i < result.length; i++) {
if (!in.readBoolean()) {
result[i] = (Class>) in.readObject();
continue;
}
result[i] = primitives[in.readByte()];
}
return result;
}
}