/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "java_lang_Class.h" #include "java_lang_reflect_Field.h" #include "java_lang_reflect_Method.h" #include "java_lang_reflect_Constructor.h" #include "JavaVM.h" #include "JavaString.h" #include "prprf.h" #include "SysCallsRuntime.h" #include "StackWalker.h" enum FieldOrMethodCategory { fieldOrMethodCategoryPublic=0, fieldOrMethodCategoryDeclared=1 }; static inline void throwClassVerifyException(VerifyError err) { // FIXME Need to be more specific in the exceptions we throw here switch (err.cause) { case VerifyError::noClassDefFound: case VerifyError::badClassFormat: default: sysThrowNamedException("java/lang/ClassNotFoundException"); break; case VerifyError::illegalAccess: sysThrowNamedException("java/lang/IllegalAccessException"); break; } } static inline void throwClassRuntimeException(RuntimeError err) { // FIXME Need to be more specific in the exceptions we throw here switch (err.cause) { case RuntimeError::illegalAccess: sysThrowNamedException("java/lang/IllegalAccessException"); break; case RuntimeError::nullPointer: sysThrowNullPointerException(); break; default: sysThrowNamedException("java/lang/IllegalArgumentException"); break; } } extern "C" { /* Some interesting things to remember: * (1) What is passed to all native methods of java/lang/Class is an instance * of the VM class Class. * (2) All native routines here do not check for security restrictions; it is * assumed that security is dealt with at a higher level. */ static inline Type &toType(Java_java_lang_Class &inClass) { Type &type = *(Type *) (&inClass); return type; } static inline JavaObject &toObject(Java_java_lang_Object &inObject) { return *(Class *)(&inObject); } /* * Class : java/lang/Class * Method : forName * Signature : (Ljava/lang/String;)Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *) Netscape_Java_java_lang_Class_forName(Java_java_lang_String *nameString) { if (!nameString) sysThrowNamedException("java/lang/ClassNotFoundException"); ClassCentral ¢ral = VM::getCentral(); char *name = ((JavaString *) nameString)->convertUtf(); // Replace dots with slashes to obtain the fully qualified name of the class. // central.addClass() expects a fully qualified class name. for (char *p = name; *p; p++) if (*p == '.') *p = '/'; Class *clz; try { clz = static_cast(central.addClass(name).getThisClass()); } catch(VerifyError err) { throwClassVerifyException(err); } catch(RuntimeError err) { throwClassRuntimeException(err); } /* Free the memory we just allocated */ JavaString::freeUtf(name); return (Java_java_lang_Class *) clz; } /* * Class : java/lang/Class * Method : forName0 * Signature : (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *) Netscape_Java_java_lang_Class_forName0(Java_java_lang_String *nameString, uint32 /* bool */, Java_java_lang_ClassLoader *) { // IMPLEMENT return Netscape_Java_java_lang_Class_forName(nameString); } /* * Class : java/lang/Class * Method : newInstance0 * Signature : ()Ljava/lang/Object; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Object *) Netscape_Java_java_lang_Class_newInstance0(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); // We can't instantiate primitive types, arrays or interfaces if (type.typeKind != tkObject) sysThrowNamedException("java/lang/InstantiationException"); const Class &clazz = asClass(type); // Check to see if the caller of the class has package access to this class Frame thisFrame; // Skip over our caller's frame, that of java.lang.Class.newInstance() Method &callerMethod = thisFrame.getCallingJavaMethod(2); ClassOrInterface *callingClass; callingClass = callerMethod.getDeclaringClass(); bool hasPackageAccess = false; if (callingClass->package.name == clazz.package.name) hasPackageAccess = true; // Check argument type. Make sure that the class that called this // method is a class that can access the class clazz. if (!(clazz.getModifiers() & CR_ACC_PUBLIC) && !hasPackageAccess) sysThrowNamedException("java/lang/IllegalAccessException"); // Make sure that the class is instantiable. The class is // instantiable if it is not abstract, and has at least one constructor // that the calling class can access. if ((clazz.getModifiers() & CR_ACC_ABSTRACT) || clazz.isInterface()) sysThrowNamedException("java/lang/InstantiationException"); Constructor *constructor = NULL; const Constructor **declaredConstructors; Int32 nDeclaredConstructors; // Check that there is at least one constructor that the calling class can call nDeclaredConstructors = const_cast(&clazz)->getDeclaredConstructors(declaredConstructors); /* Now go through each of these declared constructors and see if we have * package access to any of them * FIXME This is horrendous, too much work. Think about not doing this much * work everytime. */ for (int i = 0; i < nDeclaredConstructors; i++) { constructor = const_cast(declaredConstructors[i]); // Find a constructor that takes no arguments other than 'this' if (constructor->getSignature().nArguments > 1) continue; int32 modifiers = constructor->getModifiers(); if (modifiers & CR_METHOD_PUBLIC) break; // Can't invoke private constructors if (modifiers & CR_METHOD_PRIVATE) continue; // See if we can invoke a constructor with package scope if (hasPackageAccess) break; } if (i == nDeclaredConstructors) sysThrowNamedException("java/lang/IllegalAccessException"); try { return (Java_java_lang_Object *)&constructor->newInstance(NULL, 0); } catch (RuntimeError err) { throwClassRuntimeException(err); } } /* * Class : java/lang/Class * Method : isInstance * Signature : (Ljava/lang/Object;)Z */ NS_EXPORT NS_NATIVECALL(uint32 /* bool */) Netscape_Java_java_lang_Class_isInstance(Java_java_lang_Class *inClass, Java_java_lang_Object *inObj) { Type &type = toType(*inClass); if (type.isPrimitive()) return (Uint32)false; if (!inObj) return false; const Class &clazz = asClass(type); return (Uint32) clazz.isInstance(toObject(*inObj)); } /* * Class : java/lang/Class * Method : isAssignableFrom * Signature : (Ljava/lang/Class;)Z */ NS_EXPORT NS_NATIVECALL(uint32) /* bool */ Netscape_Java_java_lang_Class_isAssignableFrom(Java_java_lang_Class * inClass, Java_java_lang_Class * inToClass) { if (!inToClass) sysThrowNullPointerException(); return toType(*inClass).isAssignableFrom(toType(*inToClass)); } /* * Class : java/lang/Class * Method : isInterface * Signature : ()Z */ NS_EXPORT NS_NATIVECALL(uint32 /* bool */) Netscape_Java_java_lang_Class_isInterface(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); return type.isInterface(); } /* * Class : java/lang/Class * Method : isArray * Signature : ()Z */ NS_EXPORT NS_NATIVECALL(uint32 /* bool */) Netscape_Java_java_lang_Class_isArray(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); return type.isArray(); } /* * Class : java/lang/Class * Method : isPrimitive * Signature : ()Z */ NS_EXPORT NS_NATIVECALL(uint32 /* bool */) Netscape_Java_java_lang_Class_isPrimitive(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); return type.isPrimitive(); } /* * Class : java/lang/Class * Method : getName * Signature : ()Ljava/lang/String; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_String *) Netscape_Java_java_lang_Class_getName(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); const char *fullName = type.getName(); JavaString &fullNameStr = VM::intern(fullName); return (Java_java_lang_String *) &fullNameStr; } /* * Class : java/lang/Class * Method : getClassLoader * Signature : ()Ljava/lang/ClassLoader; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_ClassLoader *) Netscape_Java_java_lang_Class_getClassLoader(Java_java_lang_Class *) { #ifdef DEBUG_LOG PR_fprintf(PR_STDERR, "Netscape_Java_java_lang_Class_getClassLoader() not implemented"); #endif return 0; } /* * Class : java/lang/Class * Method : getSuperclass * Signature : ()Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *) Netscape_Java_java_lang_Class_getSuperclass(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); const Type *superClass = type.getSuperClass(); return (Java_java_lang_Class *) superClass; } /* * Class : java/lang/Class * Method : getInterfaces * Signature : ()[Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_Class *) Netscape_Java_java_lang_Class_getInterfaces(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); if (isPrimitiveKind(type.typeKind)) sysThrowNamedException("java/lang/IllegalAccessException"); ClassOrInterface &clazz = *static_cast(&type); Interface **interfaces; Int32 numInterfaces = clazz.getInterfaces(interfaces); JavaArray *arr = (JavaArray *) sysNewObjectArray(&VM::getStandardClass(cClass), numInterfaces); ArrayOf_Java_java_lang_Class *clazzArray = (ArrayOf_Java_java_lang_Class *) arr; for (Int32 i = 0; i < numInterfaces; i++) clazzArray->elements[i] = (Java_java_lang_Class *) interfaces[i]; return clazzArray; } /* * Class : java/lang/Class * Method : getComponentType * Signature : ()Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *) Netscape_Java_java_lang_Class_getComponentType(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); if (!type.isArray()) return NULL; const Type &componentType = asArray(type).getComponentType(); return (Java_java_lang_Class *) &componentType; } /* * Class : java/lang/Class * Method : getModifiers * Signature : ()I */ NS_EXPORT NS_NATIVECALL(int32) Netscape_Java_java_lang_Class_getModifiers(Java_java_lang_Class *inClass) { Type &type = toType(*inClass); return type.getModifiers(); } /* * Class : java/lang/Class * Method : getSigners * Signature : ()[Ljava/lang/Object; */ NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_Object *) Netscape_Java_java_lang_Class_getSigners(Java_java_lang_Class *) { printf("Netscape_Java_java_lang_Class_getSigners() not implemented"); return 0; } /* * Class : java/lang/Class * Method : setSigners * Signature : ([Ljava/lang/Object;)V */ NS_EXPORT NS_NATIVECALL(void) Netscape_Java_java_lang_Class_setSigners(Java_java_lang_Class *, ArrayOf_Java_java_lang_Object *) { printf("Netscape_Java_java_lang_Class_setSigners() not implemented"); } /* * Class : java/lang/Class * Method : getPrimitiveClass * Signature : (Ljava/lang/String;)Ljava/lang/Class; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *) Netscape_Java_java_lang_Class_getPrimitiveClass(Java_java_lang_String *nameString) { if (!nameString) return NULL; char *className = ((JavaString *) nameString)->convertUtf(); const PrimitiveType *clazz = PrimitiveType::getClass(className); /* Free the memory we just allocated */ JavaString::freeUtf(className); return (Java_java_lang_Class *)clazz; } /* * Class : java/lang/Class * Method : getFields0 * Signature : (I)[Ljava/lang/reflect/Field; * * fieldCategory tells us what class of fields to return. * fieldCategory can have the values defined in enum FieldCategory, * defined above. */ NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_reflect_Field *) Netscape_Java_java_lang_Class_getFields0(Java_java_lang_Class *inClass, int32 category) { #if 0 ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); const Field **fields; Int32 numFields; if (category == fieldOrMethodCategoryPublic) numFields = clazz.getFields(fields); else numFields = clazz.getDeclaredFields(fields); const Type *classField = &VM::getStandardClass(cField); JavaArray *arr = (JavaArray *) sysNewObjectArray(classField, numFields); ArrayOf_Java_java_lang_reflect_Field *fieldArray = (ArrayOf_Java_java_lang_reflect_Field *) arr; for (Int32 i = 0; i < numFields; i++) fieldArray->elements[i] = (Java_java_lang_reflect_Field *) fields[i]; return fieldArray; #else bool publik = (category == fieldOrMethodCategoryPublic); ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); Int32 numFields = 0; if (publik) { Int32 i; /* Walk all interfaces that we implement */ Int32 interfaceCount; Interface **interfaces; interfaceCount = clazz.getInterfaces(interfaces); for (i = 0; i < interfaceCount; i++) { const Field **dummy; numFields += interfaces[i]->getFields(dummy); } /* Walk our instance fields from child to parent */ ClassOrInterface *tmp; for (tmp = &clazz; tmp; tmp = (ClassOrInterface *) tmp->getSuperClass()) { const Field **fieldsInThisClass; Int32 numFieldsInThisClass = tmp->getDeclaredFields(fieldsInThisClass); for (i = 0; i < numFieldsInThisClass; i++) if (fieldsInThisClass[i]->getModifiers() & CR_FIELD_PUBLIC) numFields++; } /* Allocate a JavaArray of the correct size */ const Type *classField = &VM::getStandardClass(cField); JavaArray *arr = (JavaArray *) sysNewObjectArray(classField, numFields); ArrayOf_Java_java_lang_reflect_Field *fieldArray = (ArrayOf_Java_java_lang_reflect_Field *) arr; Int32 index = 0; /* Walk the fields again, this time copying them into our array */ for (i = 0; i < interfaceCount; i++) { const Field **interfaceFields; Int32 numInterfaceFields = interfaces[i]->getFields(interfaceFields); for (Int32 j = 0; j < numInterfaceFields; j++) fieldArray->elements[index++] = (Java_java_lang_reflect_Field *) interfaceFields[j]; } /* Add instance fields, starting with the topmost superclass */ Vector vec; for (tmp = &clazz; tmp; tmp = (ClassOrInterface *)(tmp->getSuperClass())) vec.append(tmp); for (Int32 vectorIndex = vec.size(); vectorIndex > 0; vectorIndex--) { tmp = vec[vectorIndex-1]; const Field **fieldsInThisClass; Int32 numFieldsInThisClass = tmp->getDeclaredFields(fieldsInThisClass); for (i = 0; i < numFieldsInThisClass; i++) if (fieldsInThisClass[i]->getModifiers() & CR_FIELD_PUBLIC) fieldArray->elements[index++] = (Java_java_lang_reflect_Field *) fieldsInThisClass[i]; } assert(index == numFields); return fieldArray; } else { const Field **fields; numFields = clazz.getDeclaredFields(fields); const Type *classField = &VM::getStandardClass(cField); JavaArray *arr = (JavaArray *) sysNewObjectArray(classField, numFields); ArrayOf_Java_java_lang_reflect_Field *fieldArray = (ArrayOf_Java_java_lang_reflect_Field *) arr; for (Int32 i = 0; i < numFields; i++) fieldArray->elements[i] = (Java_java_lang_reflect_Field *) fields[i]; return fieldArray; } #endif } /* * Class : java/lang/Class * Method : getMethods0 * Signature : (I)[Ljava/lang/reflect/Method; */ NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_reflect_Method *) Netscape_Java_java_lang_Class_getMethods0(Java_java_lang_Class *inClass, int32 category) { ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); const Method **methods; Int32 numMethods; if (category == fieldOrMethodCategoryPublic) numMethods = clazz.getMethods(methods); else numMethods = clazz.getDeclaredMethods(methods); const Type *classMethod = &VM::getStandardClass(cMethod); JavaArray *arr = (JavaArray *) sysNewObjectArray(classMethod, numMethods); ArrayOf_Java_java_lang_reflect_Method *methodArray = (ArrayOf_Java_java_lang_reflect_Method *) arr; for (Int32 i = 0; i < numMethods; i++) methodArray->elements[i] = (Java_java_lang_reflect_Method *) methods[i]; return methodArray; } /* * Class : java/lang/Class * Method : getConstructors0 * Signature : (I)[Ljava/lang/reflect/Constructor; */ NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_reflect_Constructor *) Netscape_Java_java_lang_Class_getConstructors0(Java_java_lang_Class *inClass, int32 category) { ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); const Constructor **constructors; Int32 numConstructors; if (category == fieldOrMethodCategoryPublic) numConstructors = clazz.getConstructors(constructors); else numConstructors = clazz.getDeclaredConstructors(constructors); const Type *classConstructor = &VM::getStandardClass(cConstructor); JavaArray *arr = (JavaArray *) sysNewObjectArray(classConstructor, numConstructors); ArrayOf_Java_java_lang_reflect_Constructor *constructorArray = (ArrayOf_Java_java_lang_reflect_Constructor *) arr; for (Int32 i = 0; i < numConstructors; i++) constructorArray->elements[i] = (Java_java_lang_reflect_Constructor *) constructors[i]; return constructorArray; } /* * Class : java/lang/Class * Method : getField0 * Signature : (Ljava/lang/String;I)Ljava/lang/reflect/Field; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_reflect_Field *) Netscape_Java_java_lang_Class_getField0(Java_java_lang_Class *inClass, Java_java_lang_String *inName, int32 category) { if (!inName || !inClass) sysThrowNullPointerException(); char *name = ((JavaString *) inName)->convertUtf(); ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); const Field *field; try { if (category == fieldOrMethodCategoryPublic) field = &clazz.getField(name); else field = &clazz.getDeclaredField(name); } catch (RuntimeError) { sysThrowNamedException("java/lang/NoSuchFieldException"); } JavaString::freeUtf(name); return (Java_java_lang_reflect_Field *)field; } /* * Class : java/lang/Class * Method : getMethod0 * Signature : (Ljava/lang/String;[Ljava/lang/Class;I)Ljava/lang/reflect/Method; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_reflect_Method *) Netscape_Java_java_lang_Class_getMethod0(Java_java_lang_Class *inClass, Java_java_lang_String *inName, ArrayOf_Java_java_lang_Class *paramTypes, int32 category) { if (!inName) sysThrowNullPointerException(); Int32 numParams; const Type **params; if (!paramTypes) { Type *dummy[1]; params = (const Type **) dummy; numParams = 0; } else { params = (const Type **) paramTypes->elements; numParams = paramTypes->length; } ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); char *name = ((JavaString *) inName)->convertUtf(); const Method *method; try { if (category == fieldOrMethodCategoryPublic) method = &clazz.getMethod(name, params, numParams); else method = &clazz.getDeclaredMethod(name, params, numParams); } catch (RuntimeError) { sysThrowNamedException("java/lang/NoSuchMethodException"); } JavaString::freeUtf(name); return (Java_java_lang_reflect_Method *) method; } /* * Class : java/lang/Class * Method : getConstructor0 * Signature : ([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor; */ NS_EXPORT NS_NATIVECALL(Java_java_lang_reflect_Constructor *) Netscape_Java_java_lang_Class_getConstructor0(Java_java_lang_Class *inClass, ArrayOf_Java_java_lang_Class *paramTypes, int32 category) { ClassOrInterface &clazz = *(ClassOrInterface *) &toType(*inClass); const Type **params; Uint32 nParams; if (paramTypes == 0) { params = (const Type **) 0; nParams = 0; } else { params = (const Type **) paramTypes->elements; nParams = paramTypes->length; } Constructor *constructor; try { if (category == fieldOrMethodCategoryPublic) constructor = &clazz.getConstructor(params, nParams); else constructor = &clazz.getDeclaredConstructor(params, nParams); } catch (RuntimeError) { sysThrowNamedException("java/lang/NoSuchMethodException"); } return (Java_java_lang_reflect_Constructor *) constructor; } } /* extern "C" */