1 /*
   2  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.script.javascript;
  27 
  28 import sun.org.mozilla.javascript.internal.*;
  29 import javax.script.*;
  30 
  31 /**
  32  * This class serves as top level scope for Rhino. This class adds
  33  * 3 top level functions (bindings, scope, sync) and two constructors
  34  * (JSAdapter, JavaAdapter).
  35  *
  36  * @author A. Sundararajan
  37  * @since 1.6
  38  */
  39 public final class RhinoTopLevel extends ImporterTopLevel {
  40     RhinoTopLevel(Context cx, RhinoScriptEngine engine) {
  41         super(cx);
  42         this.engine = engine;
  43 
  44 
  45         // initialize JSAdapter lazily. Reduces footprint & startup time.
  46         new LazilyLoadedCtor(this, "JSAdapter",
  47                 "com.sun.script.javascript.JSAdapter",
  48                 false);
  49 
  50         /*
  51          * initialize JavaAdapter. We can't lazy initialize this because
  52          * lazy initializer attempts to define a new property. But, JavaAdapter
  53          * is an exisiting property that we overwrite.
  54          */
  55         JavaAdapter.init(cx, this, false);
  56 
  57         // add top level functions
  58         String names[] = { "bindings", "scope", "sync"  };
  59         defineFunctionProperties(names, RhinoTopLevel.class,
  60                 ScriptableObject.DONTENUM);
  61     }
  62 
  63     /**
  64      * The bindings function takes a JavaScript scope object
  65      * of type ExternalScriptable and returns the underlying Bindings
  66      * instance.
  67      *
  68      *    var page = scope(pageBindings);
  69      *    with (page) {
  70      *       // code that uses page scope
  71      *    }
  72      *    var b = bindings(page);
  73      *    // operate on bindings here.
  74      */
  75     public static Object bindings(Context cx, Scriptable thisObj, Object[] args,
  76             Function funObj) {
  77         if (args.length == 1) {
  78             Object arg = args[0];
  79             if (arg instanceof Wrapper) {
  80                 arg = ((Wrapper)arg).unwrap();
  81             }
  82             if (arg instanceof ExternalScriptable) {
  83                 ScriptContext ctx = ((ExternalScriptable)arg).getContext();
  84                 Bindings bind = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
  85                 return Context.javaToJS(bind,
  86                            ScriptableObject.getTopLevelScope(thisObj));
  87             }
  88         }
  89         return cx.getUndefinedValue();
  90     }
  91 
  92     /**
  93      * The scope function creates a new JavaScript scope object
  94      * with given Bindings object as backing store. This can be used
  95      * to create a script scope based on arbitrary Bindings instance.
  96      * For example, in webapp scenario, a 'page' level Bindings instance
  97      * may be wrapped as a scope and code can be run in JavaScripe 'with'
  98      * statement:
  99      *
 100      *    var page = scope(pageBindings);
 101      *    with (page) {
 102      *       // code that uses page scope
 103      *    }
 104      */
 105     public static Object scope(Context cx, Scriptable thisObj, Object[] args,
 106             Function funObj) {
 107         if (args.length == 1) {
 108             Object arg = args[0];
 109             if (arg instanceof Wrapper) {
 110                 arg = ((Wrapper)arg).unwrap();
 111             }
 112             if (arg instanceof Bindings) {
 113                 ScriptContext ctx = new SimpleScriptContext();
 114                 ctx.setBindings((Bindings)arg, ScriptContext.ENGINE_SCOPE);
 115                 Scriptable res = new ExternalScriptable(ctx);
 116                 res.setPrototype(ScriptableObject.getObjectPrototype(thisObj));
 117                 res.setParentScope(ScriptableObject.getTopLevelScope(thisObj));
 118                 return res;
 119             }
 120         }
 121         return cx.getUndefinedValue();
 122     }
 123 
 124     /**
 125      * The sync function creates a synchronized function (in the sense
 126      * of a Java synchronized method) from an existing function. The
 127      * new function synchronizes on the <code>this</code> object of
 128      * its invocation.
 129      * js> var o = { f : sync(function(x) {
 130      *       print("entry");
 131      *       Packages.java.lang.Thread.sleep(x*1000);
 132      *       print("exit");
 133      *     })};
 134      * js> thread(function() {o.f(5);});
 135      * entry
 136      * js> thread(function() {o.f(5);});
 137      * js>
 138      * exit
 139      * entry
 140      * exit
 141      */
 142     public static Object sync(Context cx, Scriptable thisObj, Object[] args,
 143             Function funObj) {
 144         if (args.length == 1 && args[0] instanceof Function) {
 145             return new Synchronizer((Function)args[0]);
 146         } else {
 147             throw Context.reportRuntimeError("wrong argument(s) for sync");
 148         }
 149     }
 150 
 151     RhinoScriptEngine getScriptEngine() {
 152         return engine;
 153     }
 154 
 155     private RhinoScriptEngine engine;
 156 }