From d66f23fafaedaad40ee785446fd336dc7d6a4a02 Mon Sep 17 00:00:00 2001 From: "norris%netscape.com" Date: Mon, 20 Sep 1999 21:16:45 +0000 Subject: [PATCH] Changes necessary to generate adapter classes to files with associated optimizer package. --- .../mozilla/javascript/ClassNameHelper.java | 4 + js/rhino/org/mozilla/javascript/Context.java | 29 ++- .../org/mozilla/javascript/JavaAdapter.java | 232 ++++++++++++++---- .../mozilla/javascript/NativeJavaMethod.java | 24 +- .../mozilla/javascript/NativeJavaPackage.java | 2 +- .../org/mozilla/javascript/ScriptRuntime.java | 29 ++- .../javascript/ShallowNodeIterator.java | 2 +- .../javascript/resources/Messages.properties | 2 +- .../mozilla/javascript/ClassNameHelper.java | 4 + .../src/org/mozilla/javascript/Context.java | 29 ++- .../org/mozilla/javascript/JavaAdapter.java | 232 ++++++++++++++---- .../mozilla/javascript/NativeJavaMethod.java | 24 +- .../mozilla/javascript/NativeJavaPackage.java | 2 +- .../org/mozilla/javascript/ScriptRuntime.java | 29 ++- .../javascript/ShallowNodeIterator.java | 2 +- .../javascript/resources/Messages.properties | 2 +- 16 files changed, 520 insertions(+), 128 deletions(-) diff --git a/js/rhino/org/mozilla/javascript/ClassNameHelper.java b/js/rhino/org/mozilla/javascript/ClassNameHelper.java index 9d0b4979a27..b7b35eed208 100644 --- a/js/rhino/org/mozilla/javascript/ClassNameHelper.java +++ b/js/rhino/org/mozilla/javascript/ClassNameHelper.java @@ -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); } diff --git a/js/rhino/org/mozilla/javascript/Context.java b/js/rhino/org/mozilla/javascript/Context.java index 6dcbf24fcc7..7a3056964f7 100644 --- a/js/rhino/org/mozilla/javascript/Context.java +++ b/js/rhino/org/mozilla/javascript/Context.java @@ -1278,6 +1278,29 @@ public final class Context { public boolean isInterpreterClass(Class cl) { 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,9 +1849,9 @@ 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; + } } static final boolean useJSObject = false; diff --git a/js/rhino/org/mozilla/javascript/JavaAdapter.java b/js/rhino/org/mozilla/javascript/JavaAdapter.java index ce5c927f528..ac4f1a72c10 100644 --- a/js/rhino/org/mozilla/javascript/JavaAdapter.java +++ b/js/rhino/org/mozilla/javascript/JavaAdapter.java @@ -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(), ""); 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,18 +104,22 @@ public class JavaAdapter extends ScriptableObject { // method/signature. String methodKey = getMethodSignature(method); if (! generatedOverrides.containsKey(methodKey)) { - generateOverride(cfw, genName, method); - generatedOverrides.put(methodKey, methodKey); + 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. + // generate any additional overrides that the object might contain. Method[] methods = superClass.getMethods(); for (int j = 0; j < methods.length; j++) { Method method = methods[j]; @@ -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("", @@ -178,13 +252,24 @@ public class JavaAdapter extends ScriptableObject { superClass.getName().replace('.', '/'), "", "()", "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("", "()V", ClassFileWriter.ACC_PUBLIC); + + // Invoke base class constructor + cfw.add(ByteCode.ALOAD_0); // this + cfw.add(ByteCode.INVOKESPECIAL, + superClass.getName().replace('.', '/'), + "", "()", "V"); + + // Load script class + cfw.add(ByteCode.NEW, scriptClassName); + cfw.add(ByteCode.DUP); + cfw.add(ByteCode.INVOKESPECIAL, scriptClassName, "", "()", "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); diff --git a/js/rhino/org/mozilla/javascript/NativeJavaMethod.java b/js/rhino/org/mozilla/javascript/NativeJavaMethod.java index 13715690b7e..d1b449b6acc 100644 --- a/js/rhino/org/mozilla/javascript/NativeJavaMethod.java +++ b/js/rhino/org/mozilla/javascript/NativeJavaMethod.java @@ -164,21 +164,23 @@ 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 - } else { - Object errArgs[] = { names[0] }; - throw Context.reportRuntimeError( - Context.getMessage("msg.nonjava.method", errArgs)); + if (Modifier.isStatic(meth.getModifiers())) { + 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); + printDebug("Calling ", meth, args); } Object retval = meth.invoke(javaObject, args); diff --git a/js/rhino/org/mozilla/javascript/NativeJavaPackage.java b/js/rhino/org/mozilla/javascript/NativeJavaPackage.java index aa323b2459d..fbd6cbba28a 100644 --- a/js/rhino/org/mozilla/javascript/NativeJavaPackage.java +++ b/js/rhino/org/mozilla/javascript/NativeJavaPackage.java @@ -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); } diff --git a/js/rhino/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/org/mozilla/javascript/ScriptRuntime.java index 0c111be73bd..47fba70c19f 100644 --- a/js/rhino/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/org/mozilla/javascript/ScriptRuntime.java @@ -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) diff --git a/js/rhino/org/mozilla/javascript/ShallowNodeIterator.java b/js/rhino/org/mozilla/javascript/ShallowNodeIterator.java index 12160991e4e..dbc7eeca0d9 100644 --- a/js/rhino/org/mozilla/javascript/ShallowNodeIterator.java +++ b/js/rhino/org/mozilla/javascript/ShallowNodeIterator.java @@ -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; diff --git a/js/rhino/org/mozilla/javascript/resources/Messages.properties b/js/rhino/org/mozilla/javascript/resources/Messages.properties index fc09e2dee4a..e7715a33af5 100644 --- a/js/rhino/org/mozilla/javascript/resources/Messages.properties +++ b/js/rhino/org/mozilla/javascript/resources/Messages.properties @@ -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: \ diff --git a/js/rhino/src/org/mozilla/javascript/ClassNameHelper.java b/js/rhino/src/org/mozilla/javascript/ClassNameHelper.java index 9d0b4979a27..b7b35eed208 100644 --- a/js/rhino/src/org/mozilla/javascript/ClassNameHelper.java +++ b/js/rhino/src/org/mozilla/javascript/ClassNameHelper.java @@ -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); } diff --git a/js/rhino/src/org/mozilla/javascript/Context.java b/js/rhino/src/org/mozilla/javascript/Context.java index 6dcbf24fcc7..7a3056964f7 100644 --- a/js/rhino/src/org/mozilla/javascript/Context.java +++ b/js/rhino/src/org/mozilla/javascript/Context.java @@ -1278,6 +1278,29 @@ public final class Context { public boolean isInterpreterClass(Class cl) { 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,9 +1849,9 @@ 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; + } } static final boolean useJSObject = false; diff --git a/js/rhino/src/org/mozilla/javascript/JavaAdapter.java b/js/rhino/src/org/mozilla/javascript/JavaAdapter.java index ce5c927f528..ac4f1a72c10 100644 --- a/js/rhino/src/org/mozilla/javascript/JavaAdapter.java +++ b/js/rhino/src/org/mozilla/javascript/JavaAdapter.java @@ -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(), ""); 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,18 +104,22 @@ public class JavaAdapter extends ScriptableObject { // method/signature. String methodKey = getMethodSignature(method); if (! generatedOverrides.containsKey(methodKey)) { - generateOverride(cfw, genName, method); - generatedOverrides.put(methodKey, methodKey); + 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. + // generate any additional overrides that the object might contain. Method[] methods = superClass.getMethods(); for (int j = 0; j < methods.length; j++) { Method method = methods[j]; @@ -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("", @@ -178,13 +252,24 @@ public class JavaAdapter extends ScriptableObject { superClass.getName().replace('.', '/'), "", "()", "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("", "()V", ClassFileWriter.ACC_PUBLIC); + + // Invoke base class constructor + cfw.add(ByteCode.ALOAD_0); // this + cfw.add(ByteCode.INVOKESPECIAL, + superClass.getName().replace('.', '/'), + "", "()", "V"); + + // Load script class + cfw.add(ByteCode.NEW, scriptClassName); + cfw.add(ByteCode.DUP); + cfw.add(ByteCode.INVOKESPECIAL, scriptClassName, "", "()", "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); diff --git a/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java b/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java index 13715690b7e..d1b449b6acc 100644 --- a/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java @@ -164,21 +164,23 @@ 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 - } else { - Object errArgs[] = { names[0] }; - throw Context.reportRuntimeError( - Context.getMessage("msg.nonjava.method", errArgs)); + if (Modifier.isStatic(meth.getModifiers())) { + 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); + printDebug("Calling ", meth, args); } Object retval = meth.invoke(javaObject, args); diff --git a/js/rhino/src/org/mozilla/javascript/NativeJavaPackage.java b/js/rhino/src/org/mozilla/javascript/NativeJavaPackage.java index aa323b2459d..fbd6cbba28a 100644 --- a/js/rhino/src/org/mozilla/javascript/NativeJavaPackage.java +++ b/js/rhino/src/org/mozilla/javascript/NativeJavaPackage.java @@ -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); } diff --git a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java index 0c111be73bd..47fba70c19f 100644 --- a/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java +++ b/js/rhino/src/org/mozilla/javascript/ScriptRuntime.java @@ -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) diff --git a/js/rhino/src/org/mozilla/javascript/ShallowNodeIterator.java b/js/rhino/src/org/mozilla/javascript/ShallowNodeIterator.java index 12160991e4e..dbc7eeca0d9 100644 --- a/js/rhino/src/org/mozilla/javascript/ShallowNodeIterator.java +++ b/js/rhino/src/org/mozilla/javascript/ShallowNodeIterator.java @@ -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; diff --git a/js/rhino/src/org/mozilla/javascript/resources/Messages.properties b/js/rhino/src/org/mozilla/javascript/resources/Messages.properties index fc09e2dee4a..e7715a33af5 100644 --- a/js/rhino/src/org/mozilla/javascript/resources/Messages.properties +++ b/js/rhino/src/org/mozilla/javascript/resources/Messages.properties @@ -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: \