view spidermonkey-playground.cpp @ 77:8cf72992387d default tip

add JSONP
author Dion Almaer <dion@mozilla.com>
date Fri, 26 Jun 2009 10:13:32 -0700
parents ed2cf86a7c9d
children
line wrap: on
line source

/* ***** 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 Ubiquity.
 *
 * The Initial Developer of the Original Code is Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Atul Varma <atul@mozilla.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 ***** */

// This file is based on the code in the JSAPI User Guide:
//
// https://developer.mozilla.org/En/SpiderMonkey/JSAPI_User_Guide

#include "jsapi.h"
#include "jsdbgapi.h"

#include "tcb.h"
#include "wrapper.h"
#include "server_socket.h"
#include "memory_profiler.h"

// The name of our JS script containing our Trusted Code Base (TCB).
#define TCB_FILENAME "tcb.js"

// Global references to our TCB.
static JSContext *tcb_cx;
static JSObject  *tcb_global;

// The class of SecurableModules.
static JSClass SM_global_class = {
  "SecurableModuleGlobal", JSCLASS_GLOBAL_FLAGS,
  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};

// This native JS function is an implementation of the SecurableModule
// require() function.  For more information, see:
//
// https://wiki.mozilla.org/ServerJS/Modules/SecurableModules
//
// The function takes two parameters: a filename to import, and a JS
// object containing objects to export into the module.  The latter is
// accessible from the loaded module via a global called 'imports'.
//
// This function returns the SecurableModule's 'exports' global.

static JSBool require(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                      jsval *rval)
{
  char *filename;
  JSObject *exports;

  if (!JS_ConvertArguments(cx, argc, argv, "so", &filename, &exports))
    return JS_FALSE;

  FILE *f = fopen(filename, "r");
  if (!f) {
    JS_ReportError(cx, "File not found");
    return JS_FALSE;
  }

  fseek(f, 0, SEEK_END);
  long fileSize = ftell(f);
  fseek(f, 0, SEEK_SET);

  char source[fileSize];
  fread(source, fileSize, 1, f);
  fclose(f);

  // TODO: Check for return values here.
  JSContext *module_cx = JS_NewContext(JS_GetRuntime(cx), 8192);
  JS_BeginRequest(module_cx);
  JSObject *module_global = JS_NewObject(module_cx,
                                         &SM_global_class, NULL,
                                         NULL);
  JS_InitStandardClasses(module_cx, module_global);
  JS_DefineProperty(module_cx, module_global, "imports",
                    OBJECT_TO_JSVAL(exports), NULL, NULL, 0);
  JSObject *module_exports = JS_NewObject(module_cx, NULL, NULL,
                                          module_global);
  JS_DefineProperty(module_cx, module_global, "exports",
                    OBJECT_TO_JSVAL(module_exports), NULL, NULL, 0);

  jsval module_rval;
  if (!JS_EvaluateScript(module_cx, module_global, source,
                         fileSize, filename, 1, &module_rval)) {
    JS_EndRequest(module_cx);
    JS_DestroyContext(module_cx);
    return JS_FALSE;
  }

  *rval = OBJECT_TO_JSVAL(module_exports);

  JS_EndRequest(module_cx);
  JS_DestroyContext(module_cx);

  return JS_TRUE;
}

// Our global checkAccess callback just checks to see if a JS function called
// 'checkAccess' has been defined in the TCB, and delegates to that if so. If
// not, though, we do a default checkAccess.

static JSBool checkAccess(JSContext *cx, JSObject *obj, jsval id,
                          JSAccessMode mode, jsval *vp)
{
  jsval checkAccess;
  if (tcb_global && JS_GetProperty(tcb_cx, tcb_global, "checkAccess",
                                   &checkAccess) &&
      JSVAL_IS_OBJECT(checkAccess) &&
      JS_ObjectIsFunction(tcb_cx, JSVAL_TO_OBJECT(checkAccess))) {
    jsval argv[2];
    argv[0] = OBJECT_TO_JSVAL(obj);
    argv[1] = id;
    return JS_CallFunctionValue(tcb_cx, tcb_global, checkAccess, 2, argv,
                                vp);
  }

  // TODO: This doesn't work for the 'caller' attribute, and possibly other
  // things too.
  return JS_LookupPropertyById(cx, obj, id, vp);
}

static JSFunctionSpec tcb_global_functions[] = {
  JS_FS("getWrapper",     getWrapper,         1, 0, 0),
  JS_FS("unwrap",         unwrapObject,       1, 0, 0),
  JS_FS("wrap",           wrapObject,         2, 0, 0),
  JS_FS("require",        require,            2, 0, 0),
  JS_FS("profileMemory",  profileMemory,      0, 0, 0),
  JS_FS("ServerSocket",   createServerSocket, 0, 0, 0),
  JS_FS_END
};

static JSSecurityCallbacks securityCallbacks = {
  checkAccess,
  NULL,
  NULL
};

int main(int argc, const char *argv[])
{
  /* JS variables. */
  JSRuntime *rt;

  /* Create a JS runtime. */
  rt = JS_NewRuntime(8L * 1024L * 1024L);
  if (rt == NULL)
    return 1;

  JS_SetRuntimeSecurityCallbacks(rt, &securityCallbacks);

  /* Create a context. */
  tcb_cx = JS_NewContext(rt, 8192);
  if (tcb_cx == NULL)
    return 1;
  JS_SetOptions(tcb_cx, JSOPTION_VAROBJFIX);
  JS_SetVersion(tcb_cx, JSVERSION_LATEST);

  JS_BeginRequest(tcb_cx);

  jsval rval;

  if (!TCB_init(tcb_cx, &rval))
    return 1;
  tcb_global = JSVAL_TO_OBJECT(rval);

  JS_AddNamedRoot(tcb_cx, &tcb_global, "TCB Global");

  if (!JS_DefineFunctions(tcb_cx, tcb_global, tcb_global_functions))
    return 1;

  FILE *f = fopen(TCB_FILENAME, "r");
  if (!f)
    return 1;

  fseek(f, 0, SEEK_END);
  long fileSize = ftell(f);
  fseek(f, 0, SEEK_SET);
  
  char source[fileSize];
  fread(source, fileSize, 1, f);
  fclose(f);

  if (!JS_EvaluateScript(tcb_cx, tcb_global, source,
                         fileSize, TCB_FILENAME, 1,
                         &rval)) {
    TCB_handleError(tcb_cx, tcb_global);
    return 1;
  }

  /* Cleanup. */
  JS_RemoveRoot(tcb_cx, &tcb_global);
  JS_EndRequest(tcb_cx);
  JS_DestroyContext(tcb_cx);
  JS_DestroyRuntime(rt);
  JS_ShutDown();

  printf("Farewell.\n");
  return 0;
}