зеркало из https://github.com/mozilla/pjs.git
Changes necessary to generate adapter classes to files with associated optimizer package.
This commit is contained in:
Родитель
a43dc3b347
Коммит
d66f23fafa
|
@ -31,4 +31,8 @@ public interface ClassNameHelper {
|
|||
public String getTargetClassFileName(String className);
|
||||
|
||||
public String getGeneratingDirectory();
|
||||
|
||||
public void setTargetExtends(Class extendsClass);
|
||||
|
||||
public void setTargetImplements(Class[] implementsClasses);
|
||||
}
|
||||
|
|
|
@ -1279,6 +1279,29 @@ public final class Context {
|
|||
return cl == Interpreter.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class that the generated target will extend.
|
||||
*
|
||||
* @param extendsClass the class it extends
|
||||
*/
|
||||
public void setTargetExtends(Class extendsClass) {
|
||||
if (nameHelper != null) {
|
||||
nameHelper.setTargetExtends(extendsClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interfaces that the generated target will implement.
|
||||
*
|
||||
* @param implementsClasses an array of Class objects, one for each
|
||||
* interface the target will extend
|
||||
*/
|
||||
public void setTargetImplements(Class[] implementsClasses) {
|
||||
if (nameHelper != null) {
|
||||
nameHelper.setTargetImplements(implementsClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**** debugger oriented portion of API ****/
|
||||
|
||||
/**
|
||||
|
@ -1826,8 +1849,8 @@ public final class Context {
|
|||
requireSecurityDomain = s.equals("true");
|
||||
} catch (java.util.MissingResourceException mre) {
|
||||
requireSecurityDomain = true;
|
||||
throw new SecurityException("Resource \"" + securityResourceName +
|
||||
"\" not found.");
|
||||
} catch (SecurityException se) {
|
||||
requireSecurityDomain = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,29 +50,51 @@ public class JavaAdapter extends ScriptableObject {
|
|||
}
|
||||
intfs[interfaceCount++] = c;
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer("adapter");
|
||||
sb.append(serial++);
|
||||
String genName = sb.toString();
|
||||
ClassFileWriter cfw = new ClassFileWriter(genName,
|
||||
Class[] interfaces = new Class[interfaceCount];
|
||||
System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
|
||||
Scriptable obj = (Scriptable) args[args.length - 1];
|
||||
Class adapterClass = createAdapterClass(cx, obj, sb.toString(),
|
||||
superClass, interfaces,
|
||||
null, null);
|
||||
Class[] ctorParms = { FlattenedObject.class };
|
||||
Object[] ctorArgs = { new FlattenedObject(obj) };
|
||||
Object v = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
|
||||
return cx.toObject(v, ScriptableObject.getTopLevelScope(ctorObj));
|
||||
}
|
||||
|
||||
public static Class createAdapterClass(Context cx, Scriptable jsObj,
|
||||
String name, Class superClass,
|
||||
Class[] interfaces,
|
||||
String scriptClassName,
|
||||
ClassNameHelper nameHelper)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
ClassFileWriter cfw = new ClassFileWriter(name,
|
||||
superClass.getName(),
|
||||
"<adapter>");
|
||||
cfw.addField("o", "Lorg/mozilla/javascript/FlattenedObject;",
|
||||
ClassFileWriter.ACC_PRIVATE);
|
||||
cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;",
|
||||
(short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
|
||||
for (int i = 0; i < interfaceCount; i++) {
|
||||
if (intfs[i] != null)
|
||||
cfw.addInterface(intfs[i].getName());
|
||||
(short) (ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
int interfacesCount = interfaces == null ? 0 : interfaces.length;
|
||||
for (int i=0; i < interfacesCount; i++) {
|
||||
if (interfaces[i] != null)
|
||||
cfw.addInterface(interfaces[i].getName());
|
||||
}
|
||||
|
||||
generateCtor(cfw, genName, superClass);
|
||||
generateCtor(cfw, name, superClass);
|
||||
if (scriptClassName != null)
|
||||
generateEmptyCtor(cfw, name, superClass, scriptClassName);
|
||||
|
||||
Hashtable generatedOverrides = new Hashtable();
|
||||
Hashtable generatedMethods = new Hashtable();
|
||||
|
||||
// if abstract class was specified, then generate appropriate overrides.
|
||||
for (int i = 0; i < interfaceCount; i++) {
|
||||
Method[] methods = intfs[i].getMethods();
|
||||
for (int i = 0; i < interfacesCount; i++) {
|
||||
Method[] methods = interfaces[i].getMethods();
|
||||
for (int j = 0; j < methods.length; j++) {
|
||||
Method method = methods[j];
|
||||
int mods = method.getModifiers();
|
||||
|
@ -82,16 +104,20 @@ public class JavaAdapter extends ScriptableObject {
|
|||
// method/signature.
|
||||
String methodKey = getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
generateOverride(cfw, genName, method);
|
||||
Class[] parms = method.getParameterTypes();
|
||||
generateMethod(cfw, name, method.getName(), parms,
|
||||
method.getReturnType());
|
||||
generatedOverrides.put(methodKey, methodKey);
|
||||
generatedMethods.put(method.getName(), Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, go through the superclasses methods, checking for abstract methods
|
||||
// or additional methods to override.
|
||||
FlattenedObject obj = new FlattenedObject(
|
||||
(Scriptable) args[args.length - 1]);
|
||||
// Now, go through the superclasses methods, checking for abstract
|
||||
// methods or additional methods to override.
|
||||
FlattenedObject obj = jsObj != null
|
||||
? new FlattenedObject(jsObj)
|
||||
: null;
|
||||
|
||||
// generate any additional overrides that the object might contain.
|
||||
Method[] methods = superClass.getMethods();
|
||||
|
@ -103,20 +129,47 @@ public class JavaAdapter extends ScriptableObject {
|
|||
// if a method is marked abstract, must implement it or the
|
||||
// resulting class won't be instantiable. otherwise, if the object
|
||||
// has a property of the same name, then an override is intended.
|
||||
if (Modifier.isAbstract(mods) || obj.hasProperty(method.getName())) {
|
||||
if (Modifier.isAbstract(mods) ||
|
||||
(obj != null && obj.hasProperty(method.getName())))
|
||||
{
|
||||
// make sure to generate only one instance of a particular
|
||||
// method/signature.
|
||||
String methodKey = getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
generateOverride(cfw, genName, method);
|
||||
Class[] parms = method.getParameterTypes();
|
||||
generateMethod(cfw, name, method.getName(), parms,
|
||||
method.getReturnType());
|
||||
generatedOverrides.put(methodKey, method);
|
||||
generatedMethods.put(method.getName(), Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generate Java methods, fields for remaining properties that
|
||||
// are not overrides? Probably not necessary to generate methods
|
||||
// that aren't overrides.
|
||||
// Generate Java methods, fields for remaining properties that
|
||||
// are not overrides.
|
||||
Object[] ids = obj.getIds();
|
||||
for (int j=0; j < ids.length; j++) {
|
||||
if (!(ids[j] instanceof String))
|
||||
continue;
|
||||
if (generatedMethods.containsKey((String) ids[j]))
|
||||
continue;
|
||||
Object f = obj.getProperty(ids[j]);
|
||||
int length;
|
||||
if (f instanceof FlattenedObject) {
|
||||
Object p = ((FlattenedObject) f).getObject();
|
||||
if (!(p instanceof Function))
|
||||
continue;
|
||||
length = (int) Context.toNumber(((FlattenedObject) f).getProperty("length"));
|
||||
} else if (f instanceof FunctionNode) {
|
||||
length = ((FunctionNode) f).getVariableTable().getParameterCount();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Class[] parms = new Class[length];
|
||||
for (int k=0; k < length; k++)
|
||||
parms[k] = Object.class;
|
||||
generateMethod(cfw, name, (String) ids[j], parms, Object.class);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
||||
try {
|
||||
|
@ -126,21 +179,34 @@ public class JavaAdapter extends ScriptableObject {
|
|||
throw new RuntimeException("unexpected IOException");
|
||||
}
|
||||
byte[] bytes = out.toByteArray();
|
||||
|
||||
if (nameHelper != null && nameHelper.getGeneratingDirectory() != null)
|
||||
{
|
||||
try {
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot != -1)
|
||||
name = name.substring(lastDot+1);
|
||||
String filename = nameHelper.getTargetClassFileName(name);
|
||||
FileOutputStream file = new FileOutputStream(filename);
|
||||
file.write(bytes);
|
||||
file.close();
|
||||
}
|
||||
catch (IOException iox) {
|
||||
throw WrappedException.wrapException(iox);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
SecuritySupport ss = cx.getSecuritySupport();
|
||||
Class c;
|
||||
if (ss != null) {
|
||||
Object securityDomain = cx.getSecurityDomainForStackDepth(-1);
|
||||
c = ss.defineClass(genName, bytes, securityDomain);
|
||||
return ss.defineClass(name, bytes, securityDomain);
|
||||
} else {
|
||||
if (classLoader == null)
|
||||
classLoader = new MyClassLoader();
|
||||
classLoader.defineClass(genName, bytes);
|
||||
c = classLoader.loadClass(genName, true);
|
||||
classLoader.defineClass(name, bytes);
|
||||
return classLoader.loadClass(name, true);
|
||||
}
|
||||
Class[] ctorParms = { FlattenedObject.class };
|
||||
Object[] ctorArgs = { obj };
|
||||
Object v = c.getConstructor(ctorParms).newInstance(ctorArgs);
|
||||
return cx.toObject(v, ScriptableObject.getTopLevelScope(ctorObj));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,20 +218,28 @@ public class JavaAdapter extends ScriptableObject {
|
|||
{
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
return (object.hasProperty(methodId)
|
||||
? object.callMethod(methodId, args)
|
||||
: Context.getUndefinedValue());
|
||||
if (object.hasProperty(methodId))
|
||||
return object.callMethod(methodId, args);
|
||||
} catch (PropertyException ex) {
|
||||
// shouldn't occur
|
||||
} catch (NotAFunctionException ex) {
|
||||
// TODO: could occur
|
||||
} catch (JavaScriptException ex) {
|
||||
// TODO: could occur
|
||||
/*
|
||||
} catch (Exception ex) {
|
||||
// TODO: wouldn't it be better to let the exception propagate
|
||||
// up so that it could be dealt with by the calling code?
|
||||
ex.printStackTrace(System.err);
|
||||
throw new Error(ex.getMessage());
|
||||
*/
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return Context.getUndefinedValue();
|
||||
}
|
||||
|
||||
private static void generateCtor(ClassFileWriter cfw, String genName,
|
||||
private static void generateCtor(ClassFileWriter cfw, String name,
|
||||
Class superClass)
|
||||
{
|
||||
cfw.startMethod("<init>",
|
||||
|
@ -178,13 +252,24 @@ public class JavaAdapter extends ScriptableObject {
|
|||
superClass.getName().replace('.', '/'),
|
||||
"<init>", "()", "V");
|
||||
|
||||
// Set the prototype of the js object to be a LiveConnect
|
||||
// wapper of the generated class's object
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"setAdapterProto",
|
||||
"(Lorg/mozilla/javascript/FlattenedObject;" +
|
||||
"Ljava/lang/Object;)",
|
||||
"V");
|
||||
|
||||
// Save parameter in instance variable
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
cfw.add(ByteCode.PUTFIELD, genName, "o",
|
||||
cfw.add(ByteCode.PUTFIELD, name, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
// Store Scriptable object in "self" a public instance variable,
|
||||
// Store Scriptable object in "self", a public instance variable,
|
||||
// so scripts can read it.
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
|
@ -192,13 +277,70 @@ public class JavaAdapter extends ScriptableObject {
|
|||
"org/mozilla/javascript/FlattenedObject",
|
||||
"getObject", "()",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.add(ByteCode.PUTFIELD, genName, "self",
|
||||
cfw.add(ByteCode.PUTFIELD, name, "self",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)20, null); // TODO: magic number "20"
|
||||
}
|
||||
|
||||
private static void generateEmptyCtor(ClassFileWriter cfw, String name,
|
||||
Class superClass,
|
||||
String scriptClassName)
|
||||
{
|
||||
cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
|
||||
|
||||
// Invoke base class constructor
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
superClass.getName().replace('.', '/'),
|
||||
"<init>", "()", "V");
|
||||
|
||||
// Load script class
|
||||
cfw.add(ByteCode.NEW, scriptClassName);
|
||||
cfw.add(ByteCode.DUP);
|
||||
cfw.add(ByteCode.INVOKESPECIAL, scriptClassName, "<init>", "()", "V");
|
||||
|
||||
// Run script and save resulting scope
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"runScript",
|
||||
"(Lorg/mozilla/javascript/Script;)",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
cfw.add(ByteCode.ASTORE_1);
|
||||
|
||||
// Save the FlattenedObject in instance variable
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.PUTFIELD, name, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
// Store Scriptable object in "self", a public instance variable,
|
||||
// so scripts can read it.
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL,
|
||||
"org/mozilla/javascript/FlattenedObject",
|
||||
"getObject", "()",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.add(ByteCode.PUTFIELD, name, "self",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
|
||||
// Set the prototype of the js object to be a LiveConnect
|
||||
// wapper of the generated class's object
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"setAdapterProto",
|
||||
"(Lorg/mozilla/javascript/FlattenedObject;" +
|
||||
"Ljava/lang/Object;)",
|
||||
"V");
|
||||
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)20, null); // TODO: magic number "20"
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates code to create a java.lang.Boolean, java.lang.Character or a
|
||||
* java.lang.Double to wrap the specified primitive parameter. Leaves the
|
||||
|
@ -360,10 +502,10 @@ public class JavaAdapter extends ScriptableObject {
|
|||
}
|
||||
}
|
||||
|
||||
private static void generateOverride(ClassFileWriter cfw, String genName,
|
||||
Method m)
|
||||
private static void generateMethod(ClassFileWriter cfw, String genName,
|
||||
String methodName, Class[] parms,
|
||||
Class returnType)
|
||||
{
|
||||
Class[] parms = m.getParameterTypes();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append('(');
|
||||
short arrayLocal = 1; // includes this.
|
||||
|
@ -376,11 +518,11 @@ public class JavaAdapter extends ScriptableObject {
|
|||
arrayLocal += 1;
|
||||
}
|
||||
sb.append(')');
|
||||
appendTypeString(sb, m.getReturnType());
|
||||
appendTypeString(sb, returnType);
|
||||
String methodSignature = sb.toString();
|
||||
// System.out.println("generating " + m.getName() + methodSignature);
|
||||
// System.out.flush();
|
||||
cfw.startMethod(m.getName(), methodSignature,
|
||||
cfw.startMethod(methodName, methodSignature,
|
||||
ClassFileWriter.ACC_PUBLIC);
|
||||
cfw.add(ByteCode.BIPUSH, (byte) parms.length); // > 255 parms?
|
||||
cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
|
||||
|
@ -434,7 +576,7 @@ public class JavaAdapter extends ScriptableObject {
|
|||
cfw.add(ByteCode.GETFIELD, genName, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
cfw.addLoadConstant(m.getName());
|
||||
cfw.addLoadConstant(methodName);
|
||||
cfw.add(ByteCode.ALOAD, arrayLocal);
|
||||
|
||||
// go through utility method, which creates a Context to run the
|
||||
|
@ -446,7 +588,7 @@ public class JavaAdapter extends ScriptableObject {
|
|||
"Ljava/lang/Object;[Ljava/lang/Object;)",
|
||||
"Ljava/lang/Object;");
|
||||
|
||||
Class retType = m.getReturnType();
|
||||
Class retType = returnType;
|
||||
if (retType.equals(Void.TYPE)) {
|
||||
cfw.add(ByteCode.POP);
|
||||
cfw.add(ByteCode.RETURN);
|
||||
|
|
|
@ -164,18 +164,20 @@ public class NativeJavaMethod extends NativeFunction implements Function {
|
|||
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
|
||||
}
|
||||
Object javaObject;
|
||||
try {
|
||||
javaObject = ((NativeJavaObject) thisObj).unwrap();
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
if (Modifier.isStatic(meth.getModifiers())) {
|
||||
javaObject = null; // don't need it anyway
|
||||
javaObject = null; // don't need an object
|
||||
} else {
|
||||
Scriptable o = thisObj;
|
||||
while (!(o instanceof NativeJavaObject)) {
|
||||
o = o.getPrototype();
|
||||
if (o == null) {
|
||||
Object errArgs[] = { names[0] };
|
||||
throw Context.reportRuntimeError(
|
||||
Context.getMessage("msg.nonjava.method", errArgs));
|
||||
}
|
||||
}
|
||||
javaObject = ((NativeJavaObject) o).unwrap();
|
||||
}
|
||||
try {
|
||||
if (debug) {
|
||||
printDebug("Calling ", meth, args);
|
||||
|
|
|
@ -127,7 +127,7 @@ public class NativeJavaPackage extends ScriptableObject {
|
|||
// Can't add properties to Java packages. Sorry.
|
||||
}
|
||||
|
||||
public synchronized Object get(String id, Scriptable start) {
|
||||
public Object get(String id, Scriptable start) {
|
||||
return getPkgProperty(id, start, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1851,8 +1851,8 @@ public class ScriptRuntime {
|
|||
catch (JavaScriptException e) {
|
||||
throw WrappedException.wrapException(e);
|
||||
}
|
||||
for (int i=1; i < args.length; i++) {
|
||||
argsObj.put(i-1, argsObj, args[i]);
|
||||
for (int i=0; i < args.length; i++) {
|
||||
argsObj.put(i, argsObj, args[i]);
|
||||
}
|
||||
global.put("arguments", global, argsObj);
|
||||
|
||||
|
@ -1868,6 +1868,9 @@ public class ScriptRuntime {
|
|||
}
|
||||
catch (IllegalAccessException e) {
|
||||
}
|
||||
finally {
|
||||
Context.exit();
|
||||
}
|
||||
throw new RuntimeException("Error creating script object");
|
||||
}
|
||||
|
||||
|
@ -1912,6 +1915,28 @@ public class ScriptRuntime {
|
|||
return scope;
|
||||
}
|
||||
|
||||
public static FlattenedObject runScript(Script script) {
|
||||
Context cx = Context.enter();
|
||||
Scriptable global = cx.initStandardObjects(new ImporterTopLevel());
|
||||
try {
|
||||
script.exec(cx, global);
|
||||
} catch (JavaScriptException e) {
|
||||
throw new Error(e.toString());
|
||||
}
|
||||
Context.exit();
|
||||
return new FlattenedObject(global);
|
||||
}
|
||||
|
||||
public static void setAdapterProto(FlattenedObject fobj, Object adapter) {
|
||||
Scriptable obj = fobj.getObject();
|
||||
Scriptable p = obj.getPrototype();
|
||||
Scriptable scope = ScriptableObject.getTopLevelScope(obj);
|
||||
if (p == null || p == ScriptableObject.getObjectPrototype(scope)) {
|
||||
Scriptable wrapped = (Scriptable) Context.toObject(adapter, scope);
|
||||
obj.setPrototype(wrapped);
|
||||
}
|
||||
}
|
||||
|
||||
public static Scriptable initVarObj(Context cx, Scriptable scope,
|
||||
NativeFunction funObj,
|
||||
Scriptable thisObj, Object[] args)
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.util.Enumeration;
|
|||
* @see Node
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
class ShallowNodeIterator implements Enumeration {
|
||||
public class ShallowNodeIterator implements Enumeration {
|
||||
|
||||
public ShallowNodeIterator(Node n) {
|
||||
current = n;
|
||||
|
|
|
@ -117,7 +117,7 @@ msg.bad.esc.mask =\
|
|||
|
||||
# NativeJavaClass
|
||||
msg.cant.instantiate =\
|
||||
error instantiating {0}: class {1} is interface or abstract
|
||||
error instantiating ({0}): class {1} is interface or abstract
|
||||
|
||||
msg.bad.ctor.sig =\
|
||||
Found constructor with wrong signature: \
|
||||
|
|
|
@ -31,4 +31,8 @@ public interface ClassNameHelper {
|
|||
public String getTargetClassFileName(String className);
|
||||
|
||||
public String getGeneratingDirectory();
|
||||
|
||||
public void setTargetExtends(Class extendsClass);
|
||||
|
||||
public void setTargetImplements(Class[] implementsClasses);
|
||||
}
|
||||
|
|
|
@ -1279,6 +1279,29 @@ public final class Context {
|
|||
return cl == Interpreter.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class that the generated target will extend.
|
||||
*
|
||||
* @param extendsClass the class it extends
|
||||
*/
|
||||
public void setTargetExtends(Class extendsClass) {
|
||||
if (nameHelper != null) {
|
||||
nameHelper.setTargetExtends(extendsClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interfaces that the generated target will implement.
|
||||
*
|
||||
* @param implementsClasses an array of Class objects, one for each
|
||||
* interface the target will extend
|
||||
*/
|
||||
public void setTargetImplements(Class[] implementsClasses) {
|
||||
if (nameHelper != null) {
|
||||
nameHelper.setTargetImplements(implementsClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**** debugger oriented portion of API ****/
|
||||
|
||||
/**
|
||||
|
@ -1826,8 +1849,8 @@ public final class Context {
|
|||
requireSecurityDomain = s.equals("true");
|
||||
} catch (java.util.MissingResourceException mre) {
|
||||
requireSecurityDomain = true;
|
||||
throw new SecurityException("Resource \"" + securityResourceName +
|
||||
"\" not found.");
|
||||
} catch (SecurityException se) {
|
||||
requireSecurityDomain = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,29 +50,51 @@ public class JavaAdapter extends ScriptableObject {
|
|||
}
|
||||
intfs[interfaceCount++] = c;
|
||||
}
|
||||
|
||||
StringBuffer sb = new StringBuffer("adapter");
|
||||
sb.append(serial++);
|
||||
String genName = sb.toString();
|
||||
ClassFileWriter cfw = new ClassFileWriter(genName,
|
||||
Class[] interfaces = new Class[interfaceCount];
|
||||
System.arraycopy(intfs, 0, interfaces, 0, interfaceCount);
|
||||
Scriptable obj = (Scriptable) args[args.length - 1];
|
||||
Class adapterClass = createAdapterClass(cx, obj, sb.toString(),
|
||||
superClass, interfaces,
|
||||
null, null);
|
||||
Class[] ctorParms = { FlattenedObject.class };
|
||||
Object[] ctorArgs = { new FlattenedObject(obj) };
|
||||
Object v = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs);
|
||||
return cx.toObject(v, ScriptableObject.getTopLevelScope(ctorObj));
|
||||
}
|
||||
|
||||
public static Class createAdapterClass(Context cx, Scriptable jsObj,
|
||||
String name, Class superClass,
|
||||
Class[] interfaces,
|
||||
String scriptClassName,
|
||||
ClassNameHelper nameHelper)
|
||||
throws ClassNotFoundException
|
||||
{
|
||||
ClassFileWriter cfw = new ClassFileWriter(name,
|
||||
superClass.getName(),
|
||||
"<adapter>");
|
||||
cfw.addField("o", "Lorg/mozilla/javascript/FlattenedObject;",
|
||||
ClassFileWriter.ACC_PRIVATE);
|
||||
cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;",
|
||||
(short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_FINAL));
|
||||
for (int i = 0; i < interfaceCount; i++) {
|
||||
if (intfs[i] != null)
|
||||
cfw.addInterface(intfs[i].getName());
|
||||
(short) (ClassFileWriter.ACC_PUBLIC |
|
||||
ClassFileWriter.ACC_FINAL));
|
||||
int interfacesCount = interfaces == null ? 0 : interfaces.length;
|
||||
for (int i=0; i < interfacesCount; i++) {
|
||||
if (interfaces[i] != null)
|
||||
cfw.addInterface(interfaces[i].getName());
|
||||
}
|
||||
|
||||
generateCtor(cfw, genName, superClass);
|
||||
generateCtor(cfw, name, superClass);
|
||||
if (scriptClassName != null)
|
||||
generateEmptyCtor(cfw, name, superClass, scriptClassName);
|
||||
|
||||
Hashtable generatedOverrides = new Hashtable();
|
||||
Hashtable generatedMethods = new Hashtable();
|
||||
|
||||
// if abstract class was specified, then generate appropriate overrides.
|
||||
for (int i = 0; i < interfaceCount; i++) {
|
||||
Method[] methods = intfs[i].getMethods();
|
||||
for (int i = 0; i < interfacesCount; i++) {
|
||||
Method[] methods = interfaces[i].getMethods();
|
||||
for (int j = 0; j < methods.length; j++) {
|
||||
Method method = methods[j];
|
||||
int mods = method.getModifiers();
|
||||
|
@ -82,16 +104,20 @@ public class JavaAdapter extends ScriptableObject {
|
|||
// method/signature.
|
||||
String methodKey = getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
generateOverride(cfw, genName, method);
|
||||
Class[] parms = method.getParameterTypes();
|
||||
generateMethod(cfw, name, method.getName(), parms,
|
||||
method.getReturnType());
|
||||
generatedOverrides.put(methodKey, methodKey);
|
||||
generatedMethods.put(method.getName(), Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, go through the superclasses methods, checking for abstract methods
|
||||
// or additional methods to override.
|
||||
FlattenedObject obj = new FlattenedObject(
|
||||
(Scriptable) args[args.length - 1]);
|
||||
// Now, go through the superclasses methods, checking for abstract
|
||||
// methods or additional methods to override.
|
||||
FlattenedObject obj = jsObj != null
|
||||
? new FlattenedObject(jsObj)
|
||||
: null;
|
||||
|
||||
// generate any additional overrides that the object might contain.
|
||||
Method[] methods = superClass.getMethods();
|
||||
|
@ -103,20 +129,47 @@ public class JavaAdapter extends ScriptableObject {
|
|||
// if a method is marked abstract, must implement it or the
|
||||
// resulting class won't be instantiable. otherwise, if the object
|
||||
// has a property of the same name, then an override is intended.
|
||||
if (Modifier.isAbstract(mods) || obj.hasProperty(method.getName())) {
|
||||
if (Modifier.isAbstract(mods) ||
|
||||
(obj != null && obj.hasProperty(method.getName())))
|
||||
{
|
||||
// make sure to generate only one instance of a particular
|
||||
// method/signature.
|
||||
String methodKey = getMethodSignature(method);
|
||||
if (! generatedOverrides.containsKey(methodKey)) {
|
||||
generateOverride(cfw, genName, method);
|
||||
Class[] parms = method.getParameterTypes();
|
||||
generateMethod(cfw, name, method.getName(), parms,
|
||||
method.getReturnType());
|
||||
generatedOverrides.put(methodKey, method);
|
||||
generatedMethods.put(method.getName(), Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generate Java methods, fields for remaining properties that
|
||||
// are not overrides? Probably not necessary to generate methods
|
||||
// that aren't overrides.
|
||||
// Generate Java methods, fields for remaining properties that
|
||||
// are not overrides.
|
||||
Object[] ids = obj.getIds();
|
||||
for (int j=0; j < ids.length; j++) {
|
||||
if (!(ids[j] instanceof String))
|
||||
continue;
|
||||
if (generatedMethods.containsKey((String) ids[j]))
|
||||
continue;
|
||||
Object f = obj.getProperty(ids[j]);
|
||||
int length;
|
||||
if (f instanceof FlattenedObject) {
|
||||
Object p = ((FlattenedObject) f).getObject();
|
||||
if (!(p instanceof Function))
|
||||
continue;
|
||||
length = (int) Context.toNumber(((FlattenedObject) f).getProperty("length"));
|
||||
} else if (f instanceof FunctionNode) {
|
||||
length = ((FunctionNode) f).getVariableTable().getParameterCount();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Class[] parms = new Class[length];
|
||||
for (int k=0; k < length; k++)
|
||||
parms[k] = Object.class;
|
||||
generateMethod(cfw, name, (String) ids[j], parms, Object.class);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
||||
try {
|
||||
|
@ -126,21 +179,34 @@ public class JavaAdapter extends ScriptableObject {
|
|||
throw new RuntimeException("unexpected IOException");
|
||||
}
|
||||
byte[] bytes = out.toByteArray();
|
||||
|
||||
if (nameHelper != null && nameHelper.getGeneratingDirectory() != null)
|
||||
{
|
||||
try {
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot != -1)
|
||||
name = name.substring(lastDot+1);
|
||||
String filename = nameHelper.getTargetClassFileName(name);
|
||||
FileOutputStream file = new FileOutputStream(filename);
|
||||
file.write(bytes);
|
||||
file.close();
|
||||
}
|
||||
catch (IOException iox) {
|
||||
throw WrappedException.wrapException(iox);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
SecuritySupport ss = cx.getSecuritySupport();
|
||||
Class c;
|
||||
if (ss != null) {
|
||||
Object securityDomain = cx.getSecurityDomainForStackDepth(-1);
|
||||
c = ss.defineClass(genName, bytes, securityDomain);
|
||||
return ss.defineClass(name, bytes, securityDomain);
|
||||
} else {
|
||||
if (classLoader == null)
|
||||
classLoader = new MyClassLoader();
|
||||
classLoader.defineClass(genName, bytes);
|
||||
c = classLoader.loadClass(genName, true);
|
||||
classLoader.defineClass(name, bytes);
|
||||
return classLoader.loadClass(name, true);
|
||||
}
|
||||
Class[] ctorParms = { FlattenedObject.class };
|
||||
Object[] ctorArgs = { obj };
|
||||
Object v = c.getConstructor(ctorParms).newInstance(ctorArgs);
|
||||
return cx.toObject(v, ScriptableObject.getTopLevelScope(ctorObj));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -152,20 +218,28 @@ public class JavaAdapter extends ScriptableObject {
|
|||
{
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
return (object.hasProperty(methodId)
|
||||
? object.callMethod(methodId, args)
|
||||
: Context.getUndefinedValue());
|
||||
if (object.hasProperty(methodId))
|
||||
return object.callMethod(methodId, args);
|
||||
} catch (PropertyException ex) {
|
||||
// shouldn't occur
|
||||
} catch (NotAFunctionException ex) {
|
||||
// TODO: could occur
|
||||
} catch (JavaScriptException ex) {
|
||||
// TODO: could occur
|
||||
/*
|
||||
} catch (Exception ex) {
|
||||
// TODO: wouldn't it be better to let the exception propagate
|
||||
// up so that it could be dealt with by the calling code?
|
||||
ex.printStackTrace(System.err);
|
||||
throw new Error(ex.getMessage());
|
||||
*/
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return Context.getUndefinedValue();
|
||||
}
|
||||
|
||||
private static void generateCtor(ClassFileWriter cfw, String genName,
|
||||
private static void generateCtor(ClassFileWriter cfw, String name,
|
||||
Class superClass)
|
||||
{
|
||||
cfw.startMethod("<init>",
|
||||
|
@ -178,13 +252,24 @@ public class JavaAdapter extends ScriptableObject {
|
|||
superClass.getName().replace('.', '/'),
|
||||
"<init>", "()", "V");
|
||||
|
||||
// Set the prototype of the js object to be a LiveConnect
|
||||
// wapper of the generated class's object
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"setAdapterProto",
|
||||
"(Lorg/mozilla/javascript/FlattenedObject;" +
|
||||
"Ljava/lang/Object;)",
|
||||
"V");
|
||||
|
||||
// Save parameter in instance variable
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
cfw.add(ByteCode.PUTFIELD, genName, "o",
|
||||
cfw.add(ByteCode.PUTFIELD, name, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
// Store Scriptable object in "self" a public instance variable,
|
||||
// Store Scriptable object in "self", a public instance variable,
|
||||
// so scripts can read it.
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // first arg
|
||||
|
@ -192,13 +277,70 @@ public class JavaAdapter extends ScriptableObject {
|
|||
"org/mozilla/javascript/FlattenedObject",
|
||||
"getObject", "()",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.add(ByteCode.PUTFIELD, genName, "self",
|
||||
cfw.add(ByteCode.PUTFIELD, name, "self",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)20, null); // TODO: magic number "20"
|
||||
}
|
||||
|
||||
private static void generateEmptyCtor(ClassFileWriter cfw, String name,
|
||||
Class superClass,
|
||||
String scriptClassName)
|
||||
{
|
||||
cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
|
||||
|
||||
// Invoke base class constructor
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESPECIAL,
|
||||
superClass.getName().replace('.', '/'),
|
||||
"<init>", "()", "V");
|
||||
|
||||
// Load script class
|
||||
cfw.add(ByteCode.NEW, scriptClassName);
|
||||
cfw.add(ByteCode.DUP);
|
||||
cfw.add(ByteCode.INVOKESPECIAL, scriptClassName, "<init>", "()", "V");
|
||||
|
||||
// Run script and save resulting scope
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"runScript",
|
||||
"(Lorg/mozilla/javascript/Script;)",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
cfw.add(ByteCode.ASTORE_1);
|
||||
|
||||
// Save the FlattenedObject in instance variable
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.PUTFIELD, name, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
// Store Scriptable object in "self", a public instance variable,
|
||||
// so scripts can read it.
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.INVOKEVIRTUAL,
|
||||
"org/mozilla/javascript/FlattenedObject",
|
||||
"getObject", "()",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
cfw.add(ByteCode.PUTFIELD, name, "self",
|
||||
"Lorg/mozilla/javascript/Scriptable;");
|
||||
|
||||
// Set the prototype of the js object to be a LiveConnect
|
||||
// wapper of the generated class's object
|
||||
cfw.add(ByteCode.ALOAD_1); // the FlattenedObject
|
||||
cfw.add(ByteCode.ALOAD_0); // this
|
||||
cfw.add(ByteCode.INVOKESTATIC,
|
||||
"org/mozilla/javascript/ScriptRuntime",
|
||||
"setAdapterProto",
|
||||
"(Lorg/mozilla/javascript/FlattenedObject;" +
|
||||
"Ljava/lang/Object;)",
|
||||
"V");
|
||||
|
||||
cfw.add(ByteCode.RETURN);
|
||||
cfw.stopMethod((short)20, null); // TODO: magic number "20"
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates code to create a java.lang.Boolean, java.lang.Character or a
|
||||
* java.lang.Double to wrap the specified primitive parameter. Leaves the
|
||||
|
@ -360,10 +502,10 @@ public class JavaAdapter extends ScriptableObject {
|
|||
}
|
||||
}
|
||||
|
||||
private static void generateOverride(ClassFileWriter cfw, String genName,
|
||||
Method m)
|
||||
private static void generateMethod(ClassFileWriter cfw, String genName,
|
||||
String methodName, Class[] parms,
|
||||
Class returnType)
|
||||
{
|
||||
Class[] parms = m.getParameterTypes();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append('(');
|
||||
short arrayLocal = 1; // includes this.
|
||||
|
@ -376,11 +518,11 @@ public class JavaAdapter extends ScriptableObject {
|
|||
arrayLocal += 1;
|
||||
}
|
||||
sb.append(')');
|
||||
appendTypeString(sb, m.getReturnType());
|
||||
appendTypeString(sb, returnType);
|
||||
String methodSignature = sb.toString();
|
||||
// System.out.println("generating " + m.getName() + methodSignature);
|
||||
// System.out.flush();
|
||||
cfw.startMethod(m.getName(), methodSignature,
|
||||
cfw.startMethod(methodName, methodSignature,
|
||||
ClassFileWriter.ACC_PUBLIC);
|
||||
cfw.add(ByteCode.BIPUSH, (byte) parms.length); // > 255 parms?
|
||||
cfw.add(ByteCode.ANEWARRAY, "java/lang/Object");
|
||||
|
@ -434,7 +576,7 @@ public class JavaAdapter extends ScriptableObject {
|
|||
cfw.add(ByteCode.GETFIELD, genName, "o",
|
||||
"Lorg/mozilla/javascript/FlattenedObject;");
|
||||
|
||||
cfw.addLoadConstant(m.getName());
|
||||
cfw.addLoadConstant(methodName);
|
||||
cfw.add(ByteCode.ALOAD, arrayLocal);
|
||||
|
||||
// go through utility method, which creates a Context to run the
|
||||
|
@ -446,7 +588,7 @@ public class JavaAdapter extends ScriptableObject {
|
|||
"Ljava/lang/Object;[Ljava/lang/Object;)",
|
||||
"Ljava/lang/Object;");
|
||||
|
||||
Class retType = m.getReturnType();
|
||||
Class retType = returnType;
|
||||
if (retType.equals(Void.TYPE)) {
|
||||
cfw.add(ByteCode.POP);
|
||||
cfw.add(ByteCode.RETURN);
|
||||
|
|
|
@ -164,18 +164,20 @@ public class NativeJavaMethod extends NativeFunction implements Function {
|
|||
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
|
||||
}
|
||||
Object javaObject;
|
||||
try {
|
||||
javaObject = ((NativeJavaObject) thisObj).unwrap();
|
||||
}
|
||||
catch (ClassCastException e) {
|
||||
if (Modifier.isStatic(meth.getModifiers())) {
|
||||
javaObject = null; // don't need it anyway
|
||||
javaObject = null; // don't need an object
|
||||
} else {
|
||||
Scriptable o = thisObj;
|
||||
while (!(o instanceof NativeJavaObject)) {
|
||||
o = o.getPrototype();
|
||||
if (o == null) {
|
||||
Object errArgs[] = { names[0] };
|
||||
throw Context.reportRuntimeError(
|
||||
Context.getMessage("msg.nonjava.method", errArgs));
|
||||
}
|
||||
}
|
||||
javaObject = ((NativeJavaObject) o).unwrap();
|
||||
}
|
||||
try {
|
||||
if (debug) {
|
||||
printDebug("Calling ", meth, args);
|
||||
|
|
|
@ -127,7 +127,7 @@ public class NativeJavaPackage extends ScriptableObject {
|
|||
// Can't add properties to Java packages. Sorry.
|
||||
}
|
||||
|
||||
public synchronized Object get(String id, Scriptable start) {
|
||||
public Object get(String id, Scriptable start) {
|
||||
return getPkgProperty(id, start, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1851,8 +1851,8 @@ public class ScriptRuntime {
|
|||
catch (JavaScriptException e) {
|
||||
throw WrappedException.wrapException(e);
|
||||
}
|
||||
for (int i=1; i < args.length; i++) {
|
||||
argsObj.put(i-1, argsObj, args[i]);
|
||||
for (int i=0; i < args.length; i++) {
|
||||
argsObj.put(i, argsObj, args[i]);
|
||||
}
|
||||
global.put("arguments", global, argsObj);
|
||||
|
||||
|
@ -1868,6 +1868,9 @@ public class ScriptRuntime {
|
|||
}
|
||||
catch (IllegalAccessException e) {
|
||||
}
|
||||
finally {
|
||||
Context.exit();
|
||||
}
|
||||
throw new RuntimeException("Error creating script object");
|
||||
}
|
||||
|
||||
|
@ -1912,6 +1915,28 @@ public class ScriptRuntime {
|
|||
return scope;
|
||||
}
|
||||
|
||||
public static FlattenedObject runScript(Script script) {
|
||||
Context cx = Context.enter();
|
||||
Scriptable global = cx.initStandardObjects(new ImporterTopLevel());
|
||||
try {
|
||||
script.exec(cx, global);
|
||||
} catch (JavaScriptException e) {
|
||||
throw new Error(e.toString());
|
||||
}
|
||||
Context.exit();
|
||||
return new FlattenedObject(global);
|
||||
}
|
||||
|
||||
public static void setAdapterProto(FlattenedObject fobj, Object adapter) {
|
||||
Scriptable obj = fobj.getObject();
|
||||
Scriptable p = obj.getPrototype();
|
||||
Scriptable scope = ScriptableObject.getTopLevelScope(obj);
|
||||
if (p == null || p == ScriptableObject.getObjectPrototype(scope)) {
|
||||
Scriptable wrapped = (Scriptable) Context.toObject(adapter, scope);
|
||||
obj.setPrototype(wrapped);
|
||||
}
|
||||
}
|
||||
|
||||
public static Scriptable initVarObj(Context cx, Scriptable scope,
|
||||
NativeFunction funObj,
|
||||
Scriptable thisObj, Object[] args)
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.util.Enumeration;
|
|||
* @see Node
|
||||
* @author Norris Boyd
|
||||
*/
|
||||
class ShallowNodeIterator implements Enumeration {
|
||||
public class ShallowNodeIterator implements Enumeration {
|
||||
|
||||
public ShallowNodeIterator(Node n) {
|
||||
current = n;
|
||||
|
|
|
@ -117,7 +117,7 @@ msg.bad.esc.mask =\
|
|||
|
||||
# NativeJavaClass
|
||||
msg.cant.instantiate =\
|
||||
error instantiating {0}: class {1} is interface or abstract
|
||||
error instantiating ({0}): class {1} is interface or abstract
|
||||
|
||||
msg.bad.ctor.sig =\
|
||||
Found constructor with wrong signature: \
|
||||
|
|
Загрузка…
Ссылка в новой задаче