mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 13:55:43 +00:00
Norris Boyd wrote:
> Igor Bukanov wrote: > > >>Norris Boyd wrote: >> >> >>>The intention was to keep classfile and JavaAdapter optional. Those >>>dependencies crept in. We can use Invoker optionally--perhaps I should do >>>some performance numbers to see if it's worth it. >>> >>I implemented that patch, it splits Invoker.java into Invoker.java and >>its implementation in optimizer/InvokerImpl.java The reason to put it >>into optimizer package is that Invoker is very similar in spirit to >>NativeScript: it generates classes to speed up access and in this way >>there is no need to have separated option not to use: one can simply >>remove optimizer all together. >> > > Yes, that sounds great. > > >> >>I noticed during implementation that JavaAdapter.DefiningClassLoader and >>optimizer/JavaScriptClassLoader contains the same code, so maybe they >>can be moved to org.mozilla.classfile package under one name? >> > > Yes, that sounds like a good change too. Thanks for noticing that. The update is pretty messy: it touches FunctionObject which I changed to remove the special treatment of NativeWith in the previous patch, and it also add/removes files. Here is a description: DefiningClassLoader.java should go to org/mozilla/classfile. It is the same code that was in omj/optimizer/JavaScriptClassLoader.java with class rename and adding public attribute and correspondingly omj.optimizer/JavaScriptClassLoader.java should be removed. I guess it would be nice to preserve logs/history in CVS during the move. InvokerImpl.java should go to omj/optimizer. It is mostly what omj.Invoker was. invoker_changes_patch was generated via cvs diff -u Invoker.java JavaAdapter.java optimizer/Codegen.java and contains changes against the current CVS FunctionObject_invoker_patch was generated via diff -u ../../mozilla.1/javascript/FunctionObject.java FunctionObject.java and contains changes against that With patch. Igor
This commit is contained in:
parent
4b89ac4b2e
commit
0068a15b8a
@ -20,6 +20,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Igor Bukanov
|
||||
* David C. Navas
|
||||
* Ted Neward
|
||||
*
|
||||
@ -555,13 +556,10 @@ public class FunctionObject extends NativeFunction {
|
||||
private final Object doInvoke(Object thisObj, Object[] args)
|
||||
throws IllegalAccessException, InvocationTargetException
|
||||
{
|
||||
if (classLoader != null) {
|
||||
Invoker master = invokerMaster;
|
||||
if (master != null) {
|
||||
if (invoker == null) {
|
||||
invoker = (Invoker) invokersCache.get(method);
|
||||
if (invoker == null) {
|
||||
invoker = Invoker.createInvoker(method, types, classLoader);
|
||||
invokersCache.put(method, invoker);
|
||||
}
|
||||
invoker = master.createInvoker(method, types);
|
||||
}
|
||||
try {
|
||||
return invoker.invoke(thisObj, args);
|
||||
@ -617,23 +615,33 @@ public class FunctionObject extends NativeFunction {
|
||||
static void setCachingEnabled(boolean enabled) {
|
||||
if (!enabled) {
|
||||
methodsCache = null;
|
||||
classLoader = null;
|
||||
invokersCache = null;
|
||||
} else if (classLoader == null) {
|
||||
classLoader = JavaAdapter.createDefiningClassLoader();
|
||||
invokersCache = new Hashtable();
|
||||
invokerMaster = null;
|
||||
} else if (invokerMaster == null) {
|
||||
invokerMaster = newInvokerMaster();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get default master implementation or null if not available */
|
||||
private static Invoker newInvokerMaster() {
|
||||
try {
|
||||
Class cl = Class.forName(INVOKER_MASTER_CLASS);
|
||||
return (Invoker)cl.newInstance();
|
||||
}
|
||||
catch (Exception ex) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final String
|
||||
INVOKER_MASTER_CLASS = "org.mozilla.javascript.optimizer.InvokerImpl";
|
||||
|
||||
static Invoker invokerMaster = newInvokerMaster();
|
||||
|
||||
private static final short VARARGS_METHOD = -1;
|
||||
private static final short VARARGS_CTOR = -2;
|
||||
|
||||
private static boolean sawSecurityException;
|
||||
|
||||
static Method[] methodsCache;
|
||||
static Hashtable invokersCache = new Hashtable();
|
||||
static JavaAdapter.DefiningClassLoader classLoader
|
||||
= JavaAdapter.createDefiningClassLoader();
|
||||
|
||||
Method method;
|
||||
Constructor ctor;
|
||||
|
@ -18,7 +18,7 @@
|
||||
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* David C. Navas
|
||||
*
|
||||
@ -36,259 +36,19 @@
|
||||
|
||||
package org.mozilla.javascript;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.io.IOException;
|
||||
import org.mozilla.classfile.*;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Avoid cost of java.lang.reflect.Method.invoke() by compiling a class to
|
||||
* Avoid cost of java.lang.reflect.Method.invoke() by compiling a class to
|
||||
* perform the method call directly.
|
||||
*/
|
||||
public abstract class Invoker {
|
||||
static int classNumber = 0;
|
||||
|
||||
public static Invoker createInvoker(Method method, Class[] types,
|
||||
JavaAdapter.DefiningClassLoader classLoader)
|
||||
{
|
||||
Invoker result = null;
|
||||
String className = "inv" + ++classNumber;
|
||||
|
||||
ClassFileWriter cfw = new ClassFileWriter(className,
|
||||
"org.mozilla.javascript.Invoker", "");
|
||||
cfw.setFlags((short)(ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
|
||||
// Add our instantiator!
|
||||
cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
|
||||
cfw.add(ByteCode.ALOAD_0);
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
"org.mozilla.javascript.Invoker",
|
||||
"<init>", "()", "V");
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)1, null); // one argument -- this???
|
||||
|
||||
// Add the invoke() method call
|
||||
cfw.startMethod("invoke",
|
||||
"(Ljava/lang/Object;[Ljava/lang/Object;)"+
|
||||
"Ljava/lang/Object;",
|
||||
(short)(ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
|
||||
// If we return a primitive type, then do something special!
|
||||
String declaringClassName = method.getDeclaringClass().getName
|
||||
().replace('.', '/');
|
||||
Class returnType = method.getReturnType();
|
||||
String invokeSpecial = null;
|
||||
String invokeSpecialType = null;
|
||||
boolean returnsVoid = false;
|
||||
boolean returnsBoolean = false;
|
||||
|
||||
if (returnType.isPrimitive()) {
|
||||
if (returnType == Boolean.TYPE) {
|
||||
returnsBoolean = true;
|
||||
invokeSpecialType = "(Z)";
|
||||
} else if (returnType == Void.TYPE) {
|
||||
returnsVoid = true;
|
||||
invokeSpecialType = "(V)";
|
||||
} else if (returnType == Integer.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Integer");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(I)";
|
||||
} else if (returnType == Long.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Long");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(J)";
|
||||
} else if (returnType == Short.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Short");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(S)";
|
||||
} else if (returnType == Float.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Float");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(F)";
|
||||
} else if (returnType == Double.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Double");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(D)";
|
||||
} else if (returnType == Byte.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Byte");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(B)";
|
||||
} else if (returnType == Character.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial
|
||||
= "java/lang/Character");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(C)";
|
||||
}
|
||||
}
|
||||
|
||||
// handle setup of call to virtual function (if calling non-static)
|
||||
if (!java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
|
||||
cfw.add(ByteCode.ALOAD_1);
|
||||
cfw.add(ByteCode.CHECKCAST, declaringClassName);
|
||||
}
|
||||
|
||||
// Handle parameters!
|
||||
StringBuffer params = new StringBuffer(2 + ((types!=null)?(20 *
|
||||
types.length):0));
|
||||
|
||||
params.append("(");
|
||||
if (types != null) {
|
||||
for(int i = 0; i < types.length; i++) {
|
||||
Class type = types[i];
|
||||
|
||||
cfw.add(ByteCode.ALOAD_2);
|
||||
|
||||
if (i <= 5) {
|
||||
cfw.add((byte) (ByteCode.ICONST_0 + i));
|
||||
} else if (i <= Byte.MAX_VALUE) {
|
||||
cfw.add(ByteCode.BIPUSH, i);
|
||||
} else if (i <= Short.MAX_VALUE) {
|
||||
cfw.add(ByteCode.SIPUSH, i);
|
||||
} else {
|
||||
cfw.addLoadConstant((int)i);
|
||||
}
|
||||
|
||||
cfw.add(ByteCode.AALOAD);
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
// Convert enclosed type back to primitive.
|
||||
|
||||
if (type == Boolean.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Boolean");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Boolean",
|
||||
"booleanValue", "()", "Z");
|
||||
params.append("Z");
|
||||
} else if (type == Integer.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"intValue", "()", "I");
|
||||
params.append("I");
|
||||
} else if (type == Short.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"shortValue", "()", "S");
|
||||
params.append("S");
|
||||
} else if (type == Character.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Character");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Character",
|
||||
"charValue", "()", "C");
|
||||
params.append("C");
|
||||
} else if (type == Double.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"doubleValue", "()", "D");
|
||||
params.append("D");
|
||||
} else if (type == Float.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"floatValue", "()", "F");
|
||||
params.append("F");
|
||||
} else if (type == Byte.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Byte");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Byte",
|
||||
"byteValue", "()", "B");
|
||||
params.append("B");
|
||||
}
|
||||
} else {
|
||||
String typeName = type.getName().replace('.', '/');
|
||||
cfw.add(ByteCode.CHECKCAST, typeName);
|
||||
|
||||
if (!type.isArray()) {
|
||||
params.append('L');
|
||||
}
|
||||
params.append(typeName);
|
||||
|
||||
if (!type.isArray()) {
|
||||
params.append(';');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params.append(")");
|
||||
|
||||
// Call actual function!
|
||||
if (!java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, declaringClassName,
|
||||
method.getName(), params.toString(),
|
||||
(invokeSpecialType!=null?invokeSpecialType.substring(1,2)
|
||||
:returnType.isArray()?
|
||||
returnType.getName().replace('.','/')
|
||||
:"L".concat
|
||||
(returnType.getName().replace('.', '/').concat(";"))));
|
||||
} else {
|
||||
cfw.add(ByteCode.INVOKESTATIC, declaringClassName,
|
||||
method.getName(), params.toString(),
|
||||
(invokeSpecialType!=null?invokeSpecialType.substring(1,2)
|
||||
:returnType.isArray()?
|
||||
returnType.getName().replace('.','/')
|
||||
:"L".concat
|
||||
(returnType.getName().replace('.', '/').concat(";"))));
|
||||
}
|
||||
|
||||
// Handle return value
|
||||
if (returnsVoid) {
|
||||
cfw.add(ByteCode.ACONST_NULL);
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else if (returnsBoolean) {
|
||||
// HACK
|
||||
//check to see if true;
|
||||
// '7' is the number of bytes of the ifeq<branch> plus getstatic<TRUE> plus areturn instructions
|
||||
cfw.add(ByteCode.IFEQ, 7);
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
"java/lang/Boolean",
|
||||
"TRUE",
|
||||
"Ljava/lang/Boolean;");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
"java/lang/Boolean",
|
||||
"FALSE",
|
||||
"Ljava/lang/Boolean;");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else if (invokeSpecial != null) {
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
invokeSpecial,
|
||||
"<init>", invokeSpecialType, "V");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else {
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
}
|
||||
cfw.stopMethod((short)3, null); // three arguments, including the this pointer???
|
||||
|
||||
// Add class to our classloader.
|
||||
java.io.ByteArrayOutputStream bos =
|
||||
new java.io.ByteArrayOutputStream(550);
|
||||
|
||||
try {
|
||||
cfw.write(bos);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new RuntimeException("unexpected IOException" + ioe.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = bos.toByteArray();
|
||||
|
||||
Context cx = Context.getCurrentContext();
|
||||
classLoader.defineClass(className, bytes);
|
||||
Class c = classLoader.loadClass(className, true);
|
||||
result = (Invoker)c.newInstance();
|
||||
|
||||
if (false) {
|
||||
System.out.println("Generated method delegate for: " + method.getName()
|
||||
+ " on " + method.getDeclaringClass().getName() + " :: " + params.toString()
|
||||
+ " :: " + types);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public abstract Object invoke(Object that, Object [] args);
|
||||
|
||||
/** Factory method to get invoker for given method */
|
||||
public Invoker createInvoker(Method method, Class[] types) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ import java.util.*;
|
||||
|
||||
public class JavaAdapter extends ScriptableObject {
|
||||
public boolean equals(Object obj) {
|
||||
return super.equals(obj);
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
@ -167,7 +167,7 @@ public class JavaAdapter extends ScriptableObject {
|
||||
for (int i = 0; i < interfacesCount; i++) {
|
||||
Method[] methods = interfaces[i].getMethods();
|
||||
for (int j = 0; j < methods.length; j++) {
|
||||
Method method = methods[j];
|
||||
Method method = methods[j];
|
||||
int mods = method.getModifiers();
|
||||
if (Modifier.isStatic(mods) || Modifier.isFinal(mods) ||
|
||||
jsObj == null)
|
||||
@ -189,8 +189,8 @@ public class JavaAdapter extends ScriptableObject {
|
||||
// make sure to generate only one instance of a particular
|
||||
// method/signature.
|
||||
String methodName = method.getName();
|
||||
String methodKey = methodName + getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
String methodKey = methodName + getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
generateMethod(cfw, adapterName, methodName,
|
||||
method.getParameterTypes(),
|
||||
method.getReturnType());
|
||||
@ -322,15 +322,6 @@ public class JavaAdapter extends ScriptableObject {
|
||||
return classLoader.loadClass(adapterName, true);
|
||||
}
|
||||
|
||||
public static DefiningClassLoader createDefiningClassLoader() {
|
||||
try {
|
||||
return new JavaAdapter.DefiningClassLoader();
|
||||
} catch (Throwable t) {
|
||||
// most likely a security exception; just skip this optimization
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method which dynamically binds a Context to the current thread,
|
||||
* if none already exists.
|
||||
@ -595,7 +586,7 @@ public class JavaAdapter extends ScriptableObject {
|
||||
{
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append('(');
|
||||
short arrayLocal = 1; // includes this.
|
||||
short arrayLocal = 1; // includes this.
|
||||
for (int i = 0; i < parms.length; i++) {
|
||||
Class type = parms[i];
|
||||
appendTypeString(sb, type);
|
||||
@ -753,11 +744,11 @@ public class JavaAdapter extends ScriptableObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a method called "super$methodName()" which can be called
|
||||
* from JavaScript that is equivalent to calling "super.methodName()"
|
||||
* from Java. Eventually, this may be supported directly in JavaScript.
|
||||
*/
|
||||
/**
|
||||
* Generates a method called "super$methodName()" which can be called
|
||||
* from JavaScript that is equivalent to calling "super.methodName()"
|
||||
* from Java. Eventually, this may be supported directly in JavaScript.
|
||||
*/
|
||||
private static void generateSuper(ClassFileWriter cfw,
|
||||
String genName, String superName,
|
||||
String methodName, String methodSignature,
|
||||
@ -766,10 +757,10 @@ public class JavaAdapter extends ScriptableObject {
|
||||
cfw.startMethod("super$" + methodName, methodSignature,
|
||||
ClassFileWriter.ACC_PUBLIC);
|
||||
|
||||
// push "this"
|
||||
// push "this"
|
||||
cfw.add(ByteCode.ALOAD, 0);
|
||||
|
||||
// push the rest of the parameters.
|
||||
// push the rest of the parameters.
|
||||
int paramOffset = 1;
|
||||
for (int i = 0; i < parms.length; i++) {
|
||||
if (parms[i].isPrimitive()) {
|
||||
@ -789,12 +780,12 @@ public class JavaAdapter extends ScriptableObject {
|
||||
methodSignature.substring(0, rightParen + 1),
|
||||
methodSignature.substring(rightParen + 1));
|
||||
|
||||
// now, handle the return type appropriately.
|
||||
// now, handle the return type appropriately.
|
||||
Class retType = returnType;
|
||||
if (!retType.equals(Void.TYPE)) {
|
||||
generatePopResult(cfw, retType);
|
||||
} else {
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.add(ByteCode.RETURN);
|
||||
}
|
||||
cfw.stopMethod((short)(paramOffset + 1), null);
|
||||
}
|
||||
@ -839,41 +830,6 @@ public class JavaAdapter extends ScriptableObject {
|
||||
return sb;
|
||||
}
|
||||
|
||||
static final class DefiningClassLoader extends ClassLoader {
|
||||
public Class defineClass(String name, byte data[]) {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
if (loader != null) {
|
||||
Class clazz = ClassManager.defineClass(loader, name, data);
|
||||
if (clazz != null)
|
||||
return clazz;
|
||||
}
|
||||
return super.defineClass(name, data, 0, data.length);
|
||||
}
|
||||
|
||||
protected Class loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
Class clazz;
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
if (loader != null) {
|
||||
clazz = ClassManager.loadClass(loader, name, resolve);
|
||||
if (clazz != null)
|
||||
return clazz;
|
||||
}
|
||||
clazz = findLoadedClass(name);
|
||||
if (clazz == null) {
|
||||
try {
|
||||
clazz = findSystemClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return ScriptRuntime.loadClassName(name);
|
||||
}
|
||||
}
|
||||
if (resolve)
|
||||
resolveClass(clazz);
|
||||
return clazz;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a key with which to distinguish previously generated
|
||||
* adapter classes stored in a hash table.
|
||||
|
@ -79,7 +79,7 @@ public class Codegen extends Interpreter {
|
||||
|
||||
Exception e = null;
|
||||
Class result = null;
|
||||
JavaScriptClassLoader classLoader = new JavaScriptClassLoader();
|
||||
DefiningClassLoader classLoader = new DefiningClassLoader();
|
||||
|
||||
try {
|
||||
if (cx.getOptimizationLevel() > 0) {
|
||||
|
309
js/rhino/src/org/mozilla/javascript/optimizer/InvokerImpl.java
Normal file
309
js/rhino/src/org/mozilla/javascript/optimizer/InvokerImpl.java
Normal file
@ -0,0 +1,309 @@
|
||||
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* David C. Navas
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the NPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the NPL or the GPL.
|
||||
*/
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.mozilla.javascript.Invoker;
|
||||
import org.mozilla.classfile.ByteCode;
|
||||
import org.mozilla.classfile.ClassFileWriter;
|
||||
import org.mozilla.classfile.DefiningClassLoader;
|
||||
|
||||
/**
|
||||
* Avoid cost of java.lang.reflect.Method.invoke() by compiling a class to
|
||||
* perform the method call directly.
|
||||
*/
|
||||
public class InvokerImpl extends Invoker {
|
||||
|
||||
public Invoker createInvoker(Method method, Class[] types) {
|
||||
|
||||
Invoker result = (Invoker)invokersCache.get(method);
|
||||
if (result != null) { return result; }
|
||||
|
||||
synchronized (this) {
|
||||
++classNumber;
|
||||
}
|
||||
|
||||
String className = "inv" + classNumber;
|
||||
|
||||
ClassFileWriter cfw = new ClassFileWriter(className,
|
||||
"org.mozilla.javascript.Invoker", "");
|
||||
cfw.setFlags((short)(ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
|
||||
// Add our instantiator!
|
||||
cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
|
||||
cfw.add(ByteCode.ALOAD_0);
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
"org.mozilla.javascript.Invoker",
|
||||
"<init>", "()", "V");
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)1, null); // one argument -- this???
|
||||
|
||||
// Add the invoke() method call
|
||||
cfw.startMethod("invoke",
|
||||
"(Ljava/lang/Object;[Ljava/lang/Object;)"+
|
||||
"Ljava/lang/Object;",
|
||||
(short)(ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
|
||||
// If we return a primitive type, then do something special!
|
||||
String declaringClassName = method.getDeclaringClass().getName
|
||||
().replace('.', '/');
|
||||
Class returnType = method.getReturnType();
|
||||
String invokeSpecial = null;
|
||||
String invokeSpecialType = null;
|
||||
boolean returnsVoid = false;
|
||||
boolean returnsBoolean = false;
|
||||
|
||||
if (returnType.isPrimitive()) {
|
||||
if (returnType == Boolean.TYPE) {
|
||||
returnsBoolean = true;
|
||||
invokeSpecialType = "(Z)";
|
||||
} else if (returnType == Void.TYPE) {
|
||||
returnsVoid = true;
|
||||
invokeSpecialType = "(V)";
|
||||
} else if (returnType == Integer.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Integer");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(I)";
|
||||
} else if (returnType == Long.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Long");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(J)";
|
||||
} else if (returnType == Short.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Short");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(S)";
|
||||
} else if (returnType == Float.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Float");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(F)";
|
||||
} else if (returnType == Double.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Double");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(D)";
|
||||
} else if (returnType == Byte.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial = "java/lang/Byte");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(B)";
|
||||
} else if (returnType == Character.TYPE) {
|
||||
cfw.add(ByteCode.NEW, invokeSpecial
|
||||
= "java/lang/Character");
|
||||
cfw.add(ByteCode.DUP);
|
||||
invokeSpecialType = "(C)";
|
||||
}
|
||||
}
|
||||
|
||||
// handle setup of call to virtual function (if calling non-static)
|
||||
if (!java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
|
||||
cfw.add(ByteCode.ALOAD_1);
|
||||
cfw.add(ByteCode.CHECKCAST, declaringClassName);
|
||||
}
|
||||
|
||||
// Handle parameters!
|
||||
StringBuffer params = new StringBuffer(2 + ((types!=null)?(20 *
|
||||
types.length):0));
|
||||
|
||||
params.append("(");
|
||||
if (types != null) {
|
||||
for(int i = 0; i < types.length; i++) {
|
||||
Class type = types[i];
|
||||
|
||||
cfw.add(ByteCode.ALOAD_2);
|
||||
|
||||
if (i <= 5) {
|
||||
cfw.add((byte) (ByteCode.ICONST_0 + i));
|
||||
} else if (i <= Byte.MAX_VALUE) {
|
||||
cfw.add(ByteCode.BIPUSH, i);
|
||||
} else if (i <= Short.MAX_VALUE) {
|
||||
cfw.add(ByteCode.SIPUSH, i);
|
||||
} else {
|
||||
cfw.addLoadConstant((int)i);
|
||||
}
|
||||
|
||||
cfw.add(ByteCode.AALOAD);
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
// Convert enclosed type back to primitive.
|
||||
|
||||
if (type == Boolean.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Boolean");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Boolean",
|
||||
"booleanValue", "()", "Z");
|
||||
params.append("Z");
|
||||
} else if (type == Integer.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"intValue", "()", "I");
|
||||
params.append("I");
|
||||
} else if (type == Short.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"shortValue", "()", "S");
|
||||
params.append("S");
|
||||
} else if (type == Character.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Character");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Character",
|
||||
"charValue", "()", "C");
|
||||
params.append("C");
|
||||
} else if (type == Double.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"doubleValue", "()", "D");
|
||||
params.append("D");
|
||||
} else if (type == Float.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Number");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Number",
|
||||
"floatValue", "()", "F");
|
||||
params.append("F");
|
||||
} else if (type == Byte.TYPE) {
|
||||
cfw.add(ByteCode.CHECKCAST, "java/lang/Byte");
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, "java/lang/Byte",
|
||||
"byteValue", "()", "B");
|
||||
params.append("B");
|
||||
}
|
||||
} else {
|
||||
String typeName = type.getName().replace('.', '/');
|
||||
cfw.add(ByteCode.CHECKCAST, typeName);
|
||||
|
||||
if (!type.isArray()) {
|
||||
params.append('L');
|
||||
}
|
||||
params.append(typeName);
|
||||
|
||||
if (!type.isArray()) {
|
||||
params.append(';');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
params.append(")");
|
||||
|
||||
// Call actual function!
|
||||
if (!java.lang.reflect.Modifier.isStatic(method.getModifiers())) {
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL, declaringClassName,
|
||||
method.getName(), params.toString(),
|
||||
(invokeSpecialType!=null?invokeSpecialType.substring(1,2)
|
||||
:returnType.isArray()?
|
||||
returnType.getName().replace('.','/')
|
||||
:"L".concat
|
||||
(returnType.getName().replace('.', '/').concat(";"))));
|
||||
} else {
|
||||
cfw.add(ByteCode.INVOKESTATIC, declaringClassName,
|
||||
method.getName(), params.toString(),
|
||||
(invokeSpecialType!=null?invokeSpecialType.substring(1,2)
|
||||
:returnType.isArray()?
|
||||
returnType.getName().replace('.','/')
|
||||
:"L".concat
|
||||
(returnType.getName().replace('.', '/').concat(";"))));
|
||||
}
|
||||
|
||||
// Handle return value
|
||||
if (returnsVoid) {
|
||||
cfw.add(ByteCode.ACONST_NULL);
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else if (returnsBoolean) {
|
||||
// HACK
|
||||
//check to see if true;
|
||||
// '7' is the number of bytes of the ifeq<branch> plus getstatic<TRUE> plus areturn instructions
|
||||
cfw.add(ByteCode.IFEQ, 7);
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
"java/lang/Boolean",
|
||||
"TRUE",
|
||||
"Ljava/lang/Boolean;");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
cfw.add(ByteCode.GETSTATIC,
|
||||
"java/lang/Boolean",
|
||||
"FALSE",
|
||||
"Ljava/lang/Boolean;");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else if (invokeSpecial != null) {
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
invokeSpecial,
|
||||
"<init>", invokeSpecialType, "V");
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
} else {
|
||||
cfw.add(ByteCode.ARETURN);
|
||||
}
|
||||
cfw.stopMethod((short)3, null); // three arguments, including the this pointer???
|
||||
|
||||
// Add class to our classloader.
|
||||
java.io.ByteArrayOutputStream bos =
|
||||
new java.io.ByteArrayOutputStream(550);
|
||||
|
||||
try {
|
||||
cfw.write(bos);
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
throw new RuntimeException("unexpected IOException" + ioe.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] bytes = bos.toByteArray();
|
||||
|
||||
classLoader.defineClass(className, bytes);
|
||||
Class c = classLoader.loadClass(className, true);
|
||||
result = (Invoker)c.newInstance();
|
||||
|
||||
if (false) {
|
||||
System.out.println("Generated method delegate for: " + method.getName()
|
||||
+ " on " + method.getDeclaringClass().getName() + " :: " + params.toString()
|
||||
+ " :: " + types);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("unexpected " + e.toString());
|
||||
}
|
||||
|
||||
invokersCache.put(method, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public Object invoke(Object that, Object [] args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int classNumber;
|
||||
Hashtable invokersCache = new Hashtable();
|
||||
DefiningClassLoader classLoader = new DefiningClassLoader();
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* The contents of this file are subject to the Netscape Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/NPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is Rhino code, released
|
||||
* May 6, 1999.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Norris Boyd
|
||||
* Roger Lawrence
|
||||
* Patrick Beard
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the NPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the NPL or the GPL.
|
||||
*/
|
||||
|
||||
|
||||
package org.mozilla.javascript.optimizer;
|
||||
|
||||
import java.util.*;
|
||||
import org.mozilla.classfile.ClassManager;
|
||||
|
||||
/**
|
||||
* Load generated classes that implement JavaScript scripts
|
||||
* or functions.
|
||||
*
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
final class JavaScriptClassLoader extends ClassLoader {
|
||||
|
||||
JavaScriptClassLoader() {
|
||||
}
|
||||
|
||||
public Class defineClass(String name, byte data[]) {
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
if (loader != null) {
|
||||
Class clazz = ClassManager.defineClass(loader, name, data);
|
||||
if (clazz != null)
|
||||
return clazz;
|
||||
}
|
||||
return super.defineClass(name, data, 0, data.length);
|
||||
}
|
||||
|
||||
protected Class loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
Class clazz;
|
||||
ClassLoader loader = getClass().getClassLoader();
|
||||
if (loader != null) {
|
||||
clazz = ClassManager.loadClass(loader, name, resolve);
|
||||
if (clazz != null)
|
||||
return clazz;
|
||||
}
|
||||
clazz = findLoadedClass(name);
|
||||
if (clazz == null)
|
||||
clazz = findSystemClass(name);
|
||||
if (resolve)
|
||||
resolveClass(clazz);
|
||||
return clazz;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user