From: <fd...@us...> - 2007-09-15 18:14:08
|
Revision: 3502 http://jnode.svn.sourceforge.net/jnode/?rev=3502&view=rev Author: fduminy Date: 2007-09-15 11:14:00 -0700 (Sat, 15 Sep 2007) Log Message: ----------- Added ant task to check that native methods are properly implemented. For now, method signature is not checked. Modified Paths: -------------- trunk/all/build.xml trunk/builder/src/builder/org/jnode/ant/taskdefs/AnnotateTask.java trunk/builder/src/builder/org/jnode/ant/taskdefs/FileSetTask.java trunk/builder/src/builder/org/jnode/ant/taskdefs/HeaderTask.java trunk/core/.classpath trunk/core/src/core/org/jnode/vm/VmSystemClassLoader.java trunk/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java Added Paths: ----------- trunk/builder/src/builder/org/jnode/ant/taskdefs/NativeCheckTask.java trunk/core/src/core/org/jnode/vm/VmUtils.java Modified: trunk/all/build.xml =================================================================== --- trunk/all/build.xml 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/all/build.xml 2007-09-15 18:14:00 UTC (rev 3502) @@ -166,7 +166,7 @@ </target> <!-- Assemble all plugins --> - <target name="assemble-plugins" depends="assemble-projects,openjdk-annotate"> + <target name="assemble-plugins" depends="assemble-projects,openjdk-annotate,native-method-check"> <!-- Now assemble all plugins --> <taskdef name="plugin" classname="org.jnode.build.PluginTask" classpathref="cp-jnode"/> <plugin todir="${plugins.dir}" tmpdir="${build.dir}/tmp/plugins" pluginDir="${descriptors.dir}"> @@ -623,7 +623,7 @@ </fileset> </vmspecificsources> <classpathsources> - <fileset dir="${root.dir}/../openjdk/j2se/src/share/classes/"> + <fileset dir="${root.dir}/../../openjdk/j2se/src/share/classes/"> <patternset refid="cp-includes-pattern"/> <patternset refid="cp-sources-pattern"/> </fileset> @@ -638,13 +638,26 @@ classpathref="cp-jnode"/> <oj-annotate annotationFile="${root.dir}/all/conf/openjdk-annotations.properties" - trace="false"> + trace="false" failonerror="true"> <fileset dir="${root.dir}/core/build/classes"> <patternset includes="**/*.class"/> </fileset> </oj-annotate> </target> + <!-- check that all native methods are properly implemented for JNode --> + <target name="native-method-check" depends="assemble-projects"> + <echo message="native-method-check"/> + <taskdef name="native-check" classname="org.jnode.ant.taskdefs.NativeCheckTask" + classpathref="cp-jnode"/> + + <native-check trace="false" failonerror="false"> + <fileset dir="${root.dir}/core/build/classes"> + <patternset includes="**/*.class"/> + </fileset> + </native-check> + </target> + <!-- Run all tests --> <target name="tests" depends="assemble"> <ant target="tests" dir="${root.dir}/fs" inheritall="on" inheritrefs="on"/> Modified: trunk/builder/src/builder/org/jnode/ant/taskdefs/AnnotateTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/ant/taskdefs/AnnotateTask.java 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/builder/src/builder/org/jnode/ant/taskdefs/AnnotateTask.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -60,9 +60,8 @@ private File annotationFile; private String[] classesFiles; - private boolean trace = true; - public void execute() throws BuildException { + protected void doExecute() throws BuildException { classesFiles = readProperties(annotationFile); processFiles(); } @@ -74,11 +73,6 @@ public final void setAnnotationFile(File annotationFile) { this.annotationFile = annotationFile; } - - public final void setTrace(boolean trace) - { - this.trace = trace; - } /** * Read the properties file. For now, it simply contains a list of Modified: trunk/builder/src/builder/org/jnode/ant/taskdefs/FileSetTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/ant/taskdefs/FileSetTask.java 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/builder/src/builder/org/jnode/ant/taskdefs/FileSetTask.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -37,14 +37,55 @@ * */ abstract public class FileSetTask extends Task { - + protected boolean trace = false; + protected boolean failOnError = true; + private final ArrayList<FileSet> fileSets = new ArrayList<FileSet>(); + public final void setTrace(boolean trace) + { + this.trace = trace; + } + + public final void setFailOnError(boolean failOnError) + { + this.failOnError = failOnError; + } + public void addFileSet(FileSet fs) { fileSets.add(fs); } - public void execute() throws BuildException { + final public void execute() throws BuildException { + try + { + doExecute(); + } + catch(BuildException be) + { + if(failOnError) + { + throw be; + } + else + { + be.printStackTrace(); + } + } + catch(Throwable t) + { + if(failOnError) + { + throw new BuildException(t); + } + else + { + t.printStackTrace(); + } + } + } + + protected void doExecute() throws BuildException { // default implementation : simply iterate on all files processFiles(); } Modified: trunk/builder/src/builder/org/jnode/ant/taskdefs/HeaderTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/ant/taskdefs/HeaderTask.java 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/builder/src/builder/org/jnode/ant/taskdefs/HeaderTask.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -68,7 +68,7 @@ return line.trim().equals(hdrLine.trim()); } - public void execute() throws BuildException { + protected void doExecute() throws BuildException { if (headerFile == null) { throw new BuildException("HeaderFile must be set"); } Added: trunk/builder/src/builder/org/jnode/ant/taskdefs/NativeCheckTask.java =================================================================== --- trunk/builder/src/builder/org/jnode/ant/taskdefs/NativeCheckTask.java (rev 0) +++ trunk/builder/src/builder/org/jnode/ant/taskdefs/NativeCheckTask.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -0,0 +1,333 @@ +/* + * $Id: HeaderTask.java 3379 2007-08-04 10:19:57Z lsantha $ + * + * JNode.org + * Copyright (C) 2003-2006 JNode.org + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; If not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.jnode.ant.taskdefs; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.tools.ant.BuildException; +import org.jnode.vm.VmUtils; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.CodeVisitor; +import org.objectweb.asm.attrs.Attributes; + +/** + * That ant task will check that native methods are properly implemented + * for JNode. + * + * @author Fabien DUMINY (fduminy at jnode.org) + * + */ +public class NativeCheckTask extends FileSetTask { + /** + * potential implementation of the native methods for JNode. + * key: className (String) + * value: native methods for the class (List<NativeMethod>) + */ + private Map<String,List<NativeMethod>> jnodeNativeMethods = new HashMap<String, List<NativeMethod>>(); + + /** + * native methods per class + * key: className (String) + * value: native methods for the class (List<NativeMethod>) + */ + private Map<String,List<NativeMethod>> nativeMethods = new HashMap<String,List<NativeMethod>>(); + + private Set<String> missingClasses = new TreeSet<String>(); + private Map<String,List<NativeMethod>> missingMethods = new HashMap<String,List<NativeMethod>>(); + + protected void doExecute() throws BuildException { + // process all classes to find native methods + // and classes that could potentially implement native methods + // for JNode + processFiles(); + + // now, check all native methods are properly implemented + // for JNode + int nbNativeMethods = 0; + for(String className : nativeMethods.keySet()) + { + List<NativeMethod> methods = nativeMethods.get(className); + for(NativeMethod method : methods) + { + nbNativeMethods++; + checkNativeMethod(className, method); + } + } + + // report + final String INDENT = " "; + + int nbMissingClasses = missingClasses.size(); + if(!missingClasses.isEmpty()) + { + System.err.println("Missing classes:"); + for(String missingClass : missingClasses) + { + System.err.println(INDENT+missingClass); + } + } + + int nbMissingMethods = 0; + if(!missingMethods.isEmpty()) + { + System.err.println("Missing methods:"); + for(String cls : missingMethods.keySet()) + { + System.err.println(INDENT+" class "+cls); + for(NativeMethod m : missingMethods.get(cls)) + { + System.err.println(INDENT+INDENT+m.getName()); + nbMissingMethods++; + } + } + } + + System.out.println("Found "+nbNativeMethods+" native methods in "+nativeMethods.size()+" classes"); + if((nbMissingMethods != 0) || (nbMissingClasses != 0)) + { + System.err.println(missingClasses.size()+" missing classes. "+nbMissingMethods+" missing methods"); + + String message = "Some native methods are not properly implemented (see errors above)"; + if(failOnError) + { + throw new BuildException(message); + } + else + { + System.err.println("[FAILED] "+message); + } + } + else + { + System.out.println("[OK] All native methods are properly defined"); + } + } + + private boolean checkNativeMethod(String className, NativeMethod method) + { + boolean hasError = false; + String jnodeNativeClass = VmUtils.getNativeClassName(className.replace('/', '.')); + + if(jnodeNativeMethods.containsKey(jnodeNativeClass)) + { + List<NativeMethod> methods = jnodeNativeMethods.get(jnodeNativeClass); + boolean found = false; + for(NativeMethod nvMethod : methods) + { + if(method.getName().equals(nvMethod.getName())) + { + found = true; + break; + } + } + + if(!found) + { + List<NativeMethod> methodList = missingMethods.get(jnodeNativeClass); + if(methodList == null) + { + methodList = new ArrayList<NativeMethod>(); + missingMethods.put(jnodeNativeClass, methodList); + } + + methodList.add(method); + hasError = true; + } + } + else + { + missingClasses.add(jnodeNativeClass); + hasError = true; + } + + return hasError; + } + + @Override + protected void processFile(File file) throws IOException { + FileInputStream fis = null; + + try + { + fis = new FileInputStream(file); + NativeMethodClassVisitor v = getNativeMethods(file, fis); + if((v != null) && !v.getNativeMethods().isEmpty()) + { + if(v.couldImplementNativeMethods()) + { + jnodeNativeMethods.put(v.getClassName(), v.getNativeMethods()); + } + else + { + nativeMethods.put(v.getClassName(), v.getNativeMethods()); + } + } + } + finally + { + if(fis != null) + { + fis.close(); + } + } + } + + private NativeMethodClassVisitor getNativeMethods(File file, InputStream inputClass) throws BuildException { + ClassWriter cw = new ClassWriter(false); + NativeMethodClassVisitor v = null; + try { + ClassReader cr = new ClassReader(inputClass); + v = new NativeMethodClassVisitor(file, cw); + cr.accept(v, Attributes.getDefaultAttributes(), true); + + if(v.allowNatives()) + { + // if natives are allowed for that class, no need to check + // for a pure java implementation + v = null; + } + } catch (Exception ex) { + System.err.println("Unable to load class in file "+file.getAbsolutePath()+" : "+ex.getMessage()); + } + return v; + } + + private static class NativeMethodClassVisitor extends ClassAdapter { + private String className; + private boolean couldImplementNativeMethods; + private boolean allowNatives = false; + private List<NativeMethod> nativeMethods = new ArrayList<NativeMethod>(); + + public NativeMethodClassVisitor(File file, ClassVisitor cv) { + super(cv); + } + + @Override + public void visit(int version, + int access, + String name, + String superName, + String[] interfaces, + String sourceFile) + { + this.className = name.replace('/','.'); + this.allowNatives = VmUtils.allowNatives(className, "x86"); //TODO hard coded architecture: change that ! + this.couldImplementNativeMethods = VmUtils.couldImplementNativeMethods(className); + + super.visit(version, access, name, superName, interfaces, sourceFile); + } + + @Override + public CodeVisitor visitMethod(int access, + String name, + String desc, + String[] exceptions, + Attribute attrs) + { + if(!allowNatives) + { + // we don't allow native for that class => + // we must have a pure java implementation for that method + + if((couldImplementNativeMethods && isStatic(access)) || + isNative(access)) + { + nativeMethods.add(new NativeMethod(access, name, desc)); + } + } + + return null; // we don't to visit inside the method + } + + public String getClassName() { + return className; + } + + public List<NativeMethod> getNativeMethods() { + return nativeMethods; + } + + public boolean couldImplementNativeMethods() { + return couldImplementNativeMethods; + } + + public boolean allowNatives() { + return allowNatives; + } + } + + private static class NativeMethod + { + private final int access; + private final String name; + private final String desc; + public NativeMethod(int access, String name, String desc) { + super(); + this.access = access; + this.name = name; + this.desc = desc; + } + public int getAccess() { + return access; + } + public String getName() { + return name; + } + public String getDesc() { + return desc; + } + + public String toString() + { + return name + " " + desc; + } + } + + public static boolean isNative(int access) + { + return isSet(access, org.objectweb.asm.Constants.ACC_NATIVE); + } + + public static boolean isStatic(int access) + { + return isSet(access, org.objectweb.asm.Constants.ACC_STATIC); + } + + private static boolean isSet(int access, int flag) + { + return ((access & flag) == flag); + } +} Modified: trunk/core/.classpath =================================================================== --- trunk/core/.classpath 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/core/.classpath 2007-09-15 18:14:00 UTC (rev 3502) @@ -26,6 +26,7 @@ <classpathentry kind="lib" path="lib/mmtk/mmtk.jar" sourcepath="D:/epr/cvswork/MMTk/src"/> <classpathentry exported="true" kind="lib" path="lib/asm-1.5.3.jar"/> <classpathentry exported="true" kind="lib" path="lib/asm-attrs-1.5.3.jar"/> + <classpathentry exported="true" kind="lib" path="lib/asm-util-1.5.3.jar"/> <classpathentry kind="src" path="src/nanoxml"/> <classpathentry kind="lib" path="lib/mauve.jar"/> <classpathentry kind="output" path="build/classes"/> Modified: trunk/core/src/core/org/jnode/vm/VmSystemClassLoader.java =================================================================== --- trunk/core/src/core/org/jnode/vm/VmSystemClassLoader.java 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/core/src/core/org/jnode/vm/VmSystemClassLoader.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -401,11 +401,8 @@ private VmType loadNormalClass(String name) throws IOException, ClassNotFoundException { - boolean allowNatives = false; - allowNatives |= name.equals("org.jnode.vm.Unsafe"); final String archN = arch.getName(); - allowNatives |= name.equals("org.jnode.vm." + archN + ".Unsafe" - + archN.toUpperCase()); + boolean allowNatives = VmUtils.allowNatives(name, archN); // System.out.println("bvi.loadClass: " +name); final ByteBuffer image = getClassData(name); Added: trunk/core/src/core/org/jnode/vm/VmUtils.java =================================================================== --- trunk/core/src/core/org/jnode/vm/VmUtils.java (rev 0) +++ trunk/core/src/core/org/jnode/vm/VmUtils.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -0,0 +1,37 @@ +package org.jnode.vm; + +import gnu.java.lang.VMClassHelper; + +/** + * Utility class to share some Vm features. + * For now, it's especially used to know how native methods are implemented in JNode. + * + * @author Fabien DUMINY (fduminy at jnode.org) + * + */ +public class VmUtils { + private static final String NATIVE_CLASSNAME_PREFIX = "Native"; + public static boolean couldImplementNativeMethods(String className) + { + String clsName = VMClassHelper.getClassNamePortion(className); + return clsName.startsWith(NATIVE_CLASSNAME_PREFIX); + } + + public static String getNativeClassName(String className) + { + final String pkg = VMClassHelper.getPackagePortion(className); + final String nativeClassName = pkg + ((pkg.length() > 0) ? "." : "") + + NATIVE_CLASSNAME_PREFIX + VMClassHelper.getClassNamePortion(className); + return nativeClassName; + } + + public static boolean allowNatives(String className, String architectureName) + { + boolean allowNatives = false; + allowNatives |= className.equals("org.jnode.vm.Unsafe"); + allowNatives |= className.equals("org.jnode.vm." + architectureName + ".Unsafe" + + architectureName.toUpperCase()); + + return allowNatives; + } +} Modified: trunk/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java =================================================================== --- trunk/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java 2007-09-15 18:01:39 UTC (rev 3501) +++ trunk/core/src/core/org/jnode/vm/classmgr/ClassDecoder.java 2007-09-15 18:14:00 UTC (rev 3502) @@ -29,6 +29,7 @@ import java.security.ProtectionDomain; import org.jnode.system.BootLog; +import org.jnode.vm.VmUtils; import org.jnode.vm.annotation.AllowedPackages; import org.jnode.vm.annotation.CheckPermission; import org.jnode.vm.annotation.DoPrivileged; @@ -482,9 +483,7 @@ private static VmByteCode getNativeCodeReplacement(VmMethod method, VmClassLoader cl, boolean verbose) { final String className = method.getDeclaringClass().getName(); - final String pkg = VMClassHelper.getPackagePortion(className); - final String nativeClassName = pkg + ((pkg.length() > 0) ? "." : "") - + "Native" + VMClassHelper.getClassNamePortion(className); + final String nativeClassName = VmUtils.getNativeClassName(className); final VmType nativeType; try { nativeType = cl.loadClass(nativeClassName, true); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |