bug 470914 - add a __LOCATION__ field to the global object for files loaded on the commandline in xpcshell. r=timeless
authorTed Mielczarek <ted.mielczarek@gmail.com>
Sun, 18 Jan 2009 12:01:15 -0500
changeset 23903 6d33746ff263b52bf79d0b761e96020d9ce80c6c
parent 23902 eee705c20356aea694ecd7421680846855e7b3f5
child 23904 9001eaa33e8f91a1a6be587531c72bb74c034d23
child 23919 e0b6dc460118f29a6e18fb4a423b15bbbcf54dd9
push id4792
push usertmielczarek@mozilla.com
push dateSun, 18 Jan 2009 17:02:16 +0000
treeherdermozilla-central@6d33746ff263 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstimeless
bugs470914
milestone1.9.2a1pre
bug 470914 - add a __LOCATION__ field to the global object for files loaded on the commandline in xpcshell. r=timeless
js/src/xpconnect/shell/xpcshell.cpp
tools/test-harness/xpcshell-simple/example/location_load.js
tools/test-harness/xpcshell-simple/example/unit/test_location.js
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -50,29 +50,34 @@
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 #include "nsIInterfaceInfo.h"
 #include "nsIInterfaceInfoManager.h"
 #include "nsIXPCScriptable.h"
 #include "nsIServiceManager.h"
 #include "nsIComponentManager.h"
 #include "nsIComponentRegistrar.h"
+#include "nsILocalFile.h"
+#include "nsStringAPI.h"
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include "jsprf.h"
 #include "nscore.h"
 #include "nsMemory.h"
 #include "nsIGenericFactory.h"
 #include "nsIJSRuntimeService.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsIXPCSecurityManager.h"
 #ifdef XP_MACOSX
 #include "xpcshellMacUtils.h"
 #endif
+#ifdef XP_WIN
+#include <windows.h>
+#endif
 
 #ifndef XPCONNECT_STANDALONE
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #endif
 
 // all this crap is needed to do the interactive shell stuff
 #include <stdlib.h>
@@ -93,29 +98,110 @@
 #define DoEndRequest(cx)   JS_EndRequest((cx))
 #else
 #define DoBeginRequest(cx) ((void)0)
 #define DoEndRequest(cx)   ((void)0)
 #endif
 
 /***************************************************************************/
 
+static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
+
 #define EXITCODE_RUNTIME_ERROR 3
 #define EXITCODE_FILE_NOT_FOUND 4
 
 FILE *gOutFile = NULL;
 FILE *gErrFile = NULL;
 FILE *gInFile = NULL;
 
 int gExitCode = 0;
 JSBool gQuitting = JS_FALSE;
 static JSBool reportWarnings = JS_TRUE;
 static JSBool compileOnly = JS_FALSE;
 
 JSPrincipals *gJSPrincipals = nsnull;
+nsAutoString *gWorkingDirectory = nsnull;
+
+static JSBool
+GetLocationProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+#if !defined(XP_WIN) && !defined(XP_UNIX)
+    //XXX: your platform should really implement this
+    return JS_FALSE
+#endif
+    JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
+    JSScript *script = JS_GetFrameScript(cx, fp);
+    const char *filename = JS_GetScriptFilename(cx, script);
+
+    if (filename) {
+        nsresult rv;
+        nsCOMPtr<nsIXPConnect> xpc =
+            do_GetService(kXPConnectServiceContractID, &rv);
+
+#if defined(XP_WIN)
+        // convert from the system codepage to UTF-16
+        int bufferSize = MultiByteToWideChar(CP_ACP, 0, filename,
+                                             -1, NULL, 0);
+        nsAutoString filenameString;
+        filenameString.SetLength(bufferSize);
+        MultiByteToWideChar(CP_ACP, 0, filename,
+                            -1, (LPWSTR)filenameString.BeginWriting(),
+                            filenameString.Length());
+        // remove the null terminator
+        filenameString.SetLength(bufferSize - 1);
+
+        // replace forward slashes with backslashes,
+        // since nsLocalFileWin chokes on them
+        PRUnichar *start, *end;
+
+        filenameString.BeginWriting(&start, &end);
+        
+        while (start != end) {
+            if (*start == L'/')
+                *start = L'\\';
+            start++;
+        }
+#elif defined(XP_UNIX)
+        NS_ConvertUTF8toUTF16 filenameString(filename);
+#endif
+
+        nsCOMPtr<nsILocalFile> location;
+        if (NS_SUCCEEDED(rv)) {
+            rv = NS_NewLocalFile(filenameString,
+                                 PR_FALSE, getter_AddRefs(location));
+        }
+
+        if (!location && gWorkingDirectory) {
+            // could be a relative path, try appending it to the cwd
+            // and then normalize
+            nsAutoString absolutePath(*gWorkingDirectory);
+            absolutePath.Append(filenameString);
+
+            rv = NS_NewLocalFile(absolutePath,
+                                 PR_FALSE, getter_AddRefs(location));
+        }
+
+        if (location) {
+            nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
+            JSObject *locationObj = NULL;
+
+            location->Normalize();
+            rv = xpc->WrapNative(cx, obj, location,
+                                 NS_GET_IID(nsILocalFile),
+                                 getter_AddRefs(locationHolder));
+
+            if (NS_SUCCEEDED(rv) &&
+                NS_SUCCEEDED(locationHolder->GetJSObject(&locationObj))) {
+                *vp = OBJECT_TO_JSVAL(locationObj);
+            }
+        }
+    }
+
+    return JS_TRUE;
+}
 
 static JSBool
 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
 #ifdef EDITLINE
     /*
      * Use readline only if file is stdin, because there's no way to specify
      * another handle.  Are other filehandles interactive?
      */
