jsj_javaobject.c

来自「一个基于alice开发的机器人」· C语言 代码 · 共 1,090 行 · 第 1/3 页

C
1,090
字号
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 Communicator client code, released
 * March 31, 1998.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * This Original Code has been modified by IBM Corporation. Modifications made
 * by IBM described herein are Copyright (c) International Business Machines
 * Corporation, 2000.
 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
 *
 * Date             Modified by     Description of modification
 * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
 *
 * ***** END LICENSE BLOCK ***** */

/*
 * This file is part of the Java-vendor-neutral implementation of LiveConnect
 *
 * It contains the native code implementation of JS's JavaObject class.
 *
 * An instance of JavaObject is the JavaScript reflection of a Java object.
 *
 */

#include <stdlib.h>
#include <string.h>

#include "jsobj.h"
#include "jsj_private.h"      /* LiveConnect internals */
#include "jsj_hash.h"         /* Hash table with Java object as key */

#ifdef JSJ_THREADSAFE
#include "prmon.h"
#endif

/*
 * This is a hash table that maps from Java objects to JS objects.
 * It is used to ensure that the same JS object is obtained when a Java
 * object is reflected more than once, so that JS object equality tests
 * work in the expected manner, i.e. the "==" and "===" operators.
 *
 * The table entry keys are Java objects (of type jobject) and the entry values
 * are JSObject pointers.  Because the jobject type is an opaque handle and
 * not necessarily a pointer, the hashing and key comparison functions must
 * invoke the appropriate JVM functions.
 *
 * When the corresponding JS object instance is finalized, the entry is
 * removed from the table, and a Java GC root for the Java object is removed.
 */
static JSJHashTable *java_obj_reflections = NULL;

#ifdef JSJ_THREADSAFE
static PRMonitor *java_obj_reflections_monitor = NULL;
static int java_obj_reflections_mutation_count = 0;
#endif

static JSBool installed_GC_callback = JS_FALSE;
static JSGCCallback old_GC_callback = NULL;
static JavaObjectWrapper* deferred_wrappers = NULL;

static JSBool JS_DLL_CALLBACK jsj_GC_callback(JSContext *cx, JSGCStatus status)
{
    if (status == JSGC_END && deferred_wrappers) {
        JNIEnv *jEnv;
        JSJavaThreadState *jsj_env = jsj_EnterJava(cx, &jEnv);
        if (jEnv) {
            JavaObjectWrapper* java_wrapper = deferred_wrappers;
            while (java_wrapper) {
                deferred_wrappers = java_wrapper->u.next;
                (*jEnv)->DeleteGlobalRef(jEnv, java_wrapper->java_obj);
                jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
                JS_free(cx, java_wrapper);
                java_wrapper = deferred_wrappers;
            }
            jsj_ExitJava(jsj_env);
        }
    }
    /* always chain to old GC callback if non-null. */
    return old_GC_callback ? old_GC_callback(cx, status) : JS_TRUE;
}

JSBool
jsj_InitJavaObjReflectionsTable(void)
{
    JS_ASSERT(!java_obj_reflections);

    java_obj_reflections =
        JSJ_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator,
                         NULL, NULL, NULL);
    if (!java_obj_reflections)
        return JS_FALSE;

#ifdef JSJ_THREADSAFE
    java_obj_reflections_monitor = (struct PRMonitor *) PR_NewMonitor();
    if (!java_obj_reflections_monitor) {
        JSJ_HashTableDestroy(java_obj_reflections);
        return JS_FALSE;
    }
#endif

    return JS_TRUE;
}

JSObject *
jsj_WrapJavaObject(JSContext *cx,
                   JNIEnv *jEnv,
                   jobject java_obj,
                   jclass java_class)
{
    JSJHashNumber hash_code;
    JSClass *js_class;
    JSObject *js_wrapper_obj;
    JavaObjectWrapper *java_wrapper;
    JavaClassDescriptor *class_descriptor;
    JSJHashEntry *he, **hep;

#ifdef JSJ_THREADSAFE
    int mutation_count;
#endif

    js_wrapper_obj = NULL;

    hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);

#ifdef JSJ_THREADSAFE
    PR_EnterMonitor(java_obj_reflections_monitor);
#endif

    if (!installed_GC_callback) {
        /*
         * Hook into GC callback mechanism, so we can defer deleting global
         * references until it's safe.
         */
        old_GC_callback =  JS_SetGCCallback(cx, jsj_GC_callback);
        installed_GC_callback = JS_TRUE;
    }

    hep = JSJ_HashTableRawLookup(java_obj_reflections,
                                 hash_code, java_obj, (void*)jEnv);
    he = *hep;

#ifdef JSJ_THREADSAFE
    /* Track mutations to hash table */
    mutation_count = java_obj_reflections_mutation_count;

    /* We must temporarily release this monitor so as to avoid
       deadlocks with the JS GC.  See Bugsplat #354852 */
    PR_ExitMonitor(java_obj_reflections_monitor);
