Back to index

lightning-sunbird  0.9+nobinonly
jsj_JavaPackage.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /* This file is part of the Java-vendor-neutral implementation of LiveConnect
00041  *
00042  * It contains the native code implementation of the JavaPackage class.
00043  *
00044  * A JavaPackage is JavaScript's representation of a Java package.  The
00045  * JavaPackage object contains only a string, which is the path to the package,
00046  * e.g. "java/lang".  The JS properties of a JavaPackage are either nested packages
00047  * or a JavaClass object, which represents the path to a Java class.
00048  *
00049  * Note that there is no equivalent to a JavaPackage object in Java.  Example:
00050  * Although there are instances of java.lang.String and there are static methods
00051  * of java.lang.String that can be invoked, there's no such thing as a java.lang
00052  * object in Java that exists at run time.
00053  *
00054  */
00055 
00056 #include <stdlib.h>
00057 #include <string.h>
00058 
00059 #include "jsj_private.h"        /* LiveConnect internals */
00060 #include "jsjava.h"
00061 
00062 
00063 JSClass JavaPackage_class;      /* Forward declaration */
00064 
00065 /*
00066  * The native part of a JavaPackage object.  It gets stored in the object's
00067  * private slot.
00068  */
00069 typedef struct {
00070     const char * path;          /* e.g. "java/lang" or NULL if top level package */
00071     int flags;                  /* e.g. PKG_SYSTEM, PKG_CLASS */
00072 } JavaPackage_Private;
00073 
00074 static JSObject *
00075 define_JavaPackage(JSContext *cx, JSObject *parent_obj,
00076                    const char *obj_name, const char *path, int flags, int access)
00077 {
00078     JSObject *package_obj;
00079     JavaPackage_Private *package;
00080 
00081     package_obj = JS_DefineObject(cx, parent_obj, obj_name, &JavaPackage_class, 0, JSPROP_PERMANENT | access);
00082     
00083     if (!package_obj)
00084         return NULL;
00085     
00086     /* Attach private, native data to the JS object */
00087     package = (JavaPackage_Private *)JS_malloc(cx, sizeof(JavaPackage_Private));
00088     JS_SetPrivate(cx, package_obj, (void *)package);
00089     if (path)
00090         package->path = JS_strdup(cx, path);
00091     else
00092         package->path = "";
00093 
00094     package->flags = flags;
00095 
00096     /* Check for OOM */
00097     if (!package->path) {
00098         JS_DeleteProperty(cx, parent_obj, obj_name);
00099         JS_free(cx, package);
00100         return NULL;
00101     }
00102 
00103     return package_obj;
00104 }
00105 
00106 /* JavaPackage uses standard JS getProperty */
00107 
00108 /*
00109  * Don't allow user-defined properties to be set on Java package objects, e.g.
00110  * it is illegal to write "java.lang.myProperty = 4".  We probably could relax
00111  * this restriction, but it's potentially confusing and not clearly useful.
00112  */
00113 JS_STATIC_DLL_CALLBACK(JSBool)
00114 JavaPackage_setProperty(JSContext *cx, JSObject *obj, jsval slot, jsval *vp)
00115 {
00116     JavaPackage_Private *package = JS_GetPrivate(cx, obj);
00117     if (!package) {
00118         JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00119                                                 JSJMSG_BAD_ADD_TO_PACKAGE);
00120         return JS_FALSE;
00121     }
00122     JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00123                                                 JSJMSG_DONT_ADD_TO_PACKAGE);
00124     return JS_FALSE;
00125 }
00126 
00127 static JSBool quiet_resolve_failure;
00128 
00129 /*
00130  * Resolve a component name to be either the name of a class or a package.
00131  */
00132 JS_STATIC_DLL_CALLBACK(JSBool)
00133 JavaPackage_resolve(JSContext *cx, JSObject *obj, jsval id)
00134 {
00135     JavaPackage_Private *package;
00136     JSBool ok = JS_TRUE;
00137     jclass jclazz;
00138     char *subPath, *newPath;
00139     const char *path;
00140     JNIEnv *jEnv;
00141     JSJavaThreadState *jsj_env;
00142 
00143     /* Painful hack for pre_define_java_packages() */
00144     if (quiet_resolve_failure)
00145         return JS_FALSE;
00146                 
00147     package = (JavaPackage_Private *)JS_GetPrivate(cx, obj);
00148     if (!package)
00149         return JS_TRUE;
00150 
00151     if (!JSVAL_IS_STRING(id))
00152         return JS_TRUE;
00153     subPath = JS_GetStringBytes(JSVAL_TO_STRING(id));
00154 
00155     /*
00156      * There will be an attempt to invoke the toString() method when producing
00157      * the string representation of a JavaPackage.  When this occurs, avoid
00158      * creating a bogus toString package.  (This means that no one can ever
00159      * create a package with the simple name "toString", but we'll live with
00160      * that limitation.)
00161      */
00162     if (!strcmp(subPath, "toString"))
00163         return JS_FALSE;
00164 
00165     path = package->path;
00166     newPath = JS_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath);
00167     if (!newPath) {
00168         JS_ReportOutOfMemory(cx);
00169         return JS_FALSE;
00170     }
00171 
00172     jsj_env = jsj_EnterJava(cx, &jEnv);
00173     if (!jEnv) {
00174         ok = JS_FALSE;
00175         goto out;
00176     }
00177 
00178     /*
00179         Unfortunately, Java provides no way to find out whether a particular
00180         name is a package or not.  The only way to tell is to try to load the
00181         name as a class file and, if that fails, assume it's a package.  This
00182         makes things work as expected for the most part, but it has three
00183         noticeable problems that keep coming up:
00184 
00185         - You can refer to a package like java.lang.i.buried.paul without
00186           generating a complaint.  Of course, you'll never be able to refer to
00187           any classes through it.
00188 
00189         - An annoying consequence of the above is that misspelling a class name
00190           results in a cryptic error about packages.
00191 
00192         - In a browser context, i.e. where applets are involved, figuring out
00193           whether something is a class may require looking for it over the net
00194           using the current classloader.  This means that the first time you
00195           refer to java.lang.System in a js context, there will be an attempt
00196           to search for [[DOCBASE]]/java.class on the server.
00197     
00198         A solution is to explicitly tell jsjava the names of all the (local)
00199         packages on the CLASSPATH.  (Not implemented yet.)
00200 
00201     */
00202     jclazz = (*jEnv)->FindClass(jEnv, newPath);
00203     if (jclazz) {
00204         JSObject *newClass;
00205 
00206         newClass = jsj_define_JavaClass(cx, jEnv, obj, subPath, jclazz);
00207         (*jEnv)->DeleteLocalRef(jEnv, jclazz);
00208         if (!newClass) {
00209             ok = JS_FALSE;
00210             goto out;
00211         }
00212     } else {
00213 
00214         /* We assume that any failed attempt to load a class is because it
00215            doesn't exist.  If we wanted to do a better job, we would check
00216            the exception type and make sure that it's NoClassDefFoundError */
00217         (*jEnv)->ExceptionClear(jEnv);
00218 
00219         /*
00220          * If there's no class of the given name, then we must be referring to
00221          * a package.  However, don't allow bogus sub-packages of pre-defined
00222          * system packages to be created.
00223          */
00224         if (JS_InstanceOf(cx, obj, &JavaPackage_class, NULL)) {
00225             JavaPackage_Private *current_package;
00226 
00227             current_package = JS_GetPrivate(cx, obj);
00228             if (current_package->flags & PKG_SYSTEM) {
00229                 char *msg, *cp;
00230                 msg = JS_strdup(cx, newPath);
00231 
00232                 /* Check for OOM */
00233                 if (msg) {
00234                     /* Convert package of form "java/lang" to "java.lang" */
00235                     for (cp = msg; *cp != '\0'; cp++)
00236                         if (*cp == '/')
00237                             *cp = '.';
00238                     JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00239                                                 JSJMSG_MISSING_PACKAGE, msg);
00240                     free((char*)msg);
00241                 }
00242                                
00243                 ok = JS_FALSE;
00244                 goto out;
00245             }
00246         }
00247 
00248         if (!define_JavaPackage(cx, obj, subPath, newPath, 0, JSPROP_READONLY)) {
00249             ok = JS_FALSE;
00250             goto out;
00251         }
00252         
00253 #ifdef DEBUG
00254         /* printf("JavaPackage \'%s\' created\n", newPath); */
00255 #endif
00256 
00257     }
00258     
00259 out:
00260     free(newPath);
00261     jsj_ExitJava(jsj_env);
00262     return ok;
00263 }
00264 
00265 JS_STATIC_DLL_CALLBACK(JSBool)
00266 JavaPackage_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
00267 {
00268     JSString *str;
00269     char *name, *cp;
00270 
00271     JavaPackage_Private *package = JS_GetPrivate(cx, obj);
00272     if (!package) {
00273         fprintf(stderr, "JavaPackage_resolve: no private data!\n");
00274         return JS_FALSE;
00275     }
00276 
00277     switch (type) {
00278 
00279     /* Pretty-printing of JavaPackage */
00280     case JSTYPE_VOID:   /* Default value */
00281     case JSTYPE_NUMBER:
00282     case JSTYPE_STRING:
00283         /* Convert '/' to '.' so that it looks like Java language syntax. */
00284         if (!package->path)
00285             break;
00286         name = JS_smprintf("[JavaPackage %s]", package->path);
00287         if (!name) {
00288             JS_ReportOutOfMemory(cx);
00289             return JS_FALSE;
00290         }
00291         for (cp = name; *cp != '\0'; cp++)
00292             if (*cp == '/')
00293                 *cp = '.';
00294         str = JS_NewString(cx, name, strlen(name));
00295         if (!str) {
00296             free(name);
00297             /* It's not necessary to call JS_ReportOutOfMemory(), as
00298                JS_NewString() will do so on failure. */
00299             return JS_FALSE;
00300         }
00301 
00302         *vp = STRING_TO_JSVAL(str);
00303         break;
00304 
00305     case JSTYPE_OBJECT:
00306         *vp = OBJECT_TO_JSVAL(obj);
00307         break;
00308 
00309     default:
00310         break;
00311     }
00312     return JS_TRUE;
00313 }
00314 
00315 /*
00316  * Free the private native data associated with the JavaPackage object.
00317  */
00318 JS_STATIC_DLL_CALLBACK(void)
00319 JavaPackage_finalize(JSContext *cx, JSObject *obj)
00320 {
00321     JavaPackage_Private *package = JS_GetPrivate(cx, obj);
00322     if (!package)
00323         return;
00324 
00325     if (package->path)
00326         JS_free(cx, (char *)package->path);
00327     JS_free(cx, package);
00328 }
00329 
00330 /*
00331  * The definition of the JavaPackage class
00332  */
00333 JSClass JavaPackage_class = {
00334     "JavaPackage", JSCLASS_HAS_PRIVATE,
00335     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JavaPackage_setProperty,
00336     JS_EnumerateStub, JavaPackage_resolve,
00337     JavaPackage_convert, JavaPackage_finalize,
00338 
00339     /* Optionally non-null members start here. */
00340     NULL,                       /* getObjectOps */
00341     NULL,                       /* checkAccess */
00342     NULL,                       /* call */
00343     NULL,                       /* construct */
00344     NULL,                       /* xdrObject */
00345     NULL,                       /* hasInstance */
00346     NULL,                       /* mark */
00347     0,                          /* spare */
00348 };
00349 
00350 JavaPackageDef
00351 standard_java_packages[] = {
00352     {"java",                NULL,   PKG_USER,   0},
00353     {"java.applet",         NULL,   PKG_USER,   JSPROP_READONLY},
00354     {"java.awt",            NULL,   PKG_USER,   JSPROP_READONLY},
00355     {"java.awt.datatransfer",                       
00356                             NULL,   PKG_SYSTEM, JSPROP_READONLY},
00357     {"java.awt.event",      NULL,   PKG_SYSTEM, JSPROP_READONLY},
00358     {"java.awt.image",      NULL,   PKG_SYSTEM, JSPROP_READONLY},
00359     {"java.awt.peer",       NULL,   PKG_SYSTEM, JSPROP_READONLY},
00360     {"java.beans",          NULL,   PKG_USER,   JSPROP_READONLY},
00361     {"java.io",             NULL,   PKG_SYSTEM, JSPROP_READONLY},
00362     {"java.lang",           NULL,   PKG_USER,   JSPROP_READONLY},
00363     {"java.lang.reflect",   NULL,   PKG_SYSTEM, JSPROP_READONLY},
00364     {"java.math",           NULL,   PKG_SYSTEM, JSPROP_READONLY},
00365     {"java.net",            NULL,   PKG_USER,   JSPROP_READONLY},
00366     {"java.rmi",            NULL,   PKG_USER,   JSPROP_READONLY},
00367     {"java.rmi.dgc",        NULL,   PKG_USER,   JSPROP_READONLY},
00368     {"java.rmi.user",       NULL,   PKG_USER,   JSPROP_READONLY},
00369     {"java.rmi.registry",   NULL,   PKG_USER,   JSPROP_READONLY},
00370     {"java.rmi.server",     NULL,   PKG_USER,   JSPROP_READONLY},
00371     {"java.security",       NULL,   PKG_USER,   JSPROP_READONLY},
00372     {"java.security.acl",   NULL,   PKG_SYSTEM, JSPROP_READONLY},
00373     {"java.security.interfaces",
00374                             NULL,   PKG_SYSTEM, JSPROP_READONLY},
00375     {"java.sql",            NULL,   PKG_USER,   JSPROP_READONLY},
00376     {"java.text",           NULL,   PKG_USER,   JSPROP_READONLY},
00377     {"java.text.resources", NULL,   PKG_SYSTEM, JSPROP_READONLY},
00378     {"java.util",           NULL,   PKG_USER,   JSPROP_READONLY},
00379     {"java.util.zip",       NULL,   PKG_SYSTEM, JSPROP_READONLY},
00380 
00381     {"netscape",            NULL,   PKG_USER,   0},
00382     {"netscape.applet",     NULL,   PKG_SYSTEM, JSPROP_READONLY},
00383     {"netscape.application",NULL,   PKG_SYSTEM, JSPROP_READONLY},
00384     {"netscape.debug",      NULL,   PKG_SYSTEM, JSPROP_READONLY},
00385     {"netscape.javascript", NULL,   PKG_SYSTEM, JSPROP_READONLY},
00386     {"netscape.ldap",       NULL,   PKG_SYSTEM, JSPROP_READONLY},
00387     {"netscape.misc",       NULL,   PKG_SYSTEM, JSPROP_READONLY},
00388     {"netscape.net",        NULL,   PKG_SYSTEM, JSPROP_READONLY},
00389     {"netscape.plugin",     NULL,   PKG_SYSTEM, JSPROP_READONLY},
00390     {"netscape.util",       NULL,   PKG_SYSTEM, JSPROP_READONLY},
00391     {"netscape.secfile",    NULL,   PKG_SYSTEM, JSPROP_READONLY},
00392     {"netscape.security",   NULL,   PKG_SYSTEM, JSPROP_READONLY},
00393     {"netscape.WAI",        NULL,   PKG_SYSTEM, JSPROP_READONLY},
00394 
00395     {"sun",                 NULL,   PKG_USER,   0},
00396     {"Packages",            "",     PKG_USER,   JSPROP_READONLY},
00397 
00398     {NULL,                  NULL,   0,          0}
00399 };
00400 
00401 /*
00402  * On systems which provide strtok_r we'll use that function to avoid
00403  * problems with non-thread-safety.
00404  */
00405 #if HAVE_STRTOK_R
00406 # define STRTOK_1ST(str, seps, res) strtok_r (str, seps, &res)
00407 # define STRTOK_OTHER(seps, res) strtok_r (res, seps, &res)
00408 #else
00409 # define STRTOK_1ST(str, seps, res) strtok (str, seps)
00410 # define STRTOK_OTHER(seps, res) strtok (NULL, seps)
00411 #endif
00412 
00413 /*
00414  * Pre-define a hierarchy of JavaPackage objects.
00415  * Pre-defining a Java package at initialization time is not necessary, but
00416  * it will make package lookup faster and, more importantly, will avoid
00417  * unnecessary network accesses if classes are being loaded over the network.
00418  */
00419 static JSBool
00420 pre_define_java_packages(JSContext *cx, JSObject *global_obj,
00421                          JavaPackageDef *predefined_packages)
00422 {
00423     JSBool package_exists;
00424     JSObject *parent_obj;
00425     JavaPackageDef *package_def;
00426     char *simple_name, *cp, *package_name, *path;
00427     int flags;
00428 
00429     if (!predefined_packages)
00430         return JS_TRUE;
00431 
00432     /* Iterate over all pre-defined Java packages */
00433     for (package_def = predefined_packages; package_def->name; package_def++) {
00434 #if HAVE_STRTOK_R
00435        char *nextstr;
00436 #endif
00437         package_name = path = NULL;
00438 
00439         parent_obj = global_obj;
00440         package_name = strdup(package_def->name);
00441         if (!package_name)
00442             goto out_of_memory;
00443 
00444         /* Walk the chain of JavaPackage objects to get to the parent of the
00445            rightmost sub-package in the fully-qualified package name. */
00446         for (simple_name = STRTOK_1ST(package_name, ".", nextstr); simple_name /*1*/; simple_name = STRTOK_OTHER(".", nextstr)) {
00447             jsval v;
00448 
00449             if (!simple_name) {
00450                 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL, 
00451                                         JSJMSG_DOUBLE_SHIPPING, package_name);
00452                 goto error;
00453             }
00454 
00455             /* Check to see if the sub-package already exists */
00456             quiet_resolve_failure = JS_TRUE;
00457             package_exists = JS_LookupProperty(cx, parent_obj, simple_name, &v) && JSVAL_IS_OBJECT(v);
00458             quiet_resolve_failure = JS_FALSE;
00459 
00460             if (package_exists) {
00461                 parent_obj = JSVAL_TO_OBJECT(v);
00462                 continue;
00463             }
00464 
00465             /* New package objects should only be created at the terminal
00466                sub-package in a fully-qualified package-name */
00467             if (STRTOK_OTHER(".", nextstr)) {
00468                 JS_ReportErrorNumber(cx, jsj_GetErrorMessage, NULL,
00469                                 JSJMSG_BAD_PACKAGE_PREDEF,
00470                                package_def->name);
00471                 goto error;
00472             }
00473             
00474             if (package_def->path) {
00475                 path = strdup(package_def->path);
00476                 if (!path)
00477                     goto out_of_memory;
00478             } else {
00479                 /*
00480                  * The default path is specified, so create it from the
00481                  * fully-qualified package name.
00482                  */
00483                 path = strdup(package_def->name);
00484                 if (!path)
00485                     goto out_of_memory;
00486                 /* Transform package name, e.g. "java.lang" ==> "java/lang" */
00487                 for (cp = path; *cp != '\0'; cp++) {
00488                     if (*cp == '.')
00489                          *cp = '/';
00490                 }
00491             }
00492             flags = package_def->flags;
00493             parent_obj = define_JavaPackage(cx, parent_obj, simple_name, path, flags,
00494                                             package_def->access);
00495             if (!parent_obj)
00496                 goto error;
00497  
00498             free(path);
00499             break;
00500         }
00501         free(package_name);
00502     }
00503     return JS_TRUE;
00504 
00505 out_of_memory:
00506     JS_ReportOutOfMemory(cx);
00507 
00508 error:
00509     JS_FREE_IF(cx, package_name);
00510     JS_FREE_IF(cx, path);
00511     return JS_FALSE;
00512 }
00513 
00514 JS_STATIC_DLL_CALLBACK(JSBool)
00515 JavaPackage_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00516                  jsval *rval)
00517 {
00518     if (!JS_InstanceOf(cx, obj, &JavaPackage_class, argv))
00519         return JS_FALSE;
00520     return JavaPackage_convert(cx, obj, JSTYPE_STRING, rval);
00521 }
00522 
00523 static JSFunctionSpec JavaPackage_methods[] = {
00524     {"toString",   JavaPackage_toString,        0,      0,      0},
00525     {0, 0, 0, 0, 0},
00526 };
00527 
00528 /*
00529  * One-time initialization for the JavaPackage class.  (This is not
00530  * run once per thread, rather it's run once for a given JSContext.)
00531  */
00532 JSBool
00533 jsj_init_JavaPackage(JSContext *cx, JSObject *global_obj,
00534                      JavaPackageDef *additional_predefined_packages) {
00535 
00536     /* Define JavaPackage class */
00537     if (!JS_InitClass(cx, global_obj, 0, &JavaPackage_class,
00538                       0, 0, 0, JavaPackage_methods, 0, 0))
00539         return JS_FALSE;
00540 
00541     /* Add top-level packages, e.g. : java, netscape, sun */
00542     if (!pre_define_java_packages(cx, global_obj, standard_java_packages))
00543         return JS_FALSE;
00544     if (!pre_define_java_packages(cx, global_obj, additional_predefined_packages))
00545         return JS_FALSE;
00546     
00547     return JS_TRUE;
00548 }