/*
 * javaObj.c --
 *
 *	This file implements the routines that maintain the correspondence
 *	between TclObject instances and Tcl_Obj * references.
 *
 * Copyright (c) 1997 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: javaObj.c,v 1.17 2002/12/31 20:16:27 mdejong Exp $
 */

#include "java.h"
#include "javaNative.h"

static void		DupJavaCmdInternalRep(Tcl_Obj *srcPtr,
			    Tcl_Obj *dupPtr);
static void		DupTclObject(Tcl_Obj *srcPtr, Tcl_Obj *destPtr);
static void		FreeJavaCmdInternalRep(Tcl_Obj *objPtr);
static void		FreeTclObject(Tcl_Obj *objPtr);
static int		SetJavaCmdFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static int		SetTclObject(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void		UpdateTclObject(Tcl_Obj *objPtr);

static void
ThrowNullPointerException(
    JNIEnv *env,		/* Java environment pointer. */
    char *msg)			/* Message to include in exception. */
{
    jclass nullClass = (*env)->FindClass(env,
	    "java/lang/NullPointerException");
    if (!msg) {
	msg = "Invalid CObject.";
    }
    (*env)->ThrowNew(env, nullClass, msg);
    (*env)->DeleteLocalRef(env, nullClass);
}

/*
 * TclObject type information.
 */

Tcl_ObjType tclObjectType = {
     "TclObject",
     FreeTclObject,
     DupTclObject,
     UpdateTclObject,
     SetTclObject
};

/*
 * Pointer to old cmdType information.
 */

static Tcl_ObjType oldCmdType;
static Tcl_ObjType *cmdTypePtr = NULL;
static Tcl_ObjType *listTypePtr = NULL;

/*
 * Mutex to serialize access to cmdTypePtr.
 */

static Tcl_Mutex cmdTypePtrLock;


/*
 *----------------------------------------------------------------------
 *
 * JavaObjInit --
 *
 *	Initialize the JavaObj module.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Registers the TclObject type and hijacks the cmdName type.
 *
 *----------------------------------------------------------------------
 */

void
JavaObjInit()
{
    /*
     * The JavaObjInit method could get called
     * from multiple threads. We only want to
     * init the object type once.
     */

    Tcl_MutexLock(&cmdTypePtrLock);

    if (cmdTypePtr == NULL) {
        Tcl_RegisterObjType(&tclObjectType);

        /*
         * Interpose on the "cmdName" type to preserve
         * java objects.
         */

        cmdTypePtr = (Tcl_ObjType *) Tcl_GetObjType("cmdName");
        oldCmdType = *cmdTypePtr;
        cmdTypePtr->freeIntRepProc = FreeJavaCmdInternalRep;
        cmdTypePtr->dupIntRepProc = DupJavaCmdInternalRep;
        cmdTypePtr->setFromAnyProc = SetJavaCmdFromAny;

        /*
         * Grab a pointer to the Tcl list type.
         */

        listTypePtr = (Tcl_ObjType *) Tcl_GetObjType("list");
    }

    Tcl_MutexUnlock(&cmdTypePtrLock);
}

/*
 *----------------------------------------------------------------------
 *
 * printString --
 *
 *	Dump the string representation of an object to stdout. This
 *	function is purely for debugging purposes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
printString(
    JNIEnv *env,		/* Java environment. */
    jobject object)
{
    JavaInfo* jcache = JavaGetCache();
    jstring string = (*env)->CallObjectMethod(env, object, jcache->toString);
    const char *str = (*env)->GetStringUTFChars(env, string, NULL);
    printf("toString: %x '%s'\n", (unsigned int) object, str);
    (*env)->ReleaseStringUTFChars(env, string, str);
    (*env)->DeleteLocalRef(env, string);
}

/*
 *----------------------------------------------------------------------
 *
 * DupTclObject --
 *
 *	Copy the internal rep for a TclObject.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Increments the reference count on the TclObject.  Creates a
 *	new global reference to the object.
 *
 *----------------------------------------------------------------------
 */