#endif

    if (he) {
        js_wrapper_obj = (JSObject *)he->value;
        if (js_wrapper_obj)
            return js_wrapper_obj;
    }

    /* No existing reflection found.  Construct a new one */
    class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
    if (!class_descriptor)
        return NULL;
    if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
        js_class = &JavaArray_class;
    } else {
        JS_ASSERT(IS_OBJECT_TYPE(class_descriptor->type));
        js_class = &JavaObject_class;
    }

    /* Create new JS object to reflect Java object */
    js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
    if (!js_wrapper_obj)
        return NULL;

    /* Create private, native portion of JavaObject */
    java_wrapper =
        (JavaObjectWrapper *)JS_malloc(cx, sizeof(JavaObjectWrapper));
    if (!java_wrapper) {
        jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
        return NULL;
    }
    JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
    java_wrapper->class_descriptor = class_descriptor;
    java_wrapper->java_obj = NULL;

#ifdef JSJ_THREADSAFE
    PR_EnterMonitor(java_obj_reflections_monitor);

    /* We may need to do the hash table lookup again, since some other
       thread may have updated it while the lock wasn't being held. */
    if (mutation_count != java_obj_reflections_mutation_count) {
        hep = JSJ_HashTableRawLookup(java_obj_reflections,
                                     hash_code, java_obj, (void*)jEnv);
        he = *hep;
        if (he) {
            js_wrapper_obj = (JSObject *)he->value;
            if (js_wrapper_obj) {
                PR_ExitMonitor(java_obj_reflections_monitor);
                return js_wrapper_obj;
            }
        }
    }

    java_obj_reflections_mutation_count++;

#endif

    java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
    java_wrapper->java_obj = java_obj;
    if (!java_obj)
        goto out_of_memory;

    /* cache the hash code for all time. */
    java_wrapper->u.hash_code = hash_code;

    /* Add the JavaObject to the hash table */
    he = JSJ_HashTableRawAdd(java_obj_reflections, hep, hash_code,
                             java_obj, js_wrapper_obj, (void*)jEnv);
#ifdef JSJ_THREADSAFE
    PR_ExitMonitor(java_obj_reflections_monitor);
#endif

    if (!he) {
        (*jEnv)->DeleteGlobalRef(jEnv, java_obj);
        goto out_of_memory;
    }

    return js_wrapper_obj;

out_of_memory:
    /* No need to free js_wrapper_obj, as it will be finalized by GC. */
    JS_ReportOutOfMemory(cx);
    return NULL;
}

static void
remove_java_obj_reflection_from_hashtable(jobject java_obj, JSJHashNumber hash_code)
{
    JSJHashEntry *he, **hep;

#ifdef JSJ_THREADSAFE
    PR_EnterMonitor(java_obj_reflections_monitor);
#endif

    hep = JSJ_HashTableRawLookup(java_obj_reflections, hash_code,
                                 java_obj, NULL);
    he = *hep;

    JS_ASSERT(he);
    if (he)
        JSJ_HashTableRawRemove(java_obj_reflections, hep, he, NULL);

#ifdef JSJ_THREADSAFE
    java_obj_reflections_mutation_count++;

    PR_ExitMonitor(java_obj_reflections_monitor);
#endif
}

JS_EXPORT_API(void)
JavaObject_finalize(JSContext *cx, JSObject *obj)
{
    JavaObjectWrapper *java_wrapper;
    jobject java_obj;
    JNIEnv *jEnv;
    JSJavaThreadState *jsj_env;

    java_wrapper = JS_GetPrivate(cx, obj);
    if (!java_wrapper)
        return;
    java_obj = java_wrapper->java_obj;

    jsj_env = jsj_EnterJava(cx, &jEnv);
    if (!jEnv)
        return;

    if (java_obj) {
        remove_java_obj_reflection_from_hashtable(java_obj, java_wrapper->u.hash_code);

        /* defer releasing global refs until it is safe to do so. */
        java_wrapper->u.next = deferred_wrappers;
        deferred_wrappers = java_wrapper;
    } else {
        jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
        JS_free(cx, java_wrapper);
    }

    jsj_ExitJava(jsj_env);
}

/* Trivial helper for jsj_DiscardJavaObjReflections(), below */
static JSIntn
enumerate_remove_java_obj(JSJHashEntry *he, JSIntn i, void *arg)
{
    JSJavaThreadState *jsj_env = (JSJavaThreadState *)arg;
    JNIEnv *jEnv = jsj_env->jEnv;
    jobject java_obj;
    JavaObjectWrapper *java_wrapper;
    JSObject *java_wrapper_obj;

    java_wrapper_obj = (JSObject *)he->value;

    /* Warning: NULL argument may cause assertion in JS engine, but it's actually OK */
    java_wrapper = JS_GetPrivate(jsj_env->cx, java_wrapper_obj);
    java_obj = java_wrapper->java_obj;
    (*jEnv)->DeleteGlobalRef(jEnv, java_obj);
    java_wrapper->java_obj = NULL;
    return HT_ENUMERATE_REMOVE;
}

/* This shutdown routine discards all JNI references to Java objects
   that have been reflected into JS, even if there are still references
   to them from JS. */
void
jsj_DiscardJavaObjReflections(JNIEnv *jEnv)
{
    JSJavaThreadState *jsj_env;
    char *err_msg;

    /* Get the per-thread state corresponding to the current Java thread */
    jsj_env = jsj_MapJavaThreadToJSJavaThreadState(jEnv, &err_msg);
    JS_ASSERT(jsj_env);
    if (!jsj_env)
        return;

    if (java_obj_reflections) {
        JSJ_HashTableEnumerateEntries(java_obj_reflections,
                                      enumerate_remove_java_obj,
                                      (void*)jsj_env);
        JSJ_HashTableDestroy(java_obj_reflections);
        java_obj_reflections = NULL;
    }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?