From: <jsa...@us...> - 2008-11-17 16:31:32
|
Revision: 105 http://flexotask.svn.sourceforge.net/flexotask/?rev=105&view=rev Author: jsauerbach Date: 2008-11-17 16:31:23 +0000 (Mon, 17 Nov 2008) Log Message: ----------- Fix validation bug: Array.newInstance methods should be illegal only if allocation is forbidden (while Class.newInstance methods remain illegal always since they reflectively invoke unknown constructors). Also, restore accidentally omitted check for allocating methods when allocation is forbidden. Also, add support for safe-method-declarations even though no such file is currently supplied. This is step one of a future change in which both immutability-declarations and safe-method-declarations can be provided outside the classpath. Modified Paths: -------------- trunk/flexotask/src/com/ibm/realtime/flexotask/validation/CommonCodeValidator.java Modified: trunk/flexotask/src/com/ibm/realtime/flexotask/validation/CommonCodeValidator.java =================================================================== --- trunk/flexotask/src/com/ibm/realtime/flexotask/validation/CommonCodeValidator.java 2008-11-17 16:28:37 UTC (rev 104) +++ trunk/flexotask/src/com/ibm/realtime/flexotask/validation/CommonCodeValidator.java 2008-11-17 16:31:23 UTC (rev 105) @@ -90,11 +90,15 @@ /** The set of classes that are exempt from checking because they are part of the system and have been * specially designed for safety - /** The set of classes that are exempt from checking because they are part of the system and have been - * specially designed for safety */ private static Set exemptClasses = new HashSet(); + /** The set of methods that are exempt from checking because they would cause validation failure on some + * systems, but what they do is actually safe. These can be augmented by user editing of a resource stored + * in the same package as this class (like immutability-declarations). This must be done with great care. + */ + private static Set exemptMethods = new HashSet(); + static { try { trace("Initializing exempt classes"); @@ -110,6 +114,8 @@ exemptClasses.add(SimulationSupport.class.getName()); trace("Reading immutability declarations"); immutabilityInit(); + trace("Reading safe method declarations"); + safeMethodInit(); trace("Adding illegal methods"); Set im = new HashSet(); im.add(MethodWrapper.create(Object.class, "wait", "()V", false)); @@ -117,6 +123,10 @@ im.add(MethodWrapper.create(Object.class, "wait", "(JI)V", false)); im.add(MethodWrapper.create(Object.class, "notify", "()V", false)); im.add(MethodWrapper.create(Object.class, "notifyAll", "()V", false)); + /* The non-array newInstance methods are always illegal because they invoke constructors; therefore, + * they are illegal for the same reason that invoke is illegal. However, the array newInstance + * methods are only illegal when allocation is forbidden. + */ im.add(MethodWrapper.create(Class.class, "newInstance", "()Ljava/lang/Object;", false)); im.add(MethodWrapper.create(Constructor.class, "newInstance", "([Ljava/lang/Object;)Ljava/lang/Object;", false)); @@ -124,10 +134,6 @@ "(Ljava/lang/Object;Ljava/lang/Object;)V", false)); im.add(MethodWrapper.create(Field.class, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", false)); - im.add(MethodWrapper.create(Array.class, "newInstance", - "(Ljava/lang/Class;I)Ljava/lang/Object;", true)); - im.add(MethodWrapper.create(Array.class, "newInstance", - "(Ljava/lang/Class;[I)Ljava/lang/Object;", true)); im.add(MethodWrapper.create(Array.class, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", true)); im.add(MethodWrapper.create(Array.class, "get", @@ -205,7 +211,13 @@ illegalIfIsolated = Collections.unmodifiableSet(ii); trace("Illegal-when-isolated methods added"); trace("adding methods that are illegal if allocation is forbidden"); - allocationMethods = Collections.singleton(MethodWrapper.create(Object.class, "clone", "()Ljava/lang/Object;", false)); + Set ia = new HashSet(); + ia.add(MethodWrapper.create(Object.class, "clone", "()Ljava/lang/Object;", false)); + ia.add(MethodWrapper.create(Array.class, "newInstance", + "(Ljava/lang/Class;I)Ljava/lang/Object;", true)); + ia.add(MethodWrapper.create(Array.class, "newInstance", + "(Ljava/lang/Class;[I)Ljava/lang/Object;", true)); + allocationMethods = Collections.unmodifiableSet(ia); trace("done adding allocation-sensitive methods"); } catch (Exception e) { /* Serious error for these not to be found */ @@ -278,7 +290,7 @@ this.context = context; } - /** + /** * Constructor. * @param context the validation context. * @param template the flexotask template checked @@ -532,6 +544,12 @@ if (hasBeenOrWillBeAnalyzed(toConsider)) { return; } + if (exemptMethods.contains(toConsider)) { + /* Give methods declared as exempt by the user a free pass. The user is well-advised to use this facility cautiously + */ + exemptMethods.add(toConsider); + return; + } String methodClass = toConsider.getMethodClass().getName(); /* The following test is done with names because at development time the class loader used for * analysis is not the context classloader @@ -551,7 +569,8 @@ } } if (illegalMethods.contains(toConsider) || - ((defaultGetStaticChecking == CHECK_IMMUTABLE) && illegalIfIsolated.contains(toConsider))) { + ((defaultGetStaticChecking == CHECK_IMMUTABLE) && illegalIfIsolated.contains(toConsider)) || + mayNotAllocate && allocationMethods.contains(toConsider)) { StringBuilder builder = new StringBuilder("Illegal method: ").append(toConsider).append(" in ").append(caller); getCallChain(builder, caller); addMethodDeclarationViolation(Severity.ERROR, caller, builder.toString()); @@ -564,7 +583,7 @@ String sig = toConsider.getMethodSignature(); if (sig.endsWith(";")) { /* A native method that returns a class makes that class live. We rely on two things to ensure that native - * methods cannot introduce classes in any other way: (1) when executing on an flexotask scheduler thread, + * methods cannot introduce classes in any other way: (1) when executing on a Flexotask scheduler thread, * JNI is prohibited from introducing objects by modifying reference fields (2) we audit INL methods to * make sure they don't do anything bad. We also have to audit system-provided JNI methods to make sure * that the objects they return, even by "legal" means, are not objects that were cached somewhere and @@ -1210,6 +1229,49 @@ } /** + * Initialize exemptMethods set from user declarations + */ + private static void safeMethodInit() throws IOException { + InputStream safes = CommonCodeValidator.class.getResourceAsStream("safe-method-declarations"); + if (safes == null) { + /* resource is optional */ + return; + } + BufferedReader rdr = new BufferedReader(new InputStreamReader(safes)); + String line; + line = rdr.readLine(); + Set safeMethods = new HashSet(); + while (line != null) { + StringTokenizer tok = new StringTokenizer(line); + if (tok.countTokens() != 4) { + throw new IOException("Malformed line in safe-method-declarations: " + line); + } + Class clazz; + try { + clazz = Class.forName(tok.nextToken()); + } catch (ClassNotFoundException e) { + /* Perhaps this class doesn't exist in the current VM */ + trace("Class not found in immutability declaration: " + line); + clazz = null; + } + if (clazz != null) { + String methodName = tok.nextToken(); + String sig = tok.nextToken(); + boolean isStatic = Boolean.valueOf(tok.nextToken()).booleanValue(); + try { + MethodWrapper method = MethodWrapper.create(clazz, methodName, sig, isStatic); + safeMethods.add(method); + } catch (AnalysisException e) { + /* Perhaps this method doesn't exist in the current VM */ + trace("Class not found in immutability declaration: " + line); + } + } + line = rdr.readLine(); + } + CommonCodeValidator.exemptMethods = Collections.unmodifiableSet(safeMethods); + } + + /** * Returns true if provided class object is a <code>AtomicFlexotask</code> * subclass, otherwise false. * @param clazz the class object This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |