/** * 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); } }); } }
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); }
// 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 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); }
private static void generateMethod( ClassFileWriter cfw, String genName, String methodName, Class<?>[] parms, Class<?> returnType, boolean convertResult) { StringBuilder sb = new StringBuilder(); 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", "Laurora/javascript/ContextFactory;"); // push self cfw.add(ByteCode.ALOAD_0); cfw.add(ByteCode.GETFIELD, genName, "self", "Laurora/javascript/Scriptable;"); // push function cfw.add(ByteCode.ALOAD_0); cfw.add(ByteCode.GETFIELD, genName, "delegee", "Laurora/javascript/Scriptable;"); cfw.addPush(methodName); cfw.addInvoke( ByteCode.INVOKESTATIC, "aurora/javascript/JavaAdapter", "getFunction", "(Laurora/javascript/Scriptable;" + "Ljava/lang/String;" + ")Laurora/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, "aurora/javascript/JavaAdapter", "callMethod", "(Laurora/javascript/ContextFactory;" + "Laurora/javascript/Scriptable;" + "Laurora/javascript/Function;" + "[Ljava/lang/Object;" + "J" + ")Ljava/lang/Object;"); generateReturnResult(cfw, returnType, convertResult); cfw.stopMethod((short) paramsEnd); }
static Object js_createAdapter(Context cx, Scriptable scope, Object[] args) { int N = args.length; if (N == 0) { throw ScriptRuntime.typeError0("msg.adapter.zero.args"); } // Expected arguments: // Any number of NativeJavaClass objects representing the super-class // and/or interfaces to implement, followed by one NativeObject providing // the implementation, followed by any number of arguments to pass on // to the (super-class) constructor. int classCount; for (classCount = 0; classCount < N - 1; classCount++) { Object arg = args[classCount]; // We explicitly test for NativeObject here since checking for // instanceof ScriptableObject or !(instanceof NativeJavaClass) // would fail for a Java class that isn't found in the class path // as NativeJavaPackage extends ScriptableObject. if (arg instanceof NativeObject) { break; } if (!(arg instanceof NativeJavaClass)) { throw ScriptRuntime.typeError2( "msg.not.java.class.arg", String.valueOf(classCount), ScriptRuntime.toString(arg)); } } Class<?> superClass = null; Class<?>[] intfs = new Class[classCount]; int interfaceCount = 0; for (int i = 0; i < classCount; ++i) { Class<?> c = ((NativeJavaClass) args[i]).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); // next argument is implementation, must be scriptable Scriptable obj = ScriptableObject.ensureScriptable(args[classCount]); Class<?> adapterClass = getAdapterClass(scope, superClass, interfaces, obj); Object adapter; int argsCount = N - classCount - 1; try { if (argsCount > 0) { // Arguments contain parameters for super-class constructor. // We use the generic Java method lookup logic to find and // invoke the right constructor. Object[] ctorArgs = new Object[argsCount + 2]; ctorArgs[0] = obj; ctorArgs[1] = cx.getFactory(); System.arraycopy(args, classCount + 1, ctorArgs, 2, argsCount); // TODO: cache class wrapper? NativeJavaClass classWrapper = new NativeJavaClass(scope, adapterClass, true); NativeJavaMethod ctors = classWrapper.members.ctors; int index = ctors.findCachedFunction(cx, ctorArgs); if (index < 0) { String sig = NativeJavaMethod.scriptSignature(args); throw Context.reportRuntimeError2("msg.no.java.ctor", adapterClass.getName(), sig); } // Found the constructor, so try invoking it. adapter = NativeJavaClass.constructInternal(ctorArgs, ctors.methods[index]); } else { Class<?>[] ctorParms = {ScriptRuntime.ScriptableClass, ScriptRuntime.ContextFactoryClass}; Object[] ctorArgs = {obj, cx.getFactory()}; adapter = adapterClass.getConstructor(ctorParms).newInstance(ctorArgs); } Object self = getAdapterSelf(adapterClass, adapter); // Return unwrapped JavaAdapter if it implements Scriptable if (self instanceof Wrapper) { Object unwrapped = ((Wrapper) self).unwrap(); if (unwrapped instanceof Scriptable) { if (unwrapped instanceof ScriptableObject) { ScriptRuntime.setObjectProtoAndParent((ScriptableObject) unwrapped, scope); } return unwrapped; } } return self; } catch (Exception ex) { throw Context.throwAsScriptRuntimeEx(ex); } }