static void
DupTclObject(
    Tcl_Obj *srcPtr,
    Tcl_Obj *destPtr)
{
    jobject object = (jobject)(srcPtr->internalRep.twoPtrValue.ptr2);
    JNIEnv *env = JavaGetEnv();
    JavaInfo* jcache = JavaGetCache();
    jobject exception;

    /*
     * Clear pending Java exception.
     */

    exception = (*env)->ExceptionOccurred(env);
    if (exception)
        (*env)->ExceptionClear(env);

    /*
     * Add a global reference to represent the new copy.
     */

    object = (*env)->NewGlobalRef(env, object);
    destPtr->typePtr = srcPtr->typePtr;
    destPtr->internalRep.twoPtrValue.ptr2 = (VOID*) object;
    (*env)->CallVoidMethod(env, object, jcache->preserve);
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
	Tcl_Panic("DupTclObject : exception in TclObject._preserve()");
    }

    /*
     * Rethrow pending Java exception.
     */

    if (exception) {
        (*env)->Throw(env, exception);
        (*env)->DeleteLocalRef(env, exception);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * FreeTclObject --
 *
 *	Free the internal representation for a TclObject.
 *	This method is invoked by Tcl when a Tcl_Obj that
 *	wraps a TclObject has its ref count decremented to zero.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Decrements the reference count of the TclObject and frees the
 *	global object reference.
 *
 *----------------------------------------------------------------------
 */

static void
FreeTclObject(
    Tcl_Obj *objPtr)		/* Object to free. */
{
    jobject object = (jobject)(objPtr->internalRep.twoPtrValue.ptr2);
    JNIEnv *env = JavaGetEnv();
    JavaInfo* jcache = JavaGetCache();
    jobject exception;

    /*
     * Clear pending Java exception.
     */

    exception = (*env)->ExceptionOccurred(env);
    if (exception)
        (*env)->ExceptionClear(env);

    /*
     * Delete the global ref.
     */

    (*env)->CallVoidMethod(env, object, jcache->release);
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
	Tcl_Panic("FreeTclObject : exception in TclObject._release()");
    }
    (*env)->DeleteGlobalRef(env, object);
    objPtr->internalRep.twoPtrValue.ptr2 = NULL;

    /*
     * Rethrow pending Java exception.
     */

    if (exception) {
        (*env)->Throw(env, exception);
        (*env)->DeleteLocalRef(env, exception);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * SetTclObject --
 *
 *	No conversion to a TclObject is possible.
 *
 * Results:
 *	Always returns TCL_ERROR.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SetTclObject(
    Tcl_Interp *interp,
    Tcl_Obj *objPtr)
{
    if (interp) {
	Tcl_ResetResult(interp);
	Tcl_SetStringObj(Tcl_GetObjResult(interp),
		"cannot convert to TclObject", -1);
    }
    return TCL_ERROR;
}

/*
 *----------------------------------------------------------------------
 *
 * UpdateTclObject --
 *
 *	Retrieve the string representation from the TclObject.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Updates the string representation of the Tcl_Obj.
 *
 *----------------------------------------------------------------------
 */

static void
UpdateTclObject(Tcl_Obj *objPtr)
{
    jstring string;
    jobject object = (jobject)(objPtr->internalRep.twoPtrValue.ptr2);
    JNIEnv *env = JavaGetEnv();
    JavaInfo* jcache = JavaGetCache();
    jobject exception;

    /*
     * Clear pending Java exception.
     */

    exception = (*env)->ExceptionOccurred(env);
    if (exception)
        (*env)->ExceptionClear(env);

    /*
     * Update Tcl_Obj.bytes to result of TclObject.toString() call.
     */

    string = (*env)->CallObjectMethod(env, object, jcache->toString);
    if ((*env)->ExceptionOccurred(env)) {
        (*env)->ExceptionDescribe(env);
	Tcl_Panic("UpdateTclObject : exception in TclObject.toString()");
    }
    objPtr->bytes = JavaGetString(env, string, &objPtr->length);
    (*env)->DeleteLocalRef(env, string);

    /*
     * Rethrow pending Java exception.
     */

    if (exception) {
        (*env)->Throw(env, exception);
        (*env)->DeleteLocalRef(env, exception);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * JavaGetTclObj --
 *
 *	Retrieve the Tcl_Obj that corresponds to the given Java
 *	TclObject. Creates a new Tcl_Obj of type TclObject with an internal
 *	representation that points at the Java object.
 *
 * Results:
 *	Returns the Tcl_Obj that corresponds to the TclObject.
 *
 * Side effects:
 *	Adds a reference to the TclObject.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj*
JavaGetTclObj(
    JNIEnv *env,		/* Java environment. */
    jobject object)		/* TclObject. */
{
    Tcl_Obj *objPtr;
    jlong objRef;
    JavaInfo* jcache = JavaGetCache();

    if ((*env)->ExceptionOccurred(env)) {
	(*env)->ExceptionDescribe(env);
	Tcl_Panic("JavaGetTclObj : unexpected pending exception");
    }

    objRef = (*env)->CallLongMethod(env, object, jcache->getCObjectPtr);
    if ((*env)->ExceptionOccurred(env)) {
	(*env)->ExceptionDescribe(env);
	Tcl_Panic("JavaGetTclObj : exception in TclObject.getCObjectPtr()");
    }

    if (objRef != 0) {
	/*
	 * This is either a TclList or a CObject, convert to Tcl_Obj*.
	 */

	objPtr = *(Tcl_Obj**)&objRef;

#ifdef TCL_MEM_DEBUG
	if (objPtr->refCount == 0x61616161) {
	    Tcl_Panic("JavaGetTclObj : disposed object");
	}
#endif
    } else {
	/*
	 * This object is of an unknown type so we create a new Tcl object to
	 * hold the object reference.
	 */

	object = (*env)->NewGlobalRef(env, object);
	objPtr = Tcl_NewObj();
	objPtr->bytes = NULL;
	objPtr->typePtr = &tclObjectType;
	objPtr->internalRep.twoPtrValue.ptr2 = (VOID*) object;

	/*
	 * Increment the reference count on the TclObject.
	 */

	(*env)->CallVoidMethod(env, object, jcache->preserve);
	if ((*env)->ExceptionOccurred(env)) {
	    (*env)->ExceptionDescribe(env);
	    Tcl_Panic("JavaGetTclObj : exception in TclObject._preserve()");
	}
    }
    return objPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_CObject_getString --
 *
 *	Retrieve the string representation for an object.
 *
 * Class:     tcl_lang_CObject
 * Method:    getString
 * Signature: (J)Ljava/lang/String;
 *
 * Results:
 *	Returns a new Java string.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

jstring JNICALL
Java_tcl_lang_CObject_getString(
    JNIEnv *env,		/* Java environment. */
    jclass class,		/* Handle to CObject class. */
    jlong obj)			/* Value of CObject.objPtr. */
{
    Tcl_Obj *objPtr = *(Tcl_Obj **) &obj;
    char *str;
    jstring result;
    int length;

    if (!objPtr) {
	ThrowNullPointerException(env, NULL);
	return NULL;
    }

#ifdef TCL_MEM_DEBUG
    if (objPtr->refCount == 0x61616161) {
	Tcl_Panic("Java_tcl_lang_CObject_getString : disposed object");
    }
#endif

    /*
     * Convert the string rep into a Unicode string.
     */

    str = Tcl_GetStringFromObj(objPtr, &length);
    if (length > 0) {
	result = (*env)->NewStringUTF(env, str);
    } else {
	result = (*env)->NewString(env, NULL, 0);
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_CObject_incrRefCount --
 *
 *	Increment the reference count of the given object.
 *
 * Class:     tcl_lang_CObject
 * Method:    incrRefCount
 * Signature: (J)V
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void JNICALL
Java_tcl_lang_CObject_incrRefCount(
    JNIEnv *env,		/* Java environment. */
    jclass class,		/* Handle to CObject class. */
    jlong obj)			/* Value of CObject.objPtr. */
{
    Tcl_Obj *objPtr = *(Tcl_Obj **) &obj;

    if (!objPtr) {
	ThrowNullPointerException(env, NULL);
	return;
    }
    Tcl_IncrRefCount(objPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_CObject_decrRefCount --
 *
 *	Decrement the reference count for the given object.
 *
 * Class:     tcl_lang_CObject
 * Method:    decrRefCount
 * Signature: (J)V
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void JNICALL Java_tcl_lang_CObject_decrRefCount(
    JNIEnv *env,		/* Java environment. */
    jclass class,		/* Handle to CObject class. */
    jlong obj)			/* Value of CObject.objPtr. */
{
    Tcl_Obj *objPtr = *(Tcl_Obj **) &obj;

    if (!objPtr) {
	ThrowNullPointerException(env, NULL);
	return;
    }
    Tcl_DecrRefCount(objPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_CObject_makeRef --
 *
 *	Convert the Tcl_Obj into a TclObject.
 *
 * Class:     tcl_lang_CObject
 * Method:    makeRef
 * Signature: (JLtcl/lang/TclObject;)V
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the internal representation of the object.
 *
 *----------------------------------------------------------------------
 */

void JNICALL
Java_tcl_lang_CObject_makeRef(
    JNIEnv *env,		/* Java environment. */
    jclass class,		/* Handle to CObject class. */
    jlong obj,			/* Value of CObject.objPtr. */
    jobject object)		/* Handle to the TclObject. */
{
    Tcl_Obj *objPtr = *(Tcl_Obj **) &obj;
    Tcl_ObjType *oldTypePtr;
    int non_tclobject_cmd = 0;

    if (!objPtr) {
	ThrowNullPointerException(env, NULL);
	return;
    }

#ifdef TCL_MEM_DEBUG
    if (objPtr->refCount == 0x61616161) {
	Tcl_Panic("Java_tcl_lang_CObject_makeRef : disposed object");
    }
#endif

    /*
     * Free the old internalRep before setting the new one.
     * Watch for the special case of a command internal rep
     * that does not have a ref to a TclObject. We avoid
     * freeing the internal rep and add a ref in that case.
     */

    if ((objPtr->typePtr == cmdTypePtr) &&
	    (objPtr->internalRep.twoPtrValue.ptr2 == NULL)) {
	non_tclobject_cmd = 1;
    }

    oldTypePtr = (Tcl_ObjType *) objPtr->typePtr;
    if ((oldTypePtr != NULL) &&
	    (oldTypePtr->freeIntRepProc != NULL) &&
	    !non_tclobject_cmd) {
	oldTypePtr->freeIntRepProc(objPtr);
    }

    object = (*env)->NewGlobalRef(env, object);
    if (!non_tclobject_cmd)
	objPtr->typePtr = &tclObjectType;
    objPtr->internalRep.twoPtrValue.ptr2 = (VOID*) object;

    /*
     * Note that we don't change the TclObject ref count or
     * the Tcl_Obj ref count here. We expect that FreeTclObject
     * will be invoked when the ref count of this Tcl_Obj
     * reaches zero, and a CObject begins life with a
     * ref count of 1, so we are covered.
     */
}

/*
 *----------------------------------------------------------------------
 *
 * JavaBreakRef --
 *
 *	Check to see if a Tcl_Obj contains an invalid
 *	ref to a TclObject that has a CObject or TclList
 *	internal rep. This method breaks such a ref by
 *	setting the internal rep for the Tcl_Obj to
 *	a string or list rep.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Modifies the internal representation of the Tcl_Obj.
 *
 *----------------------------------------------------------------------
 */

void
JavaBreakRef(
    JNIEnv *env,		/* Java environment. */
    Tcl_Obj *objPtr)		/* Object to check. */
{
    jobject object;
    int isTclList, isCObject, dummy;
    int inst;
    JavaInfo* jcache = JavaGetCache();

#ifdef TCL_MEM_DEBUG
    if (objPtr->refCount == 0x61616161) {
	Tcl_Panic("JavaBreakRef : disposed object");
    }
#endif

    if ((objPtr->typePtr == &tclObjectType)
            || ((objPtr->typePtr == cmdTypePtr) &&
                    (objPtr->internalRep.twoPtrValue.ptr2) != NULL)) {
	object = (jobject)(objPtr->internalRep.twoPtrValue.ptr2);
	inst = (*env)->CallIntMethod(env, object, jcache->getCObjectInst);
	if ((*env)->ExceptionOccurred(env)) {
	    (*env)->ExceptionDescribe(env);
	    Tcl_Panic("JavaBreakRef : exception in TclObject.getCObjectInst()");
	}
	/* Constants returned by getCObjectInst() to indicate type. */
	isCObject = (inst == 1);
	isTclList = (inst == 2);

	if (isTclList) {
	    /*fprintf(stderr, "breaking ref for TclList \"%s\"\n", Tcl_GetString(objPtr));*/
	    Tcl_ListObjLength((Tcl_Interp *) NULL, objPtr, &dummy);
	} else if (isCObject) {
	    /*fprintf(stderr, "breaking ref for String \"%s\"\n", Tcl_GetString(objPtr));*/
	    Tcl_GetCharLength(objPtr);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * Java_tcl_lang_CObject_newCObject --
 *
 *	Allocate a new Tcl_Obj with the given string rep.
 *
 * Class:     tcl_lang_CObject
 * Method:    newCObject
 * Signature: (Ljava/lang/String;)J
 *
 * Results:
 *	Returns the address of the new Tcl_Obj with refcount of 0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

jlong JNICALL
Java_tcl_lang_CObject_newCObject(
    JNIEnv *env,		/* Java environment. */
    jclass class,		/* Handle to CObject class. */
    jstring string)		/* Initial string rep. */
{
    Tcl_Obj *objPtr;
    jlong obj;

    objPtr = Tcl_NewObj();
    if (string) {
	objPtr->bytes = JavaGetString(env, string, &objPtr->length);
    }
    obj = 0;
    *(Tcl_Obj **)&obj = objPtr;
    return obj;
}

/*
 *----------------------------------------------------------------------
 *
 * JavaGetTclObject --
 *
 *	Retrieve the Java TclObject that shadows the given Tcl_Obj.
 *	Creates a new TclObject of type CObject or TclObject that refers
 *	to the given Tcl_Obj, unless the Tcl_Obj is a TclObject already.
 *
 * Results:
 *	Returns a TclObject associated with the Tcl_Obj.
 *
 * Side effects:
 *	May allocate a new local reference in the JVM. Note that
 *	if this routine is not called as a result of a native method
 *	invocation, the caller is responsible for deleting the local
 *	reference explicitly. Will increment the TclObject.refCount
 *	for a newely created CObject wrapper.
 *
 *----------------------------------------------------------------------
 */

jobject
JavaGetTclObject(
    JNIEnv *env,
    Tcl_Obj *objPtr,		/* Object to get jobject for. */
    int *isLocalPtr)		/* 1 if returned handle is a local ref. */
{
    jobject object;
    jlong lvalue;
    JavaInfo* jcache = JavaGetCache();

    if (!objPtr) {
	return NULL;
    }

    /*
     * Make sure the Tcl_Obj does not have
     * an invalid ref to a TclObject that has a
     * CObject or TclList internal rep.
     */

    JavaBreakRef(env, objPtr);

    if ((objPtr->typePtr == &tclObjectType)
	    || ((objPtr->typePtr == cmdTypePtr) &&
		    (objPtr->internalRep.twoPtrValue.ptr2) != NULL)) {
	/*
	 * This object is a reference to a TclObject, so we extract the
	 * jobject.
	 */

	object = (jobject)(objPtr->internalRep.twoPtrValue.ptr2);
	if (isLocalPtr) {
	    *isLocalPtr = 0;
	}
    } else {
	/*
	 *
	 * We should be able to use the following statement below:
	 *
	 *     lvalue = (jlong) objPtr;
	 *
	 * But gcc warns "cast to pointer from integer of different size"
	 * so use an ugly workaround to avoid the compiler warning.
	 * Note that the value is zeroed out before assigning to
	 * handle the case where jlong is 64 while a ptr is 32 bits.
	 */

	lvalue = 0;
	*(Tcl_Obj **)&lvalue = objPtr;

	/*
	 * This object is of an unknown type, so create a new TclObject.
	 * If the Tcl object is a list, then create a TclList.
	 *
	 *    TclObject tobj = TclList.newInstance(long objPtr);
	 *
	 * Otherwise we don't know the type so create a CObject.
	 *
	 *    TclObject tobj = CObject.newInstance(long objPtr);
	 *
	 */

	if (objPtr->typePtr == listTypePtr) {
	    object = (*env)->CallStaticObjectMethod(env, jcache->TclList,
	        jcache->newTclListInstance, lvalue);
	} else {
	    object = (*env)->CallStaticObjectMethod(env, jcache->CObject,
	        jcache->newCObjectInstance, lvalue);
	}

	if ((*env)->ExceptionOccurred(env)) {
	    (*env)->ExceptionDescribe(env);
	    Tcl_Panic("JavaGetTclObject : exception in newInstance()");
	}

	/*
	 * Increment the ref count of the new TclObject so that
	 * it starts life with a ref count of 1. This operation
	 * does not change the ref count of the Tcl_Obj.
	 * We expect to be able to drop a ref to a CObject
	 * with a ref count of 1 without leaking memory in C.
	 */

	(*env)->CallVoidMethod(env, object, jcache->preserve);
	if ((*env)->ExceptionOccurred(env)) {
	    (*env)->ExceptionDescribe(env);
	    Tcl_Panic("JavaGetTclObject : exception in TclObject._preserve()");
	}

	if (isLocalPtr) {
	    *isLocalPtr = 1;
	}
    }
    return object;
}

/*
 *----------------------------------------------------------------------
 *
 * FreeJavaCmdInternalRep --
 *
 *	Free the internal rep for a java object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Decrements the refcount on a java object, and frees it if
 *	the last reference is gone.
 *
 *----------------------------------------------------------------------
 */

static void
FreeJavaCmdInternalRep(
    Tcl_Obj *objPtr)
{
    jobject jobj = (jobject) objPtr->internalRep.twoPtrValue.ptr2;

    if (jobj) {
	FreeTclObject(objPtr);
    }
    (oldCmdType.freeIntRepProc)(objPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * DupJavaCmdInternalRep --
 *
 *	Initialize the internal representation of a java Tcl_Obj to a
 *	copy of the internal representation of an existing java object.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	"dupPtr"s internal rep is set to the java object corresponding to
 *	"srcPtr"s internal rep and the refcount on the java object
 *	in incremented.
 *
 *----------------------------------------------------------------------
 */

static void
DupJavaCmdInternalRep(
    Tcl_Obj *srcPtr,
    Tcl_Obj *dupPtr)
{
    jobject jobj = (jobject) srcPtr->internalRep.twoPtrValue.ptr2;
    (oldCmdType.dupIntRepProc)(srcPtr, dupPtr);
    dupPtr->internalRep.twoPtrValue.ptr2 = jobj;
    if (jobj) {
	DupTclObject(srcPtr, dupPtr);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * SetJavaCmdFromAny --
 *
 *	Attempt to generate a command object from an arbitrary type.
 *	This routine is a wrapper around the standard cmdName setFromAny
 *	procedure.  If the object holds a reference to a TclObject,
 *	save and restore the reference after the object is converted.
 *
 * Results:
 *	The return value is a standard object Tcl result. If an error occurs
 *	during conversion, an error message is left in the interpreter's
 *	result unless "interp" is NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SetJavaCmdFromAny(
    Tcl_Interp *interp,
    Tcl_Obj *objPtr)
{
    int result;

    /*
     * Invoke the normal command type routine, but make sure
     * it doesn't free the java object by setting the typePtr
     * to NULL. Note that we have to restore the ptr2 value after
     * the conversion, since it gets set to NULL by setFromAnyProc.
     */

    if ((objPtr->typePtr == &tclObjectType) ||
	    ((objPtr->typePtr == cmdTypePtr) &&
		    (objPtr->internalRep.twoPtrValue.ptr2 != NULL))) {
	VOID *ptr2;
	if (objPtr->bytes == NULL) {
	    UpdateTclObject(objPtr);
	}
	objPtr->typePtr = NULL;
	ptr2 = objPtr->internalRep.twoPtrValue.ptr2;
	result = (oldCmdType.setFromAnyProc)(interp, objPtr);
	objPtr->internalRep.twoPtrValue.ptr2 = ptr2;
    } else {
	result = (oldCmdType.setFromAnyProc)(interp, objPtr);
    }
    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * JavaIsRef --
 *
 *	Return true if this Tcl_Obj* contains a ref to a TclObject.
 *
 * Results:
 *	1 or 0.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

int
JavaIsRef(
    Tcl_Obj *objPtr)		/* Object to check .*/
{
    return ((objPtr->typePtr == &tclObjectType) ||
            ((objPtr->typePtr == cmdTypePtr) &&
                    (objPtr->internalRep.twoPtrValue.ptr2) != NULL));
}

/*
 *----------------------------------------------------------------------
 *
 * JavaObjType --
 *
 *	Return a string that describes the internal rep type for objPtr.
 *
 * Results:
 *	A Tcl_Obj*.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

Tcl_Obj*
JavaObjType(
    Tcl_Obj *objPtr)		/* Object to check .*/
{
    char *type;
    Tcl_ObjType *stringTypePtr = (Tcl_ObjType *) Tcl_GetObjType("string");

    if (objPtr->typePtr == &tclObjectType)
        type = "tclobject";
    else if ((objPtr->typePtr == cmdTypePtr) &&
            ((objPtr->internalRep.twoPtrValue.ptr2) != NULL))
        type = "cmdtclobject";
    else if (objPtr->typePtr == cmdTypePtr)
        type = "cmd";
    else if (objPtr->typePtr == listTypePtr)
        type = "list";
    else if (objPtr->typePtr == stringTypePtr)
        type = "string";
    else
        type = "unknown";

    return Tcl_NewStringObj(type, -1);
}
