зеркало из https://github.com/mozilla/pjs.git
1348 строки
35 KiB
C++
1348 строки
35 KiB
C++
/* -*- 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.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
// 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<const ClassOrInterface *>(&type);
|
|
append(str, strLen, clazz->getName());
|
|
break;
|
|
}
|
|
|
|
case tkArray: {
|
|
const Array *array = static_cast<const Array *>(&type);
|
|
getTypeString(*const_cast<Type *> (&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<ClassOrInterface*>(static_cast<const ClassOrInterface *>(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<const Class *>(&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<const Type *>(&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<AttributeCode *>(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<AttributeCode *>(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<AttributeCode *>(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<const Class *>(&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<const Class *>(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<IntervalMapping>& mappings = entry.mappings;
|
|
for (DoublyLinkedList<IntervalMapping>::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<Class *>(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
|