/* -*- 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): */ // FieldOrMethod.cpp // // Sriram Kini // // Runtime representation of java fields or methods // Most of the operations performed on fields or methods // are located here. #include "ErrorHandling.h" #include "ClassCentral.h" #include "BytecodeTranslator.h" #include "PrimitiveOptimizer.h" #include "FieldOrMethod.h" #include "CUtils.h" #include "StringUtils.h" #include "NativeCodeCache.h" #include "Backend.h" #include "Debugger.h" #include "plstr.h" #include "prprf.h" #include "FieldOrMethod_md.h" #include "InterestingEvents.h" #include "JavaVM.h" UT_DEFINE_LOG_MODULE(FieldOrMethod); /* Convert value valueFrom, whose typekind is tkFrom, to a value of type tkTo. * Store the result in valueTo. It is assumed that valueTo is properly * aligned. If a widening conversion is not possible, throw an illegalArgument * exception. */ void Field::widen(void *valueFrom, void *valueTo, TypeKind tkFrom, TypeKind tkTo) { switch (tkTo) { case tkBoolean: /* Can't widen to these */ case tkChar: case tkByte: goto cantConvert; case tkShort: if (tkFrom == tkByte) *(Int16 *) valueTo = (Int16) *(Int8 *) valueFrom; else goto cantConvert; case tkInt: switch (tkFrom) { case tkByte: *(Int32 *) valueTo = (Int32) *(Int8 *) valueFrom; break; case tkChar: case tkShort: *(Int32 *) valueTo= (Int32) *(Int16 *) valueFrom; break; default: goto cantConvert; } break; case tkLong: switch (tkFrom) { case tkByte: *(Int64 *) valueTo = (Int64) *(Int8 *) valueFrom; break; case tkChar: case tkShort: *(Int64 *) valueTo = (Int64) *(Int16 *) valueFrom; break; case tkInt: *(Int64 *) valueTo = (Int64) *(Int32 *) valueFrom; break; default: goto cantConvert; } break; case tkFloat: switch (tkFrom) { case tkByte: *(Flt32 *) valueTo = (Flt32) *(Int8 *) valueFrom; break; case tkChar: case tkShort: *(Flt32 *) valueTo = (Flt32) *(Int16 *) valueFrom; break; case tkInt: *(Flt32 *) valueTo = (Flt32) *(Int32 *) valueFrom; break; case tkLong: *(Flt32 *) valueTo = (Flt32) *(Int64 *) valueFrom; break; default: goto cantConvert; } break; case tkDouble: switch (tkFrom) { case tkByte: *(Flt64 *) valueTo = (Flt64) *(Int8 *) valueFrom; break; case tkChar: case tkShort: *(Flt64 *) valueTo = (Flt64) *(Int16 *) valueFrom; break; case tkInt: *(Flt64 *) valueTo = (Flt64) *(Int32 *) valueFrom; break; case tkLong: *(Flt64 *) valueTo = (Flt64) *(Int64 *) valueFrom; break; case tkFloat: *(Flt64 *) valueTo = (Flt64) *(Flt32 *) valueFrom; break; default: goto cantConvert; } break; default: runtimeError(RuntimeError::internal); } return; cantConvert: runtimeError(RuntimeError::illegalArgument); } /* Return the full name of the field or method from the short name and the * class name. A full name is of the form "public static int java.lang.foo.bar"; * the correspoding package name is java.lang.foo, and the short name is bar. */ const char *Field::getFullName() { const char *accessString = ((modifiers & CR_FIELD_STATIC)) ? "static " : ""; const char *statusString; const char *finalString = ((modifiers & CR_FIELD_FINAL)) ? "final " : ""; if ((modifiers & CR_FIELD_PUBLIC)) statusString = "public "; else if ((modifiers & CR_FIELD_PROTECTED)) statusString = "protected "; else statusString = ""; char *typeString = new char[20]; Uint32 typeStrLen = 20; *typeString = 0; getTypeString(*typeOfField, typeString, typeStrLen); char *_fullName; /* The fullname is the name of the class appended by a dot, appended * by the field name */ Uint32 outlen = PL_strlen(accessString) + PL_strlen(finalString)+ PL_strlen(typeString) + PL_strlen(statusString)+ PL_strlen(className) + PL_strlen(packageName) + PL_strlen(shortName) + 8; _fullName = new char[outlen]; if (packageName && *packageName) PR_snprintf(_fullName, outlen, "%s%s%s%s %s.%s.%s", statusString, accessString, finalString, typeString, packageName, className, shortName); else PR_snprintf(_fullName, outlen, "%s%s%s%s %s.%s", statusString, accessString, finalString, typeString, className, shortName); fullName = central.getStringPool().intern(_fullName); delete [] _fullName; delete [] typeString; return fullName; } /* Appends the string for the type to str, updating strLen */ void FieldOrMethod::getTypeString(const Type &type, char *&str, Uint32 &strLen) { switch (type.typeKind) { case tkBoolean: append(str, strLen, "boolean"); break; case tkUByte: append(str, strLen, "unsigned byte"); break; case tkByte: append(str, strLen, "byte"); break; case tkChar: append(str, strLen, "byte"); break; case tkShort: append(str, strLen, "short"); break; case tkInt: append(str, strLen, "int"); break; case tkLong: append(str, strLen, "long"); break; case tkFloat: append(str, strLen, "float"); break; case tkDouble: append(str, strLen, "double"); break; case tkObject: case tkInterface: { const ClassOrInterface *clazz = static_cast(&type); append(str, strLen, clazz->getName()); break; } case tkArray: { const Array *array = static_cast(&type); getTypeString(*const_cast (&array->componentType), str, strLen); append(str, strLen, "[]"); break; } default: runtimeError(RuntimeError::unknown); break; } } /* Convert a JVM type descriptor into the long form defined by * java.lang.reflect, e.g. [[C is emitted as "char[][]" * Advances descriptor to point to the point beyond the argument just * parsed. Appends description of parsed argument to out. */ static bool descriptorToString(char *out, Uint32 outLen, const char * &descriptor) { char *typeString; switch (*descriptor) { case 'B': typeString = "byte"; break; case 'C': typeString = "char"; break; case 'D': typeString = "double"; break; case 'F': typeString = "float"; break; case 'I': typeString = "int"; break; case 'J': typeString = "long"; break; case 'S': typeString = "short"; break; case 'Z': typeString = "boolean"; break; case 'V': typeString = "void"; break; case '[': descriptor++; descriptorToString(out, outLen, descriptor); append(out, outLen, "[]"); return true; case 'L': { descriptor++; char *semicolon = strchr(descriptor, ';'); if (!semicolon) verifyError(VerifyError::badClassFormat); char *tmpStr = new char[semicolon - descriptor + 1]; char *t = tmpStr; while (*descriptor != ';') { if (*descriptor == '/') *t++ = '.'; else *t++ = *descriptor; descriptor++; } *t = 0; descriptor++; // Advance past semi-colon append(out, outLen, tmpStr); delete [] tmpStr; return true; } case ')': return false; default: verifyError(VerifyError::badClassFormat); return false; // NOTREACHED } append(out, outLen, typeString); descriptor++; return true; } /* Return the full name of the method from the short name and the * class name. A full name is of the form * "public static java.lang.foo.bar(int, long, java.lang.Object)". * the corresponding package name is java.lang.foo, and the short name is bar. */ const char *Method::getFullName() { const char *accessString = ((modifiers & CR_METHOD_STATIC)) ? "static " : ""; const char *statusString; if ((modifiers & CR_METHOD_PUBLIC)) statusString = "public "; else if ((modifiers & CR_METHOD_PROTECTED)) statusString = "protected "; else if ((modifiers & CR_METHOD_PRIVATE)) statusString = "private "; else statusString = ""; const char *abstractString = ((modifiers & CR_METHOD_ABSTRACT)) ? "abstract " : ""; char *returnString = new char[50]; Uint32 returnStringLen = 50; *returnString = 0; char *argsString = new char[256]; Uint32 argsStringLen = 256; *argsString = 0; const char *s = signatureString; if (*s++ != '(') verifyError(VerifyError::badClassFormat); // Check for method which takes no arguments if (*s == ')') append(argsString, argsStringLen, "()"); else { append(argsString, argsStringLen, "("); // Convert each method argument to string while (descriptorToString(argsString, argsStringLen, s)) append(argsString, argsStringLen, ","); if (*s != ')') { delete [] argsString; delete [] returnString; verifyError(VerifyError::badClassFormat); } /* overwrite the last comma with closing paren and null */ argsString[PL_strlen(argsString) - 1] = ')'; } /* Now parse the return argument, only if we're not a constructor*/ if (shortName != summary.getInitString()) { s++; if (!descriptorToString(returnString, returnStringLen, s)) { delete [] argsString; delete [] returnString; verifyError(VerifyError::badClassFormat); } append(returnString, returnStringLen, " "); } char *_fullName; /* Allocate enough space to hold strings for arguments */ Uint32 outLen = PL_strlen(accessString)+PL_strlen(statusString)+ PL_strlen(abstractString)+PL_strlen(returnString)+ PL_strlen(className)+PL_strlen(shortName)+PL_strlen(packageName)+PL_strlen(argsString)+5; _fullName = new char[outLen]; if (shortName == summary.getInitString()) { if (packageName && *packageName) PR_snprintf(_fullName, outLen, "%s%s%s%s.%s%s", accessString, statusString, returnString, packageName, className, argsString); else PR_snprintf(_fullName, outLen, "%s%s%s%s%s", accessString, statusString, returnString, className, argsString); } else { if (packageName && *packageName) PR_snprintf(_fullName, outLen, "%s%s%s%s%s.%s.%s%s", accessString, statusString, abstractString, returnString, packageName, className, shortName, argsString); else PR_snprintf(_fullName, outLen, "%s%s%s%s%s.%s%s", accessString, statusString, abstractString, returnString, className, shortName, argsString); } fullName = central.getStringPool().intern(_fullName); delete [] returnString; delete [] argsString; delete [] _fullName; return fullName; } /* Class Field */ /* Return the size occupied by objects whose type is represented by type */ static Uint32 getTypeSize(const Type &type) { if (isNonVoidPrimitiveKind(type.typeKind)) { return isDoublewordKind(type.typeKind) ? 8 : 4; //getTypeKindSize(type.typeKind); } else if (type.typeKind == tkObject || type.typeKind == tkInterface) { return sizeof(ptr); } else if (type.typeKind == tkArray) { return sizeof(ptr); //Arrays and objects are implemented as references } else return 0; } /* Construct a field given it's packageName, className and shortName. * (Also see FieldOrMethod::FieldOrMethod). * summary is the information about the declaring class; central is * the repository used to hold ClassFileSummary structures. * info contains compile-time information about the field. * offset is the offset of this field in the declaring class if * the field is an instance (ie., non-static) field. */ Field::Field(const char *packageName, const char *className, const char *shortName, ClassFileSummary &summary, ClassCentral &c, Pool &pool, Uint32 offset, const FieldInfo &info) : FieldOrMethod(Standard::get(cField), packageName, className, shortName, info.getDescriptor()->getUtfString(), summary, c, pool) { bool isStatic, isVolatile, isConstant; const char *sig; info.getInfo(sig, isVolatile, isConstant, isStatic); const char *next; typeOfField = ¢ral.parseFieldDescriptor(sig, next); modifiers = info.getAccessFlags(); if (isStatic) { Uint32 size = getTypeKindSize(typeOfField->typeKind); pos.address = staticAddress(new (p) char[size]); memset(addressFunction(pos.address), 0, size); } else pos.offset = offset; } // Get the memory address of a static field addr Field::getAddress() const { assert((modifiers & CR_FIELD_STATIC)); // If static initializers haven't been run, execute them now. (const_cast(static_cast(getDeclaringClass())))->runStaticInitializers(); return pos.address; } /* Return a JavaObject from the raw bytes located at address. For * primitive types, wrap an object wrapper around it */ JavaObject &Field::convertObject(void *address) { if (isPrimitiveKind(typeOfField->typeKind)) return Class::makePrimitiveObject(typeOfField->typeKind, address); else { /* For object and array types, what's stored is a pointer to the * JavaObject */ JavaObject *objp = *((JavaObject **)address); return *objp; } } /* Get the raw address of the object represented by this field */ void *Field::getRaw(JavaObject *obj) { void *address; if ((modifiers & CR_FIELD_STATIC)) { address = addressFunction(getAddress()); } else { if (!obj) runtimeError(RuntimeError::nullPointer); // Make sure that the type of the object is the // same as the declaring class of this field, or // a sub-class if (! (&obj->getType() == getDeclaringClass() || (getDeclaringClass()->isAssignableFrom(obj->getType()))) ) runtimeError(RuntimeError::illegalArgument); address = (void *) (((char *) obj) + pos.offset); } return address; } /* Get the value of the field, wrapped in a JavaObject * if neccessary */ JavaObject &Field::get(JavaObject *obj) { void *address = getRaw(obj); return convertObject(address); } // Return true if this field represents a primitive type inline bool Field::isPrimitive() { return (typeOfField->isPrimitive()); } #define FIELD_GET_PRIMITIVE(typeTo, tkTo) \ PR_BEGIN_MACRO \ if (!isPrimitive()) \ runtimeError(RuntimeError::illegalArgument); \ \ typeTo value; \ void *address = getRaw(obj); \ \ if (typeOfField->typeKind == tkTo) \ return *(typeTo *) address; \ else { \ widen(address, &value, typeOfField->typeKind, tkTo);\ return (typeTo) value; \ } \ PR_END_MACRO /* Get the value of this field within the object obj, * converted to boolean. */ Int8 Field::getBoolean(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int8, tkBoolean); } Int8 Field::getByte(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int8, tkByte); } Int16 Field::getChar(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int16, tkChar); } Int16 Field::getShort(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int16, tkShort); } Int32 Field::getInt(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int32, tkInt); } Int64 Field::getLong(JavaObject *obj) { FIELD_GET_PRIMITIVE(Int64, tkLong); } Flt32 Field::getFloat(JavaObject *obj) { FIELD_GET_PRIMITIVE(Flt32, tkFloat); } Flt64 Field::getDouble(JavaObject *obj) { FIELD_GET_PRIMITIVE(Flt32, tkDouble); } void Field::setFromValue(ValueKind vk, Value value) { assert(getModifiers() & CR_FIELD_STATIC); void *address = getRaw(0); const char *sig = getSignatureString(); switch (vk) { case vkInt: if (*sig != 'I' && *sig != 'B' && *sig != 'C' && *sig != 'S' && *sig != 'Z') verifyError(VerifyError::badClassFormat); *(Int32 *) address = value.getValueContents((Int32 *) 0); break; case vkLong: if (*sig != 'J') verifyError(VerifyError::badClassFormat); *(Int64 *) address = value.getValueContents((Int64 *) 0); break; case vkFloat: if (*sig != 'F') verifyError(VerifyError::badClassFormat); *(Flt32 *) address = value.getValueContents((Flt32 *) 0); break; case vkDouble: if (*sig != 'D') verifyError(VerifyError::badClassFormat); *(Flt64 *) address = value.getValueContents((Flt64 *) 0); break; case vkAddr: if (PL_strcmp(sig, "Ljava/lang/String;")) verifyError(VerifyError::badClassFormat); *(JavaObject **) address = (JavaObject *) addressFunction(value.getValueContents((addr *) 0)); break; default: verifyError(VerifyError::badClassFormat); } } void Field::set(JavaObject *obj, JavaObject &value) { void *address = getRaw(obj); int size = getTypeKindSize(typeOfField->typeKind); /* If this is a primitive type, then it is encased in a Java Object; * otherwise, it is the object itself */ if (isPrimitiveKind(typeOfField->typeKind)) { if (!Standard::isPrimitiveWrapperClass(*static_cast(&value.getType()))) runtimeError(RuntimeError::illegalArgument); memcpy(address, (char *)&value+sizeof(JavaObject), size); } else { assert(size == sizeof(char *)); *((JavaObject **) address) = &value; } } // Set the value of the field in various ways // This code is dependent on our current layout, and // must be reworked when our layout changes #define FIELD_SET_PRIMITIVE(typeFrom, tkFrom) \ PR_BEGIN_MACRO \ if (!isPrimitive()) \ runtimeError(RuntimeError::illegalArgument); \ \ void *address = getRaw(obj); \ \ if (typeOfField->typeKind == tkFrom) \ *(typeFrom *) address = value; \ else \ widen(&value, address, tkFrom, typeOfField->typeKind); \ PR_END_MACRO void Field::setBoolean(JavaObject *obj, Int8 value) { FIELD_SET_PRIMITIVE(Int8, tkBoolean); } void Field::setByte(JavaObject *obj, Int8 value) { FIELD_SET_PRIMITIVE(Int8, tkByte); } void Field::setChar(JavaObject *obj, Int16 value) { FIELD_SET_PRIMITIVE(Int16, tkChar); } void Field::setShort(JavaObject *obj, Int16 value) { FIELD_SET_PRIMITIVE(Int16, tkShort); } void Field::setInt(JavaObject *obj, Int32 value) { FIELD_SET_PRIMITIVE(Int32, tkInt); } void Field::setLong(JavaObject *obj, Int64 value) { FIELD_SET_PRIMITIVE(Int64, tkLong); } void Field::setFloat(JavaObject *obj, Flt32 value) { FIELD_SET_PRIMITIVE(Flt32, tkFloat); } void Field::setDouble(JavaObject *obj, Flt64 value) { FIELD_SET_PRIMITIVE(Flt64, tkDouble); } /* Class Method */ /* Parses the type descriptor of the method, resolving and * loading classes as necessary. Creates the signature structure * for the method. */ void Method::parseMethodDescriptor(const char *s) { char *paramstr; const char *t; const Type **params, *ret; int paramSize = 0; int numParams = 0; bool isStatic; if (!(isStatic = (getModifiers() & CR_METHOD_STATIC) != 0)) { /* Pass in ourselves as the first argument */ numParams = 1; params = new (p) const Type *[(paramSize = 5)]; params[0] = getDeclaringClass(); } if (*s++ != '(') verifyError(VerifyError::badClassFormat); /* Get everything between this and ')' */ if (!(t = PL_strchr(s, ')'))) verifyError(VerifyError::badClassFormat); int len = t-s+1; if (len != 1) { TemporaryStringCopy copy(s, len-1); paramstr = copy; const char *next = paramstr; while (*next) { if (numParams+1 > paramSize) { paramSize += 5; const Type **oldParams = params; params = new (p) const Type *[paramSize]; for (int i = 0; i < numParams; i++) params[i] = oldParams[i]; /* We don't do this since we're using a pool to allocate, but * this is a potential source of runaway memory usage */ /* delete [] oldParams;*/ } params[numParams] = ¢ral.parseFieldDescriptor(next, next); numParams++; } } s += len; /* Parse the return descriptor */ if (*s == 'V') { if (*(s+1)) verifyError(VerifyError::badClassFormat); ret = &PrimitiveType::obtain(tkVoid); } else { const char *next; ret = ¢ral.parseFieldDescriptor(s, next); if (*next) verifyError(VerifyError::badClassFormat); } signature.nArguments = numParams; signature.argumentTypes = params; signature.resultType = ret; signature.isStatic = isStatic; } /* Create a Method object for a method with given packageName, className * and shortName. (Also see FieldOrMethod::FieldOrMethod). * summary is the information about the declaring class; central is * the repository used to hold ClassFileSummary structures. * info contains compile-time information about the method. * vtableType is the type of the object to be passed on to the * JavaObject constructor. */ Method::Method(const char *packageName, const char *className, const char *shortName, ClassFileSummary &summary, ClassCentral &c, Pool &pool, const MethodInfo &info, const Type *vtableType) : FieldOrMethod((vtableType) ? *vtableType : *static_cast(&Standard::get(cMethod)), packageName, className, shortName, info.getDescriptor()->getUtfString(), summary, c, pool), minfo(info), argsSize(-1), dynamicallyDispatched(false), vIndex(-1), signatureResolved(false) { modifiers = info.getAccessFlags(); bool isAbstract, isStatic, isFinal, isSynchronized, isNative; const char *sig; minfo.getInfo(sig, isAbstract, isStatic, isFinal, isSynchronized, isNative); signatureString = sig; DEBUG_LOG_ONLY(htmlName = NULL); } #ifdef DEBUG_LOG void Method::printMethodAsBytecodes(LogModuleObject &f) { const Signature &signature = getSignature(); AttributeCode *code = static_cast(minfo.getAttribute("Code")); assert(code); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("Signature: ")); UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("%d locals, %d stack words, %d code bytes\n", code->getMaxLocals(), code->getMaxStack(), code->getCodeLength())); if (modifiers & CR_METHOD_ABSTRACT) UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("abstract ")); if (modifiers & CR_METHOD_STATIC) UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("static ")); if (modifiers & CR_METHOD_FINAL) UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("final ")); if (modifiers & CR_METHOD_SYNCHRONIZED) UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("synchronized ")); if (modifiers & CR_METHOD_NATIVE) UT_OBJECTLOG(f, PR_LOG_ALWAYS, ("native ")); signature.resultType->printRef(UT_LOG_MODULE(FieldOrMethod)); UT_OBJECTLOG(f, PR_LOG_ALWAYS, (" %s(", shortName)); if (signature.nArguments) { uint i = 0; while (true) { signature.argumentTypes[i]->printRef(f); i++; if (i == signature.nArguments) break; UT_OBJECTLOG(f, PR_LOG_ALWAYS, (", ")); } } UT_OBJECTLOG(f, PR_LOG_ALWAYS, (")\n")); const bytecode *bytecodesBegin = (const bytecode *)code->getCode(); disassembleBytecodes(f, bytecodesBegin, bytecodesBegin + code->getCodeLength(), bytecodesBegin, summary.getConstantPool(), 0); disassembleExceptions(f, code->getNumExceptions(), code->getExceptions(), summary.getConstantPool(), 0); } #endif // DEBUG_LOG // A little macro that just compares our stage to test stage and // causes the function to return if we are past our desired stage #define COMPILE_UP_TO_STAGE(inOurStage, inTestStage) \ PR_BEGIN_MACRO \ if (inOurStage < inTestStage) \ return (0); \ PR_END_MACRO void* Method::compile() { Class *clazz = (Class *) getDeclaringClass(); bool inhibitBackpatching = VM::theVM.getInhibitBackpatching(); CompileStage stage = VM::theVM.getCompileStage(); UT_LOG(FieldOrMethod, PR_LOG_ALWAYS, ("\n\n\n\n*** Compiling method %s::%s\n", clazz->name, getName())); // preprocess bytecodes gCompileBroadcaster->broadcastEvent(gCompileBroadcaster, kBroacastCompile, this); if (modifiers & CR_METHOD_ABSTRACT) // not allowed to compile abstract methods verifyError(VerifyError::abstractMethod); AttributeCode *code = static_cast(minfo.getAttribute("Code")); if (!code) { UT_LOG(FieldOrMethod, PR_LOG_DEBUG, ("No code attribute\n")); runtimeError(RuntimeError::illegalAccess); } COMPILE_UP_TO_STAGE(stage, csPreprocess); Uint32 maxLocals = code->getMaxLocals(); Uint32 maxStack = code->getMaxStack(); const bytecode *bytecodesBegin = (const bytecode *)code->getCode(); Uint32 codeLength = code->getCodeLength(); const Signature &signature = getSignature(); const Type **argumentTypes = signature.argumentTypes; const Type *resultType = signature.resultType; int nArguments = signature.nArguments; DEBUG_LOG_printMethodAsBytecodes(UT_LOG_MODULE(FieldOrMethod)); // create bytecode graph UT_LOG(FieldOrMethod, PR_LOG_ALWAYS, ("creating bytecode graph...\n")); Pool primitivePool; ControlGraph *cg; { Pool bytecodeGraphPool; ValueKind *argumentKinds = new(bytecodeGraphPool) ValueKind[nArguments]; for (int i = 0; i != nArguments; i++) argumentKinds[i] = typeKindToValueKind(argumentTypes[i]->typeKind); bool isStatic = (getModifiers() & CR_METHOD_STATIC) != 0; bool isSynchronized = (getModifiers() & CR_METHOD_SYNCHRONIZED) != 0; BytecodeGraph bg(summary, bytecodeGraphPool, !isStatic, isSynchronized, nArguments, argumentKinds, typeKindToValueKind(resultType->typeKind), maxLocals, maxStack, bytecodesBegin, codeLength, code->getNumExceptions(), code->getExceptions(), *this); { Pool tempPool1; UT_LOG(FieldOrMethod, PR_LOG_ALWAYS, ("dividing into blocks...\n\n")); bg.divideIntoBlocks(tempPool1); DEBUG_LOG_ONLY(bg.print(UT_LOG_MODULE(FieldOrMethod), summary.getConstantPool())); COMPILE_UP_TO_STAGE(stage, csGenPrimitives); // tempPool1 is destroyed here. } { // create primitive graph Pool tempPool2; cg = BytecodeTranslator::genPrimitiveGraph(bg, primitivePool, tempPool2); // tempPool2 is destroyed here. } // bytecodeGraphPool is destroyed here. } if (stage < csOptimize) { DEBUG_LOG_ONLY(cg->print(UT_LOG_MODULE(FieldOrMethod))); return 0; } // optimize UT_LOG(FieldOrMethod, PR_LOG_ALWAYS, ("optimizing primitive graph...\n")); simpleTrimDeadPrimitives(*cg); DEBUG_LOG_ONLY(cg->print(UT_LOG_MODULE(FieldOrMethod))); COMPILE_UP_TO_STAGE(stage, csGenInstructions); // generate code void *compiledCode = translateControlGraphToNative(*cg, *this); UT_LOG(FieldOrMethod, PR_LOG_ALWAYS, ("*** Done Compiling Method %s::%s\n", clazz->name, shortName)); // anyone know what this is? #ifdef DEBUG_LOG if (VM::debugger.getEnabled()) printDebugInfo(); #endif if (!inhibitBackpatching) updateVTable(); gCompileBroadcaster->broadcastEvent(gCompileBroadcaster, kBroacastCompile, this); return compiledCode; } // Disassemble the bytecode to the specified logmodule #ifdef DEBUG_LOG void Method::dumpBytecodeDisassembly(LogModuleObject &f) { AttributeCode *code = static_cast(minfo.getAttribute("Code")); const bytecode *bytecodesBegin = (const bytecode *)code->getCode(); Uint32 codeLength = code->getCodeLength(); const ConstantPool *constantPool = summary.getConstantPool(); disassembleBytecodes(f, bytecodesBegin, bytecodesBegin + codeLength, bytecodesBegin, constantPool, 0); } #endif // Examine the type of the argument arg against the actual type type. // If arg is a primitive wrapper class, extract the primitive value // from the wrapper class. Otherwise, pass the arg on unchanged. Uint32 Method::getWordArg(const Type &actualType, JavaObject &arg) { // Check arguments for correctness if (isPrimitiveKind(actualType.typeKind)) { if (Standard::isPrimitiveWrapperClass(*static_cast(&arg.getType()))) return Class::getPrimitiveValue(arg); else { runtimeError(RuntimeError::illegalArgument); return 0; } } else { // Ensure that arg is assignable to our argument return (Uint32) &arg; } } void Method::getDoubleWordArg(const Type &, JavaObject &arg, Uint32 &hi, Uint32 &lo) { struct { Uint32 lo; Uint32 hi; } u; memcpy(&u, ((char *) &arg+sizeof(JavaObject)), 8); hi = u.hi; lo = u.lo; } JavaObject *Method::invoke(JavaObject * PC_ONLY(obj), JavaObject * PC_ONLY(args)[], Int32 PC_ONLY(nArgs)) { #ifdef GENERATE_FOR_X86 Int32 i; Int32 start = 0; Uint32 *argsToBePassed = 0; // Make sure we're passing the right kind of object if (!(getModifiers() & CR_METHOD_STATIC)) if (!obj || !getDeclaringClass()->isAssignableFrom(*obj->type)) runtimeError(RuntimeError::illegalArgument); // Make sure we're passing the right number of arguments Int32 nArgsToCompare = ((getModifiers() & CR_METHOD_STATIC)) ? getSignature().nArguments : getSignature().nArguments-1; if (nArgs != nArgsToCompare) runtimeError(RuntimeError::illegalArgument); // If we're an instance method, pass ourselves in as the first argument if ((getModifiers() & CR_METHOD_STATIC) == 0) { start = 1; argsToBePassed = new Uint32[++nArgs*2]; argsToBePassed[0] = (Uint32) obj; } else argsToBePassed = new Uint32[nArgs*2]; Int32 nWords = 0; // Number of words to push on the stack for (nWords = start, i = start; i < nArgs; i++) { // FIX Currently assumes none of the args are double-word if (!Standard::isDoubleWordPrimitiveWrapperClass(*static_cast(signature.argumentTypes[i]))) argsToBePassed[nWords++] = (args[i-start]) ? getWordArg(*signature.argumentTypes[i], *args[i-start]): 0; else { if (args[i-start]) getDoubleWordArg(*signature.argumentTypes[i], *args[i - start], argsToBePassed[nWords], argsToBePassed[nWords+1]); else argsToBePassed[nWords] = argsToBePassed[nWords+1] = 0; nWords += 2; } } #if 0 //Uint32 returnValue; for (Int32 n = nWords - 1; n >= 0; n--) { void *arg = (void *) argsToBePassed[n]; prepareArg(n, arg); } callFunc(code); getReturnValue(returnValue); #else ReturnValue returnValue = invokeRaw(obj, argsToBePassed, nWords); #endif if (argsToBePassed) delete [] argsToBePassed; // Wrap up the return value inside an object if neccessary if (signature.resultType->typeKind != tkVoid) { if (isPrimitiveKind(signature.resultType->typeKind)) { if (!isDoublewordKind(signature.resultType->typeKind)) return &Class::makePrimitiveObject(signature.resultType->typeKind, (void *) &returnValue.wordValue); else return &Class::makePrimitiveObject(signature.resultType->typeKind, (void *) &returnValue.doubleWordValue); } else return (JavaObject *) returnValue.wordValue; } else return 0; #else trespass("Method::invoke() not implemented for this platform\n"); return 0; #endif } ReturnValue Method::invokeRaw(JavaObject* /*obj*/, Uint32 argsToBePassed[], Int32 nWords) { Uint32 wordRet; ReturnValue returnValue; void *code = addressFunction(getCode()); for (Int32 n = nWords - 1; n >= 0; n--) { void *arg = (void *) argsToBePassed[n]; prepareArg(n, arg); } callFunc(code); getReturnValue(wordRet); // XXX Double-word return values are currently broken assert(!isDoublewordKind(getSignature().resultType->typeKind)); returnValue.wordValue = wordRet; return returnValue; } #undef PC_ONLY Int32 Method::getArgsSize() { if (argsSize >= 0) return argsSize; Int32 size = 0; // Make sure the signature has been created getSignature(); for (uint i = 0; i < signature.nArguments; i++) { const Type *t = signature.argumentTypes[i]; assert(t); size += getTypeSize(*t); } return (argsSize = size); } #ifdef DEBUG_LOG // // Print a reference to this method for debugging purposes. // Return the number of characters printed. // int Method::printRef(LogModuleObject &f) const { int o = getDeclaringClass()->printRef(f); return o + UT_OBJECTLOG(f, PR_LOG_ALWAYS, (".%s", shortName)); } #endif /* Return a pointer to the compiled code of the method. This might be a * stub that actually compiles the method when called. This stub may or * may not "backpatch" the call site to point to the newly compiled method * depending on whether or not the method is normally statically dispatched. * This backpatching is not done when inhibitBackpatch is true. (This * argument is used for implementations of interface methods which are * always dispatched using a vtable and which, therefore, shouldn't * backpatch their caller.) */ addr Method::getCode(bool inhibitBackpatch) { MethodDescriptor descriptor(*this); return NativeCodeCache::getCache().lookupByDescriptor(descriptor, inhibitBackpatch); } // Checks to see that the given className, methodName and signature // represent this method. bool Method::isSelf(const char *fullyQualifiedClassName, const char *simpleMethodName, const char *javaSig) { Class *clazz = (Class *) getDeclaringClass(); return (((fullyQualifiedClassName == clazz->getName()) || (*fullyQualifiedClassName == '*')) && ((simpleMethodName == shortName) || (*simpleMethodName == '*')) && ((javaSig == signatureString) || (*javaSig == '*'))); } #ifdef DEBUG_LOG // Dumps the local variable table with the debug information void Method::printDebugInfo() { AttributeCode *code = (AttributeCode *) getMethodInfo().getAttribute("Code"); AttributeLocalVariableTable *localVariableTable = (AttributeLocalVariableTable *) code->getAttribute("LocalVariableTable"); if (!localVariableTable) { UT_LOG(Debugger, PR_LOG_ALWAYS, ("No debug info\n")); return; } // Walk the local variable table Uint32 n = localVariableTable->getNumEntries(); for(Uint32 i=0; i < n; i++) { LocalVariableEntry& entry = localVariableTable->getEntryByNumber(i); entry.dump(); DoublyLinkedList& mappings = entry.mappings; for (DoublyLinkedList::iterator j = mappings.begin(); !mappings.done(j); j = mappings.advance(j)) { IntervalMapping& mapping = mappings.get(j); mapping.dump(); } } } #endif /* Class Constructor */ JavaObject &Constructor::newInstance(JavaObject *params[], Int32 numParams) { ClassOrInterface *declaringClass = getDeclaringClass(); if (declaringClass->isArray()) runtimeError(RuntimeError::illegalArgument); JavaObject &newObject = (static_cast(declaringClass))->newInstance(); invoke(&newObject, params, numParams); return newObject; } #ifdef DEBUG_LOG void Method::resolveHTMLName() { if (htmlName) return; // gererate a hash value from the fully qualified name const char* p = toString(); Uint32 hash = 0; while(*p) hash = (hash << 1) + *p++; char hashText[11]; sprintf(hashText, "[%08x]", hash); // name length const char* name = getName(); Uint32 len = strlen(name); if(len > 16) len = 16; htmlName = new char[len + 10 + 5 + 1]; // copy name and clean out undesirable chars htmlName[len] = 0; strncpy(htmlName, name, len); char* q = htmlName; while(*q) { switch(*q) { case ':': case '<': case '>': case '/': case ' ': *q = '_'; } q++; } // append hash vale and .html strcat(htmlName, hashText); strcat(htmlName, ".html"); Uint32 newLen = strlen(htmlName); assert(newLen < 32); } #endif // DEBUG_LOG