@@ -697,17 +783,16 @@ my_GetErrorMessage(void *userRef, const 
 
 #ifdef EDITLINE
 extern "C" {
 extern char     *readline(const char *prompt);
 extern void     add_history(char *line);
 }
 #endif
 
-
 static void
 ProcessFile(JSContext *cx, JSObject *obj, const char *filename, FILE *file,
             JSBool forceTTY)
 {
     JSScript *script;
     jsval result;
     int lineno, startline;
     JSBool ok, hitEOF;
@@ -1394,16 +1479,55 @@ ContextCallback(JSContext *cx, uintN con
 
     if (contextOp == JSCONTEXT_NEW) {
         JS_SetErrorReporter(cx, my_ErrorReporter);
         JS_SetVersion(cx, JSVERSION_LATEST);
     }
     return JS_TRUE;
 }
 
+static bool
+GetCurrentWorkingDirectory(nsAString& workingDirectory)
+{
+#if !defined(XP_WIN) && !defined(XP_UNIX)
+    //XXX: your platform should really implement this
+    return false;
+#endif
+#ifdef XP_WIN
+    DWORD requiredLength = GetCurrentDirectoryW(0, NULL);
+    workingDirectory.SetLength(requiredLength);
+    GetCurrentDirectoryW(workingDirectory.Length(),
+                         (LPWSTR)workingDirectory.BeginWriting());
+    // we got a trailing null there
+    workingDirectory.SetLength(requiredLength);
+    workingDirectory.Replace(workingDirectory.Length() - 1, 1, L'\\');
+#elif defined(XP_UNIX)
+    nsCAutoString cwd;
+    // 1024 is just a guess at a sane starting value
+    size_t bufsize = 1024;
+    char* result = nsnull;
+    while (result == nsnull) {
+        if (!cwd.SetLength(bufsize))
+            return false;
+        result = getcwd(cwd.BeginWriting(), cwd.Length());
+        if (!result) {
+            if (errno != ERANGE)
+                return false;
+            // need to make the buffer bigger
+            bufsize *= 2;
+        }
+    }
+    // size back down to the actual string length
+    cwd.SetLength(strlen(result) + 1);
+    cwd.Replace(cwd.Length() - 1, 1, '/');
+    workingDirectory = NS_ConvertUTF8toUTF16(cwd);
+#endif
+    return true;
+}
+
 int
 main(int argc, char **argv, char **envp)
 {
 #ifdef XP_MACOSX
     InitAutoreleasePool();
 #endif
     JSRuntime *rt;
     JSContext *cx;
@@ -1540,16 +1664,23 @@ main(int argc, char **argv, char **envp)
         }
 
         envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
         if (!envobj || !JS_SetPrivate(cx, envobj, envp)) {
             JS_EndRequest(cx);
             return 1;
         }
 
+        nsAutoString workingDirectory;
+        if (GetCurrentWorkingDirectory(workingDirectory))
+            gWorkingDirectory = &workingDirectory;
+
+        JS_DefineProperty(cx, glob, "__LOCATION__", JSVAL_VOID,
+                          GetLocationProperty, NULL, 0);
+
         argc--;
         argv++;
 
         result = ProcessArgs(cx, glob, argv, argc);
 
 
 //#define TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN 1
 
new file mode 100644
--- /dev/null
+++ b/tools/test-harness/xpcshell-simple/example/location_load.js
@@ -0,0 +1,2 @@
+// Gets loaded via test_location.js
+do_check_eq(__LOCATION__.leafName, "location_load.js");
new file mode 100644
--- /dev/null
+++ b/tools/test-harness/xpcshell-simple/example/unit/test_location.js
@@ -0,0 +1,44 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* ***** 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.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Ted Mielczarek <ted.mielczarek@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either 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.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function run_test() {
+  do_check_eq(__LOCATION__.leafName, "test_location.js");
+  // also check that __LOCATION__ works via load()
+  do_import_script("tools/test-harness/xpcshell-simple/example/location_load.js");
+}