diff options
author | Egil Moeller <egil.moller@freecode.no> | 2010-03-22 00:16:53 +0100 |
---|---|---|
committer | Egil Moeller <egil.moller@freecode.no> | 2010-03-22 00:16:53 +0100 |
commit | fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7 (patch) | |
tree | 928419d6bfe794865e4b8f0634bd792f795c4b8a /trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java | |
parent | 0fdab3db005c4fa31fb8306b573c0b35073d3e56 (diff) | |
parent | d56b9b3b82cdebcaeb00eec0fcb4326ad21adaa8 (diff) | |
download | etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.tar.gz etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.tar.xz etherpad-fc00cfedd6c25d1634a1e0d28a7b7bc5f42e1cd7.zip |
Merge branch 'master' into plugin-framework
Diffstat (limited to 'trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java')
-rw-r--r-- | trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java | 1129 |
1 files changed, 0 insertions, 1129 deletions
diff --git a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java b/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java deleted file mode 100644 index 6e0a827..0000000 --- a/trunk/infrastructure/rhino1_7R1/src/org/mozilla/javascript/JavaAdapter.java +++ /dev/null @@ -1,1129 +0,0 @@ -/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0 - * - * The contents of this file are subject to the Mozilla 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/MPL/ - * - * 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 the Initial Developer are Copyright (C) 1997-1999 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Beard - * Norris Boyd - * Igor Bukanov - * Mike McCabe - * Matthias Radestock - * Andi Vajda - * Andrew Wason - * Kemal Bayram - * - * Alternatively, the contents of this file may be used under the terms of - * the GNU General Public License Version 2 or later (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 - * MPL, indicate your decision by deleting the provisions above and replacing - * 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 MPL or the GPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.javascript; - -import org.mozilla.classfile.*; -import java.lang.reflect.*; -import java.io.*; -import java.security.*; -import java.util.*; - -public final class JavaAdapter implements IdFunctionCall -{ - /** - * Provides a key with which to distinguish previously generated - * adapter classes stored in a hash table. - */ - static class JavaAdapterSignature - { - Class superClass; - Class[] interfaces; - ObjToIntMap names; - - JavaAdapterSignature(Class superClass, Class[] interfaces, - ObjToIntMap names) - { - this.superClass = superClass; - this.interfaces = interfaces; - this.names = names; - } - - public boolean equals(Object obj) - { - if (!(obj instanceof JavaAdapterSignature)) - return false; - JavaAdapterSignature sig = (JavaAdapterSignature) obj; - if (superClass != sig.superClass) - return false; - if (interfaces != sig.interfaces) { - if (interfaces.length != sig.interfaces.length) - return false; - for (int i=0; i < interfaces.length; i++) - if (interfaces[i] != sig.interfaces[i]) - return false; - } - if (names.size() != sig.names.size()) - return false; - ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(names); - for (iter.start(); !iter.done(); iter.next()) { - String name = (String)iter.getKey(); - int arity = iter.getValue(); - if (arity != names.get(name, arity + 1)) - return false; - } - return true; - } - - public int hashCode() - { - return superClass.hashCode() - | (0x9e3779b9 * (names.size() | (interfaces.length << 16))); - } - } - - public static void init(Context cx, Scriptable scope, boolean sealed) - { - JavaAdapter obj = new JavaAdapter(); - IdFunctionObject ctor = new IdFunctionObject(obj, FTAG, Id_JavaAdapter, - "JavaAdapter", 1, scope); - ctor.markAsConstructor(null); - if (sealed) { - ctor.sealObject(); - } - ctor.exportAsScopeProperty(); - } - - public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope, - Scriptable thisObj, Object[] args) - { - if (f.hasTag(FTAG)) { - if (f.methodId() == Id_JavaAdapter) { - return js_createAdapter(cx, scope, args); - } - } - throw f.unknown(); - } - - public static Object convertResult(Object result, Class c) - { - if (result == Undefined.instance && - (c != ScriptRuntime.ObjectClass && - c != ScriptRuntime.StringClass)) - { - // Avoid an error for an undefined value; return null instead. - return null; - } - return Context.jsToJava(result, c); - } - - public static Scriptable createAdapterWrapper(Scriptable obj, - Object adapter) - { - Scriptable scope = ScriptableObject.getTopLevelScope(obj); - NativeJavaObject res = new NativeJavaObject(scope, adapter, null, true); - res.setPrototype(obj); - return res; - } - - public static Object getAdapterSelf(Class adapterClass, Object adapter) - throws NoSuchFieldException, IllegalAccessException - { - Field self = adapterClass.getDeclaredField("self"); - return self.get(adapter); - } - - static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) - { - int N = args.length; - if (N == 0) { - throw ScriptRuntime.typeError0("msg.adapter.zero.args"); - } - - Class superClass = null; - Class[] intfs = new Class[N - 1]; - int interfaceCount = 0; - for (int i = 0; i != N - 1; ++i) { - Object arg = args[i]; - if (!(arg instanceof NativeJavaClass)) { - throw ScriptRuntime.typeError2("msg.not.java.class.arg", - String.valueOf(i), - ScriptRuntime.toString(arg)); - } - Class c = ((NativeJavaClass) arg).getClassObject(); - if (!c.isInterface()) { - if (superClass != null) { - throw ScriptRuntime.typeError2("msg.only.one.super", - superClass.getName(), c.getName()); - } - superClass = c; - } else { - intfs[interfaceCount++] = c; - } - } - - if (superClass == null) - superClass = ScriptRuntime.ObjectClass; - - Class[] interfaces = new Class[interfaceCount]; - System.arraycopy(intfs, 0, interfaces, 0, interfaceCount); - Scriptable obj = ScriptRuntime.toObject(cx, scope, args[N - 1]); - - Class adapterClass = getAdapterClass(scope, superClass, interfaces, - obj); - - Class[] ctorParms = { - ScriptRuntime.ContextFactoryClass, - ScriptRuntime.ScriptableClass - }; - Object[] ctorArgs = { cx.getFactory(), obj }; - try { - Object adapter = adapterClass.getConstructor(ctorParms). - newInstance(ctorArgs); - return getAdapterSelf(adapterClass, adapter); - } catch (Exception ex) { - throw Context.throwAsScriptRuntimeEx(ex); - } - } - - // Needed by NativeJavaObject serializer - public static void writeAdapterObject(Object javaObject, - ObjectOutputStream out) - throws IOException - { - Class cl = javaObject.getClass(); - out.writeObject(cl.getSuperclass().getName()); - - Class[] interfaces = cl.getInterfaces(); - String[] interfaceNames = new String[interfaces.length]; - - for (int i=0; i < interfaces.length; i++) - interfaceNames[i] = interfaces[i].getName(); - - out.writeObject(interfaceNames); - - try { - Object delegee = cl.getField("delegee").get(javaObject); - out.writeObject(delegee); - return; - } catch (IllegalAccessException e) { - } catch (NoSuchFieldException e) { - } - throw new IOException(); - } - - // Needed by NativeJavaObject de-serializer - public static Object readAdapterObject(Scriptable self, - ObjectInputStream in) - throws IOException, ClassNotFoundException - { - ContextFactory factory; - Context cx = Context.getCurrentContext(); - if (cx != null) { - factory = cx.getFactory(); - } else { - factory = null; - } - - Class superClass = Class.forName((String)in.readObject()); - - String[] interfaceNames = (String[])in.readObject(); - Class[] interfaces = new Class[interfaceNames.length]; - - for (int i=0; i < interfaceNames.length; i++) - interfaces[i] = Class.forName(interfaceNames[i]); - - Scriptable delegee = (Scriptable)in.readObject(); - - Class adapterClass = getAdapterClass(self, superClass, interfaces, - delegee); - - Class[] ctorParms = { - ScriptRuntime.ContextFactoryClass, - ScriptRuntime.ScriptableClass, - ScriptRuntime.ScriptableClass - }; - Object[] ctorArgs = { factory, delegee, self }; - try { - return adapterClass.getConstructor(ctorParms).newInstance(ctorArgs); - } catch(InstantiationException e) { - } catch(IllegalAccessException e) { - } catch(InvocationTargetException e) { - } catch(NoSuchMethodException e) { - } - - throw new ClassNotFoundException("adapter"); - } - - private static ObjToIntMap getObjectFunctionNames(Scriptable obj) - { - Object[] ids = ScriptableObject.getPropertyIds(obj); - ObjToIntMap map = new ObjToIntMap(ids.length); - for (int i = 0; i != ids.length; ++i) { - if (!(ids[i] instanceof String)) - continue; - String id = (String) ids[i]; - Object value = ScriptableObject.getProperty(obj, id); - if (value instanceof Function) { - Function f = (Function)value; - int length = ScriptRuntime.toInt32( - ScriptableObject.getProperty(f, "length")); - if (length < 0) { - length = 0; - } - map.put(id, length); - } - } - return map; - } - - private static Class getAdapterClass(Scriptable scope, Class superClass, - Class[] interfaces, Scriptable obj) - { - ClassCache cache = ClassCache.get(scope); - Map<JavaAdapterSignature,Class<?>> generated - = cache.getInterfaceAdapterCacheMap(); - - ObjToIntMap names = getObjectFunctionNames(obj); - JavaAdapterSignature sig; - sig = new JavaAdapterSignature(superClass, interfaces, names); - Class<?> adapterClass = generated.get(sig); - if (adapterClass == null) { - String adapterName = "adapter" - + cache.newClassSerialNumber(); - byte[] code = createAdapterCode(names, adapterName, - superClass, interfaces, null); - - adapterClass = loadAdapterClass(adapterName, code); - if (cache.isCachingEnabled()) { - generated.put(sig, adapterClass); - } - } - return adapterClass; - } - - public static byte[] createAdapterCode(ObjToIntMap functionNames, - String adapterName, - Class superClass, - Class[] interfaces, - String scriptClassName) - { - ClassFileWriter cfw = new ClassFileWriter(adapterName, - superClass.getName(), - "<adapter>"); - cfw.addField("factory", "Lorg/mozilla/javascript/ContextFactory;", - (short) (ClassFileWriter.ACC_PUBLIC | - ClassFileWriter.ACC_FINAL)); - cfw.addField("delegee", "Lorg/mozilla/javascript/Scriptable;", - (short) (ClassFileWriter.ACC_PUBLIC | - ClassFileWriter.ACC_FINAL)); - cfw.addField("self", "Lorg/mozilla/javascript/Scriptable;", - (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()); - } - - String superName = superClass.getName().replace('.', '/'); - generateCtor(cfw, adapterName, superName); - generateSerialCtor(cfw, adapterName, superName); - if (scriptClassName != null) - generateEmptyCtor(cfw, adapterName, superName, scriptClassName); - - ObjToIntMap generatedOverrides = new ObjToIntMap(); - ObjToIntMap generatedMethods = new ObjToIntMap(); - - // generate methods to satisfy all specified interfaces. - 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(); - if (Modifier.isStatic(mods) || Modifier.isFinal(mods)) { - continue; - } - String methodName = method.getName(); - Class[] argTypes = method.getParameterTypes(); - if (!functionNames.has(methodName)) { - try { - superClass.getMethod(methodName, argTypes); - // The class we're extending implements this method and - // the JavaScript object doesn't have an override. See - // bug 61226. - continue; - } catch (NoSuchMethodException e) { - // Not implemented by superclass; fall through - } - } - // make sure to generate only one instance of a particular - // method/signature. - String methodSignature = getMethodSignature(method, argTypes); - String methodKey = methodName + methodSignature; - if (! generatedOverrides.has(methodKey)) { - generateMethod(cfw, adapterName, methodName, - argTypes, method.getReturnType()); - generatedOverrides.put(methodKey, 0); - generatedMethods.put(methodName, 0); - } - } - } - - // Now, go through the superclass's methods, checking for abstract - // methods or additional methods to override. - - // generate any additional overrides that the object might contain. - Method[] methods = getOverridableMethods(superClass); - for (int j = 0; j < methods.length; j++) { - Method method = methods[j]; - int mods = method.getModifiers(); - // 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. - boolean isAbstractMethod = Modifier.isAbstract(mods); - String methodName = method.getName(); - if (isAbstractMethod || functionNames.has(methodName)) { - // make sure to generate only one instance of a particular - // method/signature. - Class[] argTypes = method.getParameterTypes(); - String methodSignature = getMethodSignature(method, argTypes); - String methodKey = methodName + methodSignature; - if (! generatedOverrides.has(methodKey)) { - generateMethod(cfw, adapterName, methodName, - argTypes, method.getReturnType()); - generatedOverrides.put(methodKey, 0); - generatedMethods.put(methodName, 0); - - // if a method was overridden, generate a "super$method" - // which lets the delegate call the superclass' version. - if (!isAbstractMethod) { - generateSuper(cfw, adapterName, superName, - methodName, methodSignature, - argTypes, method.getReturnType()); - } - } - } - } - - // Generate Java methods for remaining properties that are not - // overrides. - ObjToIntMap.Iterator iter = new ObjToIntMap.Iterator(functionNames); - for (iter.start(); !iter.done(); iter.next()) { - String functionName = (String)iter.getKey(); - if (generatedMethods.has(functionName)) - continue; - int length = iter.getValue(); - Class[] parms = new Class[length]; - for (int k=0; k < length; k++) - parms[k] = ScriptRuntime.ObjectClass; - generateMethod(cfw, adapterName, functionName, parms, - ScriptRuntime.ObjectClass); - } - return cfw.toByteArray(); - } - - static Method[] getOverridableMethods(Class c) - { - ArrayList<Method> list = new ArrayList<Method>(); - HashSet<String> skip = new HashSet<String>(); - while (c != null) { - Method[] methods = c.getDeclaredMethods(); - for (int i = 0; i < methods.length; i++) { - String methodKey = methods[i].getName() + - getMethodSignature(methods[i], - methods[i].getParameterTypes()); - if (skip.contains(methodKey)) - continue; // skip this method - int mods = methods[i].getModifiers(); - if (Modifier.isStatic(mods)) - continue; - if (Modifier.isFinal(mods)) { - // Make sure we don't add a final method to the list - // of overridable methods. - skip.add(methodKey); - continue; - } - if (Modifier.isPublic(mods) || Modifier.isProtected(mods)) { - list.add(methods[i]); - skip.add(methodKey); - } - } - c = c.getSuperclass(); - } - return list.toArray(new Method[list.size()]); - } - - static Class loadAdapterClass(String className, byte[] classBytes) - { - Object staticDomain; - Class domainClass = SecurityController.getStaticSecurityDomainClass(); - if(domainClass == CodeSource.class || domainClass == ProtectionDomain.class) { - ProtectionDomain protectionDomain = JavaAdapter.class.getProtectionDomain(); - if(domainClass == CodeSource.class) { - staticDomain = protectionDomain == null ? null : protectionDomain.getCodeSource(); - } - else { - staticDomain = protectionDomain; - } - } - else { - staticDomain = null; - } - GeneratedClassLoader loader = SecurityController.createLoader(null, - staticDomain); - Class result = loader.defineClass(className, classBytes); - loader.linkClass(result); - return result; - } - - public static Function getFunction(Scriptable obj, String functionName) - { - Object x = ScriptableObject.getProperty(obj, functionName); - if (x == Scriptable.NOT_FOUND) { - // This method used to swallow the exception from calling - // an undefined method. People have come to depend on this - // somewhat dubious behavior. It allows people to avoid - // implementing listener methods that they don't care about, - // for instance. - return null; - } - if (!(x instanceof Function)) - throw ScriptRuntime.notFunctionError(x, functionName); - - return (Function)x; - } - - /** - * Utility method which dynamically binds a Context to the current thread, - * if none already exists. - */ - public static Object callMethod(ContextFactory factory, - final Scriptable thisObj, - final Function f, final Object[] args, - final long argsToWrap) - { - if (f == null) { - // See comments in getFunction - return Undefined.instance; - } - if (factory == null) { - factory = ContextFactory.getGlobal(); - } - - final Scriptable scope = f.getParentScope(); - if (argsToWrap == 0) { - return Context.call(factory, f, scope, thisObj, args); - } - - Context cx = Context.getCurrentContext(); - if (cx != null) { - return doCall(cx, scope, thisObj, f, args, argsToWrap); - } else { - return factory.call(new ContextAction() { - public Object run(Context cx) - { - return doCall(cx, scope, thisObj, f, args, argsToWrap); - } - }); - } - } - - private static Object doCall(Context cx, Scriptable scope, - Scriptable thisObj, Function f, - Object[] args, long argsToWrap) - { - // Wrap the rest of objects - for (int i = 0; i != args.length; ++i) { - if (0 != (argsToWrap & (1 << i))) { - Object arg = args[i]; - if (!(arg instanceof Scriptable)) { - args[i] = cx.getWrapFactory().wrap(cx, scope, arg, - null); - } - } - } - return f.call(cx, scope, thisObj, args); - } - - public static Scriptable runScript(final Script script) - { - return (Scriptable)Context.call(new ContextAction() { - public Object run(Context cx) - { - ScriptableObject global = ScriptRuntime.getGlobal(cx); - script.exec(cx, global); - return global; - } - }); - } - - private static void generateCtor(ClassFileWriter cfw, String adapterName, - String superName) - { - cfw.startMethod("<init>", - "(Lorg/mozilla/javascript/ContextFactory;" - +"Lorg/mozilla/javascript/Scriptable;)V", - ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "<init>", "()V"); - - // Save parameter in instance variable "factory" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // first arg: ContextFactory instance - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // Save parameter in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_2); // second arg: Scriptable delegee - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.ALOAD_0); // this for the following PUTFIELD for self - // create a wrapper object to be used as "this" in method calls - cfw.add(ByteCode.ALOAD_2); // the Scriptable delegee - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "createAdapterWrapper", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/Object;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod((short)3); // 3: this + factory + delegee - } - - private static void generateSerialCtor(ClassFileWriter cfw, - String adapterName, - String superName) - { - cfw.startMethod("<init>", - "(Lorg/mozilla/javascript/ContextFactory;" - +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Scriptable;" - +")V", - ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "<init>", "()V"); - - // Save parameter in instance variable "factory" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // first arg: ContextFactory instance - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // Save parameter in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_2); // second arg: Scriptable delegee - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - // save self - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_3); // second arg: Scriptable self - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod((short)4); // 4: this + factory + delegee + self - } - - private static void generateEmptyCtor(ClassFileWriter cfw, - String adapterName, - String superName, - String scriptClassName) - { - cfw.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC); - - // Invoke base class constructor - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESPECIAL, superName, "<init>", "()V"); - - // Set factory to null to use current global when necessary - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.ACONST_NULL); - cfw.add(ByteCode.PUTFIELD, adapterName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // Load script class - cfw.add(ByteCode.NEW, scriptClassName); - cfw.add(ByteCode.DUP); - cfw.addInvoke(ByteCode.INVOKESPECIAL, scriptClassName, "<init>", "()V"); - - // Run script and save resulting scope - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "runScript", - "(Lorg/mozilla/javascript/Script;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.ASTORE_1); - - // Save the Scriptable in instance variable "delegee" - cfw.add(ByteCode.ALOAD_0); // this - cfw.add(ByteCode.ALOAD_1); // the Scriptable - cfw.add(ByteCode.PUTFIELD, adapterName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.ALOAD_0); // this for the following PUTFIELD for self - // create a wrapper object to be used as "this" in method calls - cfw.add(ByteCode.ALOAD_1); // the Scriptable - cfw.add(ByteCode.ALOAD_0); // this - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "createAdapterWrapper", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/Object;" - +")Lorg/mozilla/javascript/Scriptable;"); - cfw.add(ByteCode.PUTFIELD, adapterName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - cfw.add(ByteCode.RETURN); - cfw.stopMethod((short)2); // this + delegee - } - - /** - * Generates code to wrap Java arguments into Object[]. - * Non-primitive Java types are left as-is pending conversion - * in the helper method. Leaves the array object on the top of the stack. - */ - static void generatePushWrappedArgs(ClassFileWriter cfw, - Class[] argTypes, - int arrayLength) - { - // push arguments - cfw.addPush(arrayLength); - cfw.add(ByteCode.ANEWARRAY, "java/lang/Object"); - int paramOffset = 1; - for (int i = 0; i != argTypes.length; ++i) { - cfw.add(ByteCode.DUP); // duplicate array reference - cfw.addPush(i); - paramOffset += generateWrapArg(cfw, paramOffset, argTypes[i]); - cfw.add(ByteCode.AASTORE); - } - } - - /** - * Generates code to wrap Java argument into Object. - * Non-primitive Java types are left unconverted pending conversion - * in the helper method. Leaves the wrapper object on the top of the stack. - */ - private static int generateWrapArg(ClassFileWriter cfw, int paramOffset, - Class argType) - { - int size = 1; - if (!argType.isPrimitive()) { - cfw.add(ByteCode.ALOAD, paramOffset); - - } else if (argType == Boolean.TYPE) { - // wrap boolean values with java.lang.Boolean. - cfw.add(ByteCode.NEW, "java/lang/Boolean"); - cfw.add(ByteCode.DUP); - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Boolean", - "<init>", "(Z)V"); - - } else if (argType == Character.TYPE) { - // Create a string of length 1 using the character parameter. - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.addInvoke(ByteCode.INVOKESTATIC, "java/lang/String", - "valueOf", "(C)Ljava/lang/String;"); - - } else { - // convert all numeric values to java.lang.Double. - cfw.add(ByteCode.NEW, "java/lang/Double"); - cfw.add(ByteCode.DUP); - String typeName = argType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 's': - case 'i': - // load an int value, convert to double. - cfw.add(ByteCode.ILOAD, paramOffset); - cfw.add(ByteCode.I2D); - break; - case 'l': - // load a long, convert to double. - cfw.add(ByteCode.LLOAD, paramOffset); - cfw.add(ByteCode.L2D); - size = 2; - break; - case 'f': - // load a float, convert to double. - cfw.add(ByteCode.FLOAD, paramOffset); - cfw.add(ByteCode.F2D); - break; - case 'd': - cfw.add(ByteCode.DLOAD, paramOffset); - size = 2; - break; - } - cfw.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Double", - "<init>", "(D)V"); - } - return size; - } - - /** - * Generates code to convert a wrapped value type to a primitive type. - * Handles unwrapping java.lang.Boolean, and java.lang.Number types. - * Generates the appropriate RETURN bytecode. - */ - static void generateReturnResult(ClassFileWriter cfw, Class retType, - boolean callConvertResult) - { - // wrap boolean values with java.lang.Boolean, convert all other - // primitive values to java.lang.Double. - if (retType == Void.TYPE) { - cfw.add(ByteCode.POP); - cfw.add(ByteCode.RETURN); - - } else if (retType == Boolean.TYPE) { - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toBoolean", "(Ljava/lang/Object;)Z"); - cfw.add(ByteCode.IRETURN); - - } else if (retType == Character.TYPE) { - // characters are represented as strings in JavaScript. - // return the first character. - // first convert the value to a string if possible. - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toString", - "(Ljava/lang/Object;)Ljava/lang/String;"); - cfw.add(ByteCode.ICONST_0); - cfw.addInvoke(ByteCode.INVOKEVIRTUAL, "java/lang/String", - "charAt", "(I)C"); - cfw.add(ByteCode.IRETURN); - - } else if (retType.isPrimitive()) { - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/Context", - "toNumber", "(Ljava/lang/Object;)D"); - String typeName = retType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 's': - case 'i': - cfw.add(ByteCode.D2I); - cfw.add(ByteCode.IRETURN); - break; - case 'l': - cfw.add(ByteCode.D2L); - cfw.add(ByteCode.LRETURN); - break; - case 'f': - cfw.add(ByteCode.D2F); - cfw.add(ByteCode.FRETURN); - break; - case 'd': - cfw.add(ByteCode.DRETURN); - break; - default: - throw new RuntimeException("Unexpected return type " + - retType.toString()); - } - - } else { - String retTypeStr = retType.getName(); - if (callConvertResult) { - cfw.addLoadConstant(retTypeStr); - cfw.addInvoke(ByteCode.INVOKESTATIC, - "java/lang/Class", - "forName", - "(Ljava/lang/String;)Ljava/lang/Class;"); - - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "convertResult", - "(Ljava/lang/Object;" - +"Ljava/lang/Class;" - +")Ljava/lang/Object;"); - } - // Now cast to return type - cfw.add(ByteCode.CHECKCAST, retTypeStr); - cfw.add(ByteCode.ARETURN); - } - } - - private static void generateMethod(ClassFileWriter cfw, String genName, - String methodName, Class[] parms, - Class returnType) - { - StringBuffer sb = new StringBuffer(); - int paramsEnd = appendMethodSignature(parms, returnType, sb); - String methodSignature = sb.toString(); - cfw.startMethod(methodName, methodSignature, - ClassFileWriter.ACC_PUBLIC); - - // Prepare stack to call method - - // push factory - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "factory", - "Lorg/mozilla/javascript/ContextFactory;"); - - // push self - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "self", - "Lorg/mozilla/javascript/Scriptable;"); - - // push function - cfw.add(ByteCode.ALOAD_0); - cfw.add(ByteCode.GETFIELD, genName, "delegee", - "Lorg/mozilla/javascript/Scriptable;"); - cfw.addPush(methodName); - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "getFunction", - "(Lorg/mozilla/javascript/Scriptable;" - +"Ljava/lang/String;" - +")Lorg/mozilla/javascript/Function;"); - - // push arguments - generatePushWrappedArgs(cfw, parms, parms.length); - - // push bits to indicate which parameters should be wrapped - if (parms.length > 64) { - // If it will be an issue, then passing a static boolean array - // can be an option, but for now using simple bitmask - throw Context.reportRuntimeError0( - "JavaAdapter can not subclass methods with more then" - +" 64 arguments."); - } - long convertionMask = 0; - for (int i = 0; i != parms.length; ++i) { - if (!parms[i].isPrimitive()) { - convertionMask |= (1 << i); - } - } - cfw.addPush(convertionMask); - - // go through utility method, which creates a Context to run the - // method in. - cfw.addInvoke(ByteCode.INVOKESTATIC, - "org/mozilla/javascript/JavaAdapter", - "callMethod", - "(Lorg/mozilla/javascript/ContextFactory;" - +"Lorg/mozilla/javascript/Scriptable;" - +"Lorg/mozilla/javascript/Function;" - +"[Ljava/lang/Object;" - +"J" - +")Ljava/lang/Object;"); - - generateReturnResult(cfw, returnType, true); - - cfw.stopMethod((short)paramsEnd); - } - - /** - * Generates code to push typed parameters onto the operand stack - * prior to a direct Java method call. - */ - private static int generatePushParam(ClassFileWriter cfw, int paramOffset, - Class paramType) - { - if (!paramType.isPrimitive()) { - cfw.addALoad(paramOffset); - return 1; - } - String typeName = paramType.getName(); - switch (typeName.charAt(0)) { - case 'z': - case 'b': - case 'c': - case 's': - case 'i': - // load an int value, convert to double. - cfw.addILoad(paramOffset); - return 1; - case 'l': - // load a long, convert to double. - cfw.addLLoad(paramOffset); - return 2; - case 'f': - // load a float, convert to double. - cfw.addFLoad(paramOffset); - return 1; - case 'd': - cfw.addDLoad(paramOffset); - return 2; - } - throw Kit.codeBug(); - } - - /** - * Generates code to return a Java type, after calling a Java method - * that returns the same type. - * Generates the appropriate RETURN bytecode. - */ - private static void generatePopResult(ClassFileWriter cfw, - Class retType) - { - if (retType.isPrimitive()) { - String typeName = retType.getName(); - switch (typeName.charAt(0)) { - case 'b': - case 'c': - case 's': - case 'i': - case 'z': - cfw.add(ByteCode.IRETURN); - break; - case 'l': - cfw.add(ByteCode.LRETURN); - break; - case 'f': - cfw.add(ByteCode.FRETURN); - break; - case 'd': - cfw.add(ByteCode.DRETURN); - break; - } - } else { - cfw.add(ByteCode.ARETURN); - } - } - - /** - * 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, - Class[] parms, Class returnType) - { - cfw.startMethod("super$" + methodName, methodSignature, - ClassFileWriter.ACC_PUBLIC); - - // push "this" - cfw.add(ByteCode.ALOAD, 0); - - // push the rest of the parameters. - int paramOffset = 1; - for (int i = 0; i < parms.length; i++) { - paramOffset += generatePushParam(cfw, paramOffset, parms[i]); - } - - // call the superclass implementation of the method. - cfw.addInvoke(ByteCode.INVOKESPECIAL, - superName, - methodName, - methodSignature); - - // now, handle the return type appropriately. - Class retType = returnType; - if (!retType.equals(Void.TYPE)) { - generatePopResult(cfw, retType); - } else { - cfw.add(ByteCode.RETURN); - } - cfw.stopMethod((short)(paramOffset + 1)); - } - - /** - * Returns a fully qualified method name concatenated with its signature. - */ - private static String getMethodSignature(Method method, Class[] argTypes) - { - StringBuffer sb = new StringBuffer(); - appendMethodSignature(argTypes, method.getReturnType(), sb); - return sb.toString(); - } - - static int appendMethodSignature(Class[] argTypes, - Class returnType, - StringBuffer sb) - { - sb.append('('); - int firstLocal = 1 + argTypes.length; // includes this. - for (int i = 0; i < argTypes.length; i++) { - Class type = argTypes[i]; - appendTypeString(sb, type); - if (type == Long.TYPE || type == Double.TYPE) { - // adjust for duble slot - ++firstLocal; - } - } - sb.append(')'); - appendTypeString(sb, returnType); - return firstLocal; - } - - private static StringBuffer appendTypeString(StringBuffer sb, Class type) - { - while (type.isArray()) { - sb.append('['); - type = type.getComponentType(); - } - if (type.isPrimitive()) { - char typeLetter; - if (type == Boolean.TYPE) { - typeLetter = 'Z'; - } else if (type == Long.TYPE) { - typeLetter = 'J'; - } else { - String typeName = type.getName(); - typeLetter = Character.toUpperCase(typeName.charAt(0)); - } - sb.append(typeLetter); - } else { - sb.append('L'); - sb.append(type.getName().replace('.', '/')); - sb.append(';'); - } - return sb; - } - - static int[] getArgsToConvert(Class[] argTypes) - { - int count = 0; - for (int i = 0; i != argTypes.length; ++i) { - if (!argTypes[i].isPrimitive()) - ++count; - } - if (count == 0) - return null; - int[] array = new int[count]; - count = 0; - for (int i = 0; i != argTypes.length; ++i) { - if (!argTypes[i].isPrimitive()) - array[count++] = i; - } - return array; - } - - private static final Object FTAG = new Object(); - private static final int Id_JavaAdapter = 1; -} |