/** * Returns the null value for the specific type * * @param type the type of the parameter for which the null value is required * @return the null value for the specific type */ Object getNullValueForType(Class type) { if (!type.isPrimitive()) { return null; } if (type == Boolean.TYPE) { return Boolean.FALSE; } if (type == Character.TYPE) { return new Character((char) 0); } if (type == Byte.TYPE) { return new Byte((byte) 0); } if (type == Short.TYPE) { return new Short((short) 0); } if (type == Integer.TYPE) { return new Integer(0); } if (type == Long.TYPE) { return new Long(0L); } if (type == Float.TYPE) { return new Float(0f); } if (type == Double.TYPE) { return new Double(0d); } fail("Don't know how to handle type " + type); return null; // unreachable statement }
/** * 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 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(); }
public Class[] getValidAnnotationTypes(Class type) { Class[] valid_types; if (Buffer.class.isAssignableFrom(type)) valid_types = getValidBufferTypes(type); else if (type.isPrimitive()) valid_types = getValidPrimitiveTypes(type); else if (String.class.equals(type)) valid_types = new Class[] {GLubyte.class}; else valid_types = new Class[] {}; return valid_types; }
/** Write a {@link Writable}, {@link String}, primitive type, or an array of the preceding. */ public static void writeObject( DataOutput out, Object instance, Class declaredClass, Configuration conf) throws IOException { if (instance == null) { // null instance = new NullInstance(declaredClass, conf); declaredClass = Writable.class; } UTF8.writeString(out, declaredClass.getName()); // always write declared if (declaredClass.isArray()) { // array int length = Array.getLength(instance); out.writeInt(length); for (int i = 0; i < length; i++) { writeObject(out, Array.get(instance, i), declaredClass.getComponentType(), conf); } } else if (declaredClass == String.class) { // String UTF8.writeString(out, (String) instance); } else if (declaredClass.isPrimitive()) { // primitive type if (declaredClass == Boolean.TYPE) { // boolean out.writeBoolean(((Boolean) instance).booleanValue()); } else if (declaredClass == Character.TYPE) { // char out.writeChar(((Character) instance).charValue()); } else if (declaredClass == Byte.TYPE) { // byte out.writeByte(((Byte) instance).byteValue()); } else if (declaredClass == Short.TYPE) { // short out.writeShort(((Short) instance).shortValue()); } else if (declaredClass == Integer.TYPE) { // int out.writeInt(((Integer) instance).intValue()); } else if (declaredClass == Long.TYPE) { // long out.writeLong(((Long) instance).longValue()); } else if (declaredClass == Float.TYPE) { // float out.writeFloat(((Float) instance).floatValue()); } else if (declaredClass == Double.TYPE) { // double out.writeDouble(((Double) instance).doubleValue()); } else if (declaredClass == Void.TYPE) { // void } else { throw new IllegalArgumentException("Not a primitive: " + declaredClass); } } else if (declaredClass.isEnum()) { // enum UTF8.writeString(out, ((Enum) instance).name()); } else if (Writable.class.isAssignableFrom(declaredClass)) { // Writable UTF8.writeString(out, instance.getClass().getName()); ((Writable) instance).write(out); } else { throw new IOException("Can't write: " + instance + " as " + declaredClass); } }
/** * Returns compact class host. * * @param obj Object to compact. * @return String. */ @Nullable public static Object compactObject(Object obj) { if (obj == null) return null; if (obj instanceof Enum) return obj.toString(); if (obj instanceof String || obj instanceof Boolean || obj instanceof Number) return obj; if (obj instanceof Collection) { Collection col = (Collection) obj; Object[] res = new Object[col.size()]; int i = 0; for (Object elm : col) res[i++] = compactObject(elm); return res; } if (obj.getClass().isArray()) { Class<?> arrType = obj.getClass().getComponentType(); if (arrType.isPrimitive()) { if (obj instanceof boolean[]) return Arrays.toString((boolean[]) obj); if (obj instanceof byte[]) return Arrays.toString((byte[]) obj); if (obj instanceof short[]) return Arrays.toString((short[]) obj); if (obj instanceof int[]) return Arrays.toString((int[]) obj); if (obj instanceof long[]) return Arrays.toString((long[]) obj); if (obj instanceof float[]) return Arrays.toString((float[]) obj); if (obj instanceof double[]) return Arrays.toString((double[]) obj); } Object[] arr = (Object[]) obj; int iMax = arr.length - 1; StringBuilder sb = new StringBuilder("["); for (int i = 0; i <= iMax; i++) { sb.append(compactObject(arr[i])); if (i != iMax) sb.append(", "); } sb.append("]"); return sb.toString(); } return U.compact(obj.getClass().getName()); }
/** * 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; }
static String[] c_signature(Class c, String expr) { if (c.isPrimitive()) return strs("j" + c.toString(), expr, expr); if (c == Pointer.class) return strs( "void*", "createPointerFromIO(env, " + expr + ", NULL)", "getPointerPeer(env, " + expr + ")"); // TODO callIO if (c == CLong.class) return strs("long", "BoxCLong(env, " + expr + ")", "UnBoxCLong(env, " + expr + ")"); if (c == SizeT.class) return strs("size_t", "BoxSizeT(env, " + expr + ")", "UnBoxSizeT(env, " + expr + ")"); if (c == TimeT.class) return strs("time_t", "BoxTimeT(env, " + expr + ")", "UnBoxTimeT(env, " + expr + ")"); throw new UnsupportedOperationException("Cannot compute C signature for " + c.getName()); }
/** * Used by the parser to create a new array with elements of the given class and store it in the * master objects table under the given ID. * * @param objID A unique ID used to index objects in the master object table. * @param cls The class to instantiate. * @param size The number of elements in the array. * @param depth The number of levels of indirection. * @return The newly instantiated array. */ public Object newArrayInstance(Long objID, Class cls, int size, int depth) throws ParseException { if (pMasterTable.containsKey(objID)) throw new ParseException("Duplicate object ID (" + objID + " encountered!"); Object obj = null; try { if (depth == 1) { obj = Array.newInstance(cls, size); } else if (depth > 1) { StringBuilder buf = new StringBuilder(); int wk; for (wk = 1; wk < depth; wk++) buf.append("["); if (cls.isPrimitive()) { if (cls == Boolean.TYPE) buf.append("Z"); else if (cls == Byte.TYPE) buf.append("B"); else if (cls == Short.TYPE) buf.append("S"); else if (cls == Integer.TYPE) buf.append("I"); else if (cls == Long.TYPE) buf.append("J"); else if (cls == Float.TYPE) buf.append("F"); else if (cls == Double.TYPE) buf.append("D"); else if (cls == Character.TYPE) buf.append("C"); else throw new IllegalStateException("Unknown array type (" + cls + ")!"); } else { buf.append("L" + cls.getName() + ";"); } Class acls = Class.forName(buf.toString()); obj = Array.newInstance(acls, size); } else { throw new NegativeArraySizeException("Array depth (" + depth + ") must be positive!"); } } catch (Exception ex) { throw new ParseException(ex.toString()); } pMasterTable.put(objID, obj); return obj; }
void generateFindMethodAndArgs(Method method, IndentedWriter iw) throws IOException { iw.println("Class[] argTypes = "); iw.println("{"); iw.upIndent(); Class[] argTypes = method.getParameterTypes(); for (int i = 0, len = argTypes.length; i < len; ++i) { if (i != 0) iw.println(","); iw.print(CodegenUtils.simpleClassName(argTypes[i]) + ".class"); } iw.println(); iw.downIndent(); iw.println("};"); iw.println( "Method method = Connection.class.getMethod( \042" + method.getName() + "\042 , argTypes );"); iw.println(); iw.println("Object[] args = "); iw.println("{"); iw.upIndent(); for (int i = 0, len = argTypes.length; i < len; ++i) { if (i != 0) iw.println(","); String argName = CodegenUtils.generatedArgumentName(i); Class argType = argTypes[i]; if (argType.isPrimitive()) { if (argType == boolean.class) iw.print("Boolean.valueOf( " + argName + " )"); else if (argType == byte.class) iw.print("new Byte( " + argName + " )"); else if (argType == char.class) iw.print("new Character( " + argName + " )"); else if (argType == short.class) iw.print("new Short( " + argName + " )"); else if (argType == int.class) iw.print("new Integer( " + argName + " )"); else if (argType == long.class) iw.print("new Long( " + argName + " )"); else if (argType == float.class) iw.print("new Float( " + argName + " )"); else if (argType == double.class) iw.print("new Double( " + argName + " )"); } else iw.print(argName); } iw.downIndent(); iw.println("};"); }
private static void writeValue(Object value, OutputStream os) throws IOException { if (value == null) { os.write(-1); } else { Class valueClass = value.getClass(); // write 0 for a single value, 1 for array, and 2 for Vector if (valueClass.isArray()) { os.write(1); int length = Array.getLength(value); PDataStream.writeInt(length, os); Class componentType = valueClass.getComponentType(); if (componentType.isPrimitive()) { // primitive PDataStream.writeBoolean(true, os); // 1: int;2: long; 3: byte; 4: boolean; 5: character; 6: // short; 7: float; 8: double writePrimitiveArray(componentType, value, length, os); } else { PDataStream.writeBoolean(false, os); PDataStream.writeUTF(componentType.getName(), os); Object[] oArr = (Object[]) value; for (int i = 0; i < length; i++) { writeValue(oArr[i], os); } } } else if (valueClass.equals(Vector.class)) { os.write(2); int size = ((Vector) value).size(); PDataStream.writeInt(size, os); for (int i = 0; i < size; i++) { writeValue(((Vector) value).elementAt(i), os); } } else { os.write(0); writeRealObject(value, valueClass, os); } } }
private static StringBuilder appendTypeString(StringBuilder 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; }
/** Mimics SimpleXML's naming for classes without {@link Root#name()}. */ private static String getClassName(Class<?> type) { if (type.isArray()) type = type.getComponentType(); final String name = type.getSimpleName(); if (type.isPrimitive()) return name; else return Introspector.decapitalize(name); }
/** * Checks all of the classes in the serialization scope that implement TetradSerializable to make * sure all of their fields are either themselves (a) primitive, (b) TetradSerializable, or (c) * assignable from types designated as safely serializable by virtue of being included in the * safelySerializableTypes array (see), or are arrays whose lowest order component types satisfy * either (a), (b), or (c). Safely serializable classes in the Java API currently include * collections classes, plus String and Class. Collections classes are included, since their types * will be syntactically checkable in JDK 1.5. String and Class are members of a broader type of * Class whose safely can by checked by making sure there is no way to pass into them via * constructor or method argument any object that is not TetradSerializable or safely * serializable. But it's easy enough now to just make a list. * * @see #safelySerializableTypes */ public void checkNestingOfFields() { List classes = getAssignableClasses(new File(getSerializableScope()), TetradSerializable.class); boolean foundUnsafeField = false; for (Object aClass : classes) { Class clazz = (Class) aClass; if (TetradSerializableExcluded.class.isAssignableFrom(clazz)) { continue; } Field[] fields = clazz.getDeclaredFields(); FIELDS: for (Field field : fields) { // System.out.println(field); if (Modifier.isTransient(field.getModifiers())) { continue; } if (Modifier.isStatic(field.getModifiers())) { continue; } Class type = field.getType(); while (type.isArray()) { type = type.getComponentType(); } if (type.isPrimitive()) { continue; } if (type.isEnum()) { continue; } // // Printing out Collections fields temporarily. // if (Collection.class.isAssignableFrom(type)) { // System.out.println("COLLECTION FIELD: " + field); // } // // if (Map.class.isAssignableFrom(type)) { // System.out.println("MAP FIELD: " + field); // } if (TetradSerializable.class.isAssignableFrom(type) && !TetradSerializableExcluded.class.isAssignableFrom(clazz)) { continue; } for (Class safelySerializableClass : safelySerializableTypes) { if (safelySerializableClass.isAssignableFrom(type)) { continue FIELDS; } } // A reference in an inner class to the outer class. if (field.getName().equals("this$0")) { continue; } System.out.println("UNSAFE FIELD:" + field); foundUnsafeField = true; } } if (foundUnsafeField) { throw new RuntimeException("Unsafe serializable fields found. Please " + "fix immediately."); } }
/** * Process and loop until told to stop. A callback_done will stop the loop and will return a * result. Otherwise null is returned. */ public Object run() throws CommandException { Object result = null; boolean shutdown = false; boolean closeWhenDone = true; Commands.ValueObject valueObject = new Commands.ValueObject(); // Working value object so not continually recreated. InvokableValueSender valueSender = new InvokableValueSender(); // Working valuesender so not continually recreated. try { boolean doLoop = true; /** * Note: In the cases below you will see a lot of finally clauses that null variables out. * This is because this is a long running loop, and variables declared within blocks are not * garbage collected until the method is terminated, so these variables once set would never * be GC'd. The nulling at the end of the case makes sure that any of those objects set are * now available for garbage collection when necessary. */ while (doLoop && isConnected()) { byte cmd = 0; try { if (LINUX_1_3) socket.setSoTimeout(1000); // Linux 1.3 bug, see comment on LINUX_1_3 cmd = in.readByte(); if (LINUX_1_3 && isConnected()) socket.setSoTimeout(0); // Linux 1.3 bug, see comment on LINUX_1_3 } catch (InterruptedIOException e) { continue; // Timeout, try again } switch (cmd) { case Commands.QUIT_CONNECTION: doLoop = false; break; // Close this connection case Commands.TERMINATE_SERVER: doLoop = false; shutdown = true; // Shutdown everything break; case Commands.GET_CLASS: String className = in.readUTF(); Class aClass = null; Class superClass = null; String superClassName = null; boolean added = false; try { aClass = Class.forName( className); // Turns out using JNI format for array type will work fine. added = server.getIdentityID(aClass, valueObject); boolean isInterface = aClass.isInterface(); boolean isAbstract = java.lang.reflect.Modifier.isAbstract(aClass.getModifiers()); superClass = aClass.getSuperclass(); superClassName = (superClass != null) ? superClass.getName() : ""; // $NON-NLS-1$ out.writeByte(Commands.GET_CLASS_RETURN); out.writeInt(valueObject.objectID); out.writeBoolean(isInterface); out.writeBoolean(isAbstract); out.writeUTF(superClassName); out.flush(); } catch (ClassNotFoundException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.GET_CLASS_NOT_FOUND, valueObject); } catch (ExceptionInInitializerError e) { sendException(e.getException(), valueObject, out); } catch (LinkageError e) { sendException(e, valueObject, out); } catch (Throwable e) { // Something bad, did we add a class? If we did remove it from the table. if (added) server.removeObject(server.getObject(valueObject.objectID)); throw e; } finally { // clear out for GC to work. className = null; aClass = null; superClass = null; superClassName = null; valueObject.set(); } break; case Commands.GET_CLASS_FROM_ID: int classID = in.readInt(); try { aClass = (Class) server.getObject(classID); boolean isInterface = aClass.isInterface(); boolean isAbstract = java.lang.reflect.Modifier.isAbstract(aClass.getModifiers()); superClass = aClass.getSuperclass(); superClassName = (superClass != null) ? superClass.getName() : ""; // $NON-NLS-1$ out.writeByte(Commands.GET_CLASS_ID_RETURN); out.writeUTF(aClass.getName()); out.writeBoolean(isInterface); out.writeBoolean(isAbstract); out.writeUTF(superClassName); out.flush(); } catch (ClassCastException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); } finally { // clear out for GC to work. aClass = null; superClass = null; superClassName = null; valueObject.set(); } break; case Commands.GET_OBJECT_DATA: int objectID = in.readInt(); Object anObject = null; try { anObject = server.getObject(objectID); valueObject.setObjectID(objectID, server.getIdentityID(anObject.getClass())); Commands.writeValue(out, valueObject, true); } catch (ClassCastException e) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); } finally { anObject = null; // Clear out for GC to work valueObject.set(); } break; case Commands.RELEASE_OBJECT: int id = in.readInt(); server.removeObject(server.getObject(id)); break; case Commands.NEW_INIT_STRING: classID = in.readInt(); // ID Of class to do new upon. String initString = in.readUTF(); // The init string. Object newValue = null; Class theClass = null; try { theClass = (Class) server.getObject(classID); if (theClass == null) { // The class wasn't found. So imply ClassNotFound exception. throw new ClassNotFoundException(); } InitializationStringParser parser = null; try { parser = InitializationStringParser.createParser(initString); newValue = parser.evaluate(); boolean primitive = parser.isPrimitive(); // If expected class is Void.TYPE, that means don't test the type of the result // to verify if correct type, just return what it really is. if (theClass != Void.TYPE && primitive != theClass.isPrimitive()) { valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command. } if (primitive) { try { // Need to do special tests for compatibility and assignment. sendObject( newValue, classID != Commands.VOID_TYPE ? classID : server.getIdentityID(parser.getExpectedType()), valueObject, out, true); // This will make sure it goes out as the correct primitive type } catch (ClassCastException e) { // The returned type is not of the correct type for what is expected. valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command } } else { if (newValue != null) { // Test to see if they are compatible. (Null can be assigned to any object, // so that is why it was tested out above). // If expected class is Void.TYPE, that means don't test the type of the result // to verify if correct type, just return what it really is. if (theClass != Void.TYPE && !theClass.isInstance(newValue)) { // The returned type is not of the correct type for what is expected. valueObject.set(); Commands.sendErrorCommand(out, Commands.CLASS_CAST_EXCEPTION, valueObject); continue; // Goto next command } } sendObject( newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Send out as an object. } } catch (InitializationStringEvaluationException e) { if (e instanceof EvaluationException) { // Want to return the real exception. sendException(e.getOriginalException(), valueObject, out); } else { // Couldn't be evaluated, return an error for this. setExceptionIntoValue(e.getOriginalException(), valueObject); Commands.sendErrorCommand(out, Commands.CANNOT_EVALUATE_STRING, valueObject); } } finally { parser = null; // Clear out for GC to work } } catch (Throwable e) { sendException(e, valueObject, out); } finally { // Clear out for GC to work initString = null; theClass = null; newValue = null; valueObject.set(); } break; case Commands.INVOKE: Object target = null; Object[] parms = null; Class returnType = null; java.lang.reflect.Method aMethod = null; try { int methodID = in.readInt(); // ID of method to invoke aMethod = (java.lang.reflect.Method) server.getObject(methodID); // Method to invoke Commands.readValue(in, valueObject); target = getInvokableObject(valueObject); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parms = (Object[]) valueSender.getArray(); } else { // It is all objects or null, so it should be an Object[] or null. If not, then this // is an error. parms = (Object[]) valueObject.anObject; } if (!aMethod.isAccessible()) aMethod.setAccessible( true); // We will allow all to occur. Let access control be handled by IDE and // compiler. newValue = aMethod.invoke(target, parms); returnType = aMethod.getReturnType(); if (returnType.isPrimitive()) { int returnTypeID = server.getIdentityID(returnType); // Need to tell sendObject the correct primitive type. sendObject(newValue, returnTypeID, valueObject, out, true); } else { sendObject( newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Just send the object back. sendObject knows how to iterpret the type } } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. // These should end the thread. } catch (java.lang.reflect.InvocationTargetException e) { // This is a wrappered exception. Return the wrappered one so it looks like // it was the real one. (Sometimes the method being invoked is on a // java.lang.reflect.Constructor.newInstance, // which in turn is an InvocationTargetException, so we will go until we don't have an // InvocationTargetException. Throwable t = e; do { t = ((java.lang.reflect.InvocationTargetException) t).getTargetException(); } while (t instanceof java.lang.reflect.InvocationTargetException); sendException(t, valueObject, out); } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { // Clear out for GC to work valueObject.set(); parms = null; target = null; aMethod = null; returnType = null; newValue = null; valueSender.clear(); } break; case Commands.INVOKE_WITH_METHOD_PASSED: aClass = null; String methodName = null; Class[] parmTypes = null; target = null; parms = null; returnType = null; aMethod = null; try { Commands.readValue(in, valueObject); aClass = (Class) getInvokableObject(valueObject); // The class that has the method. methodName = in.readUTF(); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parmTypes = (Class[]) valueSender.getArray(); } else { // It null, so it should be an null. If not, then this is an error. parmTypes = null; } aMethod = aClass.getMethod(methodName, parmTypes); // Now we get the info for the invocation of the method and execute it. Commands.readValue(in, valueObject); target = getInvokableObject(valueObject); Commands.readValue(in, valueObject); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); parms = (Object[]) valueSender.getArray(); } else { // It is all objects or null, so it should be an Object[] or null. If not, then this // is an error. parms = (Object[]) valueObject.anObject; } if (!aMethod.isAccessible()) aMethod.setAccessible( true); // We will allow all to occur. Let access control be handled by IDE and // compiler. newValue = aMethod.invoke(target, parms); returnType = aMethod.getReturnType(); if (returnType.isPrimitive()) { int returnTypeID = server.getIdentityID(returnType); // Need to tell sendObject the correct primitive type. sendObject(newValue, returnTypeID, valueObject, out, true); } else { sendObject( newValue, NOT_A_PRIMITIVE, valueObject, out, true); // Just send the object back. sendObject knows how to iterpret the type } } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. // These should end the thread. } catch (java.lang.reflect.InvocationTargetException e) { // This is a wrappered exception. Return the wrappered one so it looks like // it was the real one. (Sometimes the method being invoked is on a // java.lang.reflect.Constructor.newInstance, // which in turn is an InvocationTargetException, so we will go until we don't have an // InvocationTargetException. Throwable t = e; do { t = ((java.lang.reflect.InvocationTargetException) t).getTargetException(); } while (t instanceof java.lang.reflect.InvocationTargetException); sendException(t, valueObject, out); } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { aClass = null; methodName = null; parmTypes = null; // Clear out for GC to work valueObject.set(); parms = null; target = null; aMethod = null; returnType = null; newValue = null; valueSender.clear(); } break; case Commands.GET_ARRAY_CONTENTS: try { target = server.getObject(in.readInt()); // Array to get the ids for. valueObject.setArrayIDS( new ArrayContentsRetriever(target), Array.getLength(target), Commands.OBJECT_CLASS); Commands.writeValue(out, valueObject, true); // Write it back as a value command. } catch (CommandException e) { throw e; // Throw it again. These we don't want to come up as an exception proxy. // These should end the thread. } catch (Throwable e) { sendException(e, valueObject, out); // Turn it into a exception proxy on the client. } finally { target = null; valueObject.set(); } break; case Commands.CALLBACK_DONE: try { if (connectionThread != null) { valueObject.set(); Commands.sendErrorCommand(out, Commands.UNKNOWN_COMMAND_SENT, valueObject); } else { try { Commands.readBackValue(in, valueObject, Commands.NO_TYPE_CHECK); if (valueObject.type == Commands.ARRAY_IDS) { // It is an array containing IDs, as it normally would be. valueSender.initialize(valueObject); Commands.readArray(in, valueObject.anInt, valueSender, valueObject, false); result = valueSender.getArray(); } else { result = getInvokableObject(valueObject); } doLoop = false; // We need to terminate and return result closeWhenDone = false; // Don't close, we will continue. } catch (CommandErrorException e) { // There was an command error on the other side. This means // connection still good, but don't continue the callback processing. doLoop = false; // We need to terminate and return result closeWhenDone = false; // Don't close, we will continue. throw e; // Let it go on out. } } } finally { valueObject.set(); valueSender.clear(); } break; case Commands.ERROR: try { // Got an error command. Don't know what to do but read the // value and simply print it out. Commands.readValue(in, valueObject); result = getInvokableObject(valueObject); System.out.println("Error sent to server: Result=" + result); // $NON-NLS-1$ } finally { valueObject.set(); } break; case Commands.EXPRESSION_TREE_COMMAND: try { processExpressionCommand(valueObject, valueSender); } finally { valueObject.set(); valueSender.clear(); } break; default: // Unknown command. We don't know how long it is, so we need to shut the connection // down. System.err.println("Error: Invalid cmd send to server: Cmd=" + cmd); // $NON-NLS-1$ doLoop = false; closeWhenDone = true; break; } } } catch (EOFException e) { // This is ok. It means that the connection on the other side was terminated. // So just accept this and go down. } catch (CommandException e) { throw e; } catch (SocketException e) { if (socket != null) throw new UnexpectedExceptionCommandException( false, e); // socket null means a valid close request } catch (Throwable e) { e.printStackTrace(); } finally { if (closeWhenDone) { try { for (Iterator itr = expressionProcessors.values().iterator(); itr.hasNext(); ) { ExpressionProcesserController exp = (ExpressionProcesserController) itr.next(); exp.close(); } } finally { expressionProcessors.clear(); } if (in != null) try { in.close(); } catch (Exception e) { } in = null; if (out != null) try { out.close(); } catch (Exception e) { } out = null; close(); } } if (closeWhenDone && connectionThread != null) server.removeConnectionThread(connectionThread); if (shutdown) server.requestShutdown(); return result; }
/** * Derive a ranking based on how "natural" the conversion is. The special value CONVERSION_NONE * means no conversion is possible, and CONVERSION_NONTRIVIAL signals that more type conformance * testing is required. Based on <a * href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">"preferred method * conversions" from Live Connect 3</a> */ static int getConversionWeight(Object fromObj, Class<?> to) { int fromCode = getJSTypeCode(fromObj); switch (fromCode) { case JSTYPE_UNDEFINED: if (to == ScriptRuntime.StringClass || to == ScriptRuntime.ObjectClass) { return 1; } break; case JSTYPE_NULL: if (!to.isPrimitive()) { return 1; } break; case JSTYPE_BOOLEAN: // "boolean" is #1 if (to == Boolean.TYPE) { return 1; } else if (to == ScriptRuntime.BooleanClass) { return 2; } else if (to == ScriptRuntime.ObjectClass) { return 3; } else if (to == ScriptRuntime.StringClass) { return 4; } break; case JSTYPE_NUMBER: if (to.isPrimitive()) { if (to == Double.TYPE) { return 1; } else if (to != Boolean.TYPE) { return 1 + getSizeRank(to); } } else { if (to == ScriptRuntime.StringClass) { // native numbers are #1-8 return 9; } else if (to == ScriptRuntime.ObjectClass) { return 10; } else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) { // "double" is #1 return 2; } } break; case JSTYPE_STRING: if (to == ScriptRuntime.StringClass) { return 1; } else if (to.isInstance(fromObj)) { return 2; } else if (to.isPrimitive()) { if (to == Character.TYPE) { return 3; } else if (to != Boolean.TYPE) { return 4; } } break; case JSTYPE_JAVA_CLASS: if (to == ScriptRuntime.ClassClass) { return 1; } else if (to == ScriptRuntime.ObjectClass) { return 3; } else if (to == ScriptRuntime.StringClass) { return 4; } break; case JSTYPE_JAVA_OBJECT: case JSTYPE_JAVA_ARRAY: Object javaObj = fromObj; if (javaObj instanceof Wrapper) { javaObj = ((Wrapper) javaObj).unwrap(); } if (to.isInstance(javaObj)) { return CONVERSION_NONTRIVIAL; } if (to == ScriptRuntime.StringClass) { return 2; } else if (to.isPrimitive() && to != Boolean.TYPE) { return (fromCode == JSTYPE_JAVA_ARRAY) ? CONVERSION_NONE : 2 + getSizeRank(to); } break; case JSTYPE_OBJECT: // Other objects takes #1-#3 spots if (to == fromObj.getClass()) { // No conversion required return 1; } if (to.isArray()) { if (fromObj instanceof NativeArray) { // This is a native array conversion to a java array // Array conversions are all equal, and preferable to object // and string conversion, per LC3. return 1; } } else if (to == ScriptRuntime.ObjectClass) { return 2; } else if (to == ScriptRuntime.StringClass) { return 3; } else if (to == ScriptRuntime.DateClass) { if (fromObj instanceof NativeDate) { // This is a native date to java date conversion return 1; } } else if (to.isInterface()) { if (fromObj instanceof Function) { // See comments in coerceType if (to.getMethods().length == 1) { return 1; } } return 11; } else if (to.isPrimitive() && to != Boolean.TYPE) { return 3 + getSizeRank(to); } break; } return CONVERSION_NONE; }
/** Type-munging for field setting and method invocation. Conforms to LC3 specification */ static Object coerceTypeImpl(Class<?> type, Object value) { if (value != null && value.getClass() == type) { return value; } switch (getJSTypeCode(value)) { case JSTYPE_NULL: // raise error if type.isPrimitive() if (type.isPrimitive()) { reportConversionError(value, type); } return null; case JSTYPE_UNDEFINED: if (type == ScriptRuntime.StringClass || type == ScriptRuntime.ObjectClass) { return "undefined"; } else { reportConversionError("undefined", type); } break; case JSTYPE_BOOLEAN: // Under LC3, only JS Booleans can be coerced into a Boolean value if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_NUMBER: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type == ScriptRuntime.ObjectClass) { return coerceToNumber(Double.TYPE, value); } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_STRING: if (type == ScriptRuntime.StringClass || type.isInstance(value)) { return value; } else if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) { // Special case for converting a single char string to a // character // Placed here because it applies *only* to JS strings, // not other JS objects converted to strings if (((String) value).length() == 1) { return new Character(((String) value).charAt(0)); } else { return coerceToNumber(type, value); } } else if ((type.isPrimitive() && type != Boolean.TYPE) || ScriptRuntime.NumberClass.isAssignableFrom(type)) { return coerceToNumber(type, value); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_CLASS: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type == ScriptRuntime.ClassClass || type == ScriptRuntime.ObjectClass) { return value; } else if (type == ScriptRuntime.StringClass) { return value.toString(); } else { reportConversionError(value, type); } break; case JSTYPE_JAVA_OBJECT: case JSTYPE_JAVA_ARRAY: if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); } if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } else { if (type == ScriptRuntime.StringClass) { return value.toString(); } else { if (type.isInstance(value)) { return value; } else { reportConversionError(value, type); } } } break; case JSTYPE_OBJECT: if (type == ScriptRuntime.StringClass) { return ScriptRuntime.toString(value); } else if (type.isPrimitive()) { if (type == Boolean.TYPE) { reportConversionError(value, type); } return coerceToNumber(type, value); } else if (type.isInstance(value)) { return value; } else if (type == ScriptRuntime.DateClass && value instanceof NativeDate) { double time = ((NativeDate) value).getJSTimeValue(); // XXX: This will replace NaN by 0 return new Date((long) time); } else if (type.isArray() && value instanceof NativeArray) { // Make a new java array, and coerce the JS array components // to the target (component) type. NativeArray array = (NativeArray) value; long length = array.getLength(); Class<?> arrayType = type.getComponentType(); Object Result = Array.newInstance(arrayType, (int) length); for (int i = 0; i < length; ++i) { try { Array.set(Result, i, coerceType(arrayType, array.get(i, array))); } catch (EvaluatorException ee) { reportConversionError(value, type); } } return Result; } else if (value instanceof Wrapper) { value = ((Wrapper) value).unwrap(); if (type.isInstance(value)) return value; reportConversionError(value, type); } else if (type.isInterface() && value instanceof Callable) { // Try to use function as implementation of Java interface. // // XXX: Currently only instances of ScriptableObject are // supported since the resulting interface proxies should // be reused next time conversion is made and generic // Callable has no storage for it. Weak references can // address it but for now use this restriction. if (value instanceof ScriptableObject) { ScriptableObject so = (ScriptableObject) value; Object key = Kit.makeHashKeyFromPair(COERCED_INTERFACE_KEY, type); Object old = so.getAssociatedValue(key); if (old != null) { // Function was already wrapped return old; } Context cx = Context.getContext(); Object glue = InterfaceAdapter.create(cx, type, (Callable) value); // Store for later retrival glue = so.associateValue(key, glue); return glue; } reportConversionError(value, type); } else { reportConversionError(value, type); } break; } return value; }
/** Read a {@link Writable}, {@link String}, primitive type, or an array of the preceding. */ @SuppressWarnings("unchecked") public static Object readObject(DataInput in, ObjectWritable objectWritable, Configuration conf) throws IOException { String className = UTF8.readString(in); Class<?> declaredClass = PRIMITIVE_NAMES.get(className); if (declaredClass == null) { declaredClass = loadClass(conf, className); } Object instance; if (declaredClass.isPrimitive()) { // primitive types if (declaredClass == Boolean.TYPE) { // boolean instance = Boolean.valueOf(in.readBoolean()); } else if (declaredClass == Character.TYPE) { // char instance = Character.valueOf(in.readChar()); } else if (declaredClass == Byte.TYPE) { // byte instance = Byte.valueOf(in.readByte()); } else if (declaredClass == Short.TYPE) { // short instance = Short.valueOf(in.readShort()); } else if (declaredClass == Integer.TYPE) { // int instance = Integer.valueOf(in.readInt()); } else if (declaredClass == Long.TYPE) { // long instance = Long.valueOf(in.readLong()); } else if (declaredClass == Float.TYPE) { // float instance = Float.valueOf(in.readFloat()); } else if (declaredClass == Double.TYPE) { // double instance = Double.valueOf(in.readDouble()); } else if (declaredClass == Void.TYPE) { // void instance = null; } else { throw new IllegalArgumentException("Not a primitive: " + declaredClass); } } else if (declaredClass.isArray()) { // array int length = in.readInt(); instance = Array.newInstance(declaredClass.getComponentType(), length); for (int i = 0; i < length; i++) { Array.set(instance, i, readObject(in, conf)); } } else if (declaredClass == String.class) { // String instance = UTF8.readString(in); } else if (declaredClass.isEnum()) { // enum instance = Enum.valueOf((Class<? extends Enum>) declaredClass, UTF8.readString(in)); } else { // Writable Class instanceClass = null; String str = UTF8.readString(in); instanceClass = loadClass(conf, str); Writable writable = WritableFactories.newInstance(instanceClass, conf); writable.readFields(in); instance = writable; if (instanceClass == NullInstance.class) { // null declaredClass = ((NullInstance) instance).declaredClass; instance = null; } } if (objectWritable != null) { // store values objectWritable.declaredClass = declaredClass; objectWritable.instance = instance; } return instance; }
/** * 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, "aurora/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, "aurora/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, "aurora/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, "aurora/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); } }