/*
* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package test.com.sun.max.vm.jni;
import java.io.*;
import java.lang.reflect.*;
import junit.framework.*;
import test.com.sun.max.vm.*;
import com.sun.max.annotate.*;
import com.sun.max.ide.*;
import com.sun.max.io.*;
import com.sun.max.program.*;
import com.sun.max.vm.jni.*;
import com.sun.max.vm.type.*;
/**
*/
public class MangleTest extends MaxTestCase {
public MangleTest(String name) {
super(name);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(MangleTest.suite());
}
public static Test suite() {
return new VmTestSetup(new TestCaseClassSet(AutoTest.class).toTestSuite());
}
private boolean mangleAndDemangle(String className, String name, String signature) {
try {
final String mangled = Mangle.mangleMethod(JavaTypeDescriptor.parseTypeDescriptor(className), name, signature == null ? null : SignatureDescriptor.create(signature), false);
final Mangle.DemangledMethod demangled = Mangle.demangleMethod(mangled);
return demangled.mangle().equals(mangled);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
}
private static boolean isOverloadedNative(Method method) {
for (Method m : method.getDeclaringClass().getDeclaredMethods()) {
if (Modifier.isNative(m.getModifiers()) && !m.equals(method) && m.getName().equals(method.getName())) {
return true;
}
}
return false;
}
private static boolean containedByTopLevelClass(Method method) {
return method.getDeclaringClass().getEnclosingClass() == null;
}
private String mangleAndDemangle(Method method) {
assert Modifier.isNative(method.getModifiers());
final String mangled = Mangle.mangleMethod(method, isOverloadedNative(method));
final Mangle.DemangledMethod demangled = Mangle.demangleMethod(mangled);
assertEquals(demangled.mangle(), mangled);
if (containedByTopLevelClass(method)) {
final Method demangledMethod = demangled.toJava(method.getDeclaringClass().getClassLoader());
if (demangledMethod == null) {
ProgramWarning.message("cannot demangle " + mangled + " to an existing Method object");
} else {
assertTrue(method.equals(demangledMethod));
}
}
return mangled;
}
/**
* Gets the output from the standard javah tool for a given class.
*/
private String javah(Class declaringClass) {
if (declaringClass.isAnonymousClass()) {
return null;
}
try {
final File tempFile = File.createTempFile("MangleTest_", ".h");
final String[] args = {"-o", tempFile.getAbsolutePath(), "-classpath", Classpath.fromSystem().toString(), declaringClass.getName()};
ToolChain.javah(args);
final String headerFile = new String(Files.toChars(tempFile));
tempFile.delete();
return headerFile;
} catch (IOException e) {
e.printStackTrace();
fail();
return null;
}
}
private void mangleAndDemangle(Class declaringClass) {
String headerFile = null;
boolean headerFileGenerated = false;
try {
for (Method method : declaringClass.getDeclaredMethods()) {
if (Modifier.isNative(method.getModifiers()) && method.getAnnotation(C_FUNCTION.class) == null) {
if (!headerFileGenerated) {
headerFile = javah(declaringClass);
headerFileGenerated = true;
}
String mangled = mangleAndDemangle(method);
if (headerFile != null && !headerFile.contains(mangled)) {
if (containedByTopLevelClass(method)) {
fail("can't find '" + mangled + "' in javah generated file");
} else {
ProgramWarning.message("can't find '" + mangled + "' in javah generated for non top-level " + declaringClass);
}
}
}
}
} catch (NoClassDefFoundError e) {
ProgramWarning.message(e + " while trying to get the declared methods of " + declaringClass);
}
}
public void test_mangle() {
Trace.on(1);
class LocalClass {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
final Object localAnonymousClass = new Object() {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
};
class InnerClass {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
}
}
final Object anonymousClass = new Object() {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
};
// Reflection based
mangleAndDemangle(MangleTest.class);
mangleAndDemangle(NestedClass.class);
mangleAndDemangle(InnerClass.class);
mangleAndDemangle(LocalClass.class);
mangleAndDemangle(anonymousClass.getClass());
mangleAndDemangle(new LocalClass().localAnonymousClass.getClass());
mangleAndDemangle(LocalClass.InnerClass.class);
}
public void test_max() {
// Maxine classes
new ClassSearch() {
@Override
protected boolean visitClass(boolean isArchiveEntry, String className) {
if (isArchiveEntry || className.endsWith("package-info")) {
return true;
}
if (!className.startsWith("com.sun.max")) {
return true;
}
if (className.startsWith("com.sun.max.asm")) {
// Too many generated methods
return true;
}
if (className.startsWith("com.sun.max.vm.asm.")) {
// Too many generated methods
return true;
}
try {
final Class<?> clazz = Class.forName(className, false, MangleTest.class.getClassLoader());
mangleAndDemangle(clazz);
} catch (ClassNotFoundException e) {
ProgramWarning.message(e.toString());
}
return true;
}
}.run(Classpath.fromSystem());
// String based
assertTrue(mangleAndDemangle("Ljava/lang/Class;", "forName", null));
}
public native void simple(int arg1, String arg2, Exception arg3);
public native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
public native String overload(int arg1, String arg2);
public native String overload(String arg2);
static native String staticOverload(int arg1, String arg2);
static native String staticOverload(String arg2);
private static final class NestedClass {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
static native String staticOverload(int arg1, String arg2);
static native String staticOverload(String arg2);
}
private class InnerClass {
native void simple(int arg1, String arg2, Exception arg3);
native void underscore_(int arg1, String arg2, Exception arg3);
public native void overload();
native String overload(int arg1, String arg2);
native String overload(String arg2);
}
}