Skip to content

Commit

Permalink
GlobalScope is removed as its introduction had to serious consequence…
Browse files Browse the repository at this point in the history
…s with compatibility and usage. Instead a special purpose class ClassCache is introduces to hold various caches. The class binds itself to an arbitrary object by using new SciptableObject.associateValue method so arbitrary ScriptableObject can be used as a scope without performance penalties.
  • Loading branch information
ibukanov committed Nov 1, 2003
1 parent 0024622 commit 8047a4a
Show file tree
Hide file tree
Showing 16 changed files with 327 additions and 261 deletions.
2 changes: 1 addition & 1 deletion apiClasses.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apiClasses=\
src/org/mozilla/javascript/ClassCache.java,\
src/org/mozilla/javascript/ClassDefinitionException.java,\
src/org/mozilla/javascript/ClassRepository.java,\
src/org/mozilla/javascript/ClassShutter.java,\
Expand All @@ -8,7 +9,6 @@ apiClasses=\
src/org/mozilla/javascript/ErrorReporter.java,\
src/org/mozilla/javascript/Function.java,\
src/org/mozilla/javascript/FunctionObject.java,\
src/org/mozilla/javascript/GlobalScope.java,\
src/org/mozilla/javascript/ImporterTopLevel.java,\
src/org/mozilla/javascript/JavaScriptException.java,\
src/org/mozilla/javascript/PropertyException.java,\
Expand Down
4 changes: 2 additions & 2 deletions docs/scopes.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ <h2> Scopes</h2>
<p>A top-level scope is created by calling <tt>Context.initStandardObjects</tt>
to create all the standard objects: </p>
<pre>
GlobalScope scope = cx.initStandardObjects();
ScriptableObject scope = cx.initStandardObjects();
</pre>
The easiest way to embed Rhino is just to create a new scope this way whenever
you need one. However, <tt>initStandardObjects</tt> is an expensive method
Expand Down Expand Up @@ -146,7 +146,7 @@ <h2>Sealed shared scopes</h2>
<p>The ECMAScript standard defines that scripts can add properties to all standard library objects and in many cases it is also possible to change or delete their properties as well. Such behavior may not be suitable with shared scopes since if a script by mistake adds a property to a library object from the shared scope, that object would not be garbage collected until there re active references to the shared scope potentially leading to memory leaks. In addition if a script alters some of the standard objects, the library may not work properly for other scripts. Such bugs are hard to debug and to remove a possibility for them to occur one can use seal the shared scope and all its objects.
<p>
A notion of a sealed object is a JavaScript extension supported by Rhino and it means that properties can not be added/deleted to the object and the existing object properties can not be changed. Any attempt to modify sealed object throws an exception. To seal all objects in the standard library pass <tt>true</tt> for the sealed argument when calling <tt>Context.initStandardObjects(boolean)</tt>:
<pre> GlobalScope sealedSharedScope = cx.initStandardObjects(true);</pre>
<pre> ScriptableObject sealedSharedScope = cx.initStandardObjects(true);</pre>
This seals only all standard library objects, it does not seal the shared scope itself thus after calling <tt>initStandardObjects</tt>, <tt>sealedSharedScope</tt> cab be farther populated with application-specific objects and functions. Then after a custom initialization is done, one can seal the shared scope by calling <tt>ScriptableObject.sealObject()</tt>:
<pre> sealedSharedScope.sealObject();</pre>

Expand Down
2 changes: 1 addition & 1 deletion examples/DynamicScopes.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ static void runScripts(Context cx)
// Initialize the standard objects (Object, Function, etc.)
// This must be done before scripts can be executed. The call
// returns a new scope that we will share.
GlobalScope scope = cx.initStandardObjects(true);
ScriptableObject scope = cx.initStandardObjects(true);

// Now we can evaluate a script and functions will be compiled to
// use dynamic scope if the Context is so initialized.
Expand Down
9 changes: 7 additions & 2 deletions examples/Shell.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@
*
* @author Norris Boyd
*/
public class Shell extends GlobalScope
public class Shell extends ScriptableObject
{
public String getClassName()
{
return "global";
}

/**
* Main entry point.
*
Expand Down Expand Up @@ -213,7 +218,7 @@ public static double version(Context cx, Scriptable thisObj,
public static void load(Context cx, Scriptable thisObj,
Object[] args, Function funObj)
{
Shell shell = (Shell)GlobalScope.get(thisObj);
Shell shell = (Shell)getTopLevelScope(thisObj);
for (int i = 0; i < args.length; i++) {
shell.processSource(cx, cx.toString(args[i]));
}
Expand Down
199 changes: 199 additions & 0 deletions src/org/mozilla/javascript/ClassCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/* -*- Mode: java; 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 cache holder of generated code for Java reflection
*
* The Initial Developer of the Original Code is
* RUnit Software AS.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s): Igor Bukanov, igor@fastmail.fm
*
* 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 ***** */

package org.mozilla.javascript;

import java.io.*;
import java.util.Hashtable;

/**
* Cache of generated classes and data structures to access Java runtime
* from JavaScript.
*
* @author Igor Bukanov
*
* @since Rhino 1.5 Release 5
*/
public class ClassCache
{
private static final Object AKEY = new Object();

private volatile boolean cachingIsEnabled = true;

Hashtable classTable = new Hashtable();

boolean invokerOptimization = true;
Invoker invokerMaster;

Hashtable javaAdapterGeneratedClasses = new Hashtable();

Hashtable javaAdapterIFGlueMasters = new Hashtable();

private int generatedClassSerial;

/**
* Search for ClassCache object in the given scope.
* The method first calls
* {@link ScriptableObject.getTopLevelScope(Scriptable scope)}
* to get the top most scope and then tries to locate associated
* ClassCache object in the prototype chain of the top scope.
*
* @param scope scope to search for ClassCache object.
* @return previously associated ClassCache object or a new instance of
* ClassCache if no ClassCache object was found.
*
* @see #associate(ScriptableObject topScope)
*/
public static ClassCache get(Scriptable scope)
{
scope = ScriptableObject.getTopLevelScope(scope);
Scriptable obj = scope;
do {
if (obj instanceof ScriptableObject) {
ScriptableObject so = (ScriptableObject)obj;
ClassCache lc = (ClassCache)so.getAssociatedValue(AKEY);
if (lc != null) {
return lc;
}
}
obj = obj.getPrototype();
} while (obj != null);

// ALERT: warn somehow about wrong cache usage ?
return new ClassCache();
}

/**
* Associate ClassCache object with the given top-level scope.
* The ClassCache object can only be associated with the given scope once.
*
* @param topScope scope to associate this ClassCache object with.
* @return true if no prevous ClassCache objects were embedded into
* the scope and this ClassCache were successfully associated
* or false otherwise.
*
* @see #get(Scriptable scope)
*/
public boolean associate(ScriptableObject topScope)
{
if (topScope.getParentScope() != null) {
// Can only associate cache with top level scope
throw new IllegalArgumentException();
}
return this != topScope.associateValue(AKEY, this);
}

/**
* Empty caches of generated Java classes and Java reflection information.
*/
public synchronized void clearCaches()
{
classTable = new Hashtable();
javaAdapterGeneratedClasses = new Hashtable();
javaAdapterIFGlueMasters = new Hashtable();
Invoker im = invokerMaster;
if (im != null) {
im.clearMasterCaches();
}
}

/**
* Check if generated Java classes and Java reflection information
* is cached.
*/
public final boolean isCachingEnabled()
{
return cachingIsEnabled;
}

/**
* Set whether to cache some values.
* <p>
* By default, the engine will cache generated classes and
* results of <tt>Class.getMethods()</tt> and similar calls.
* This can speed execution dramatically, but increases the memory
* footprint. Also, with caching enabled, references may be held to
* objects past the lifetime of any real usage.
* <p>
* If caching is enabled and this method is called with a
* <code>false</code> argument, the caches will be emptied.
* <p>
* Caching is enabled by default.
*
* @param cachingEnabled if true, caching is enabled
*
* @see #clearCaches()
*/
public synchronized void setCachingEnabled(boolean enabled)
{
if (enabled == cachingIsEnabled)
return;
if (!enabled)
clearCaches();
cachingIsEnabled = enabled;
}

/**
* To optimize invocation of reflected Java methods, the engine generates
* special glue classes that will call the methods directly. By default
* the optimization is enabled since it allows to speedup method invocation
* compared with calling <tt>Method.invoke</tt> by factor 2-2.5 under JDK
* 1.4.2 and by factor 10-15 under JDK 1.3.1. If increase memory
* consumption is too high or the optimization brings no benefits in a
* particular VM, then the optimization can be disabled.
*
* @param enabled if true, invoke optimization is enabled.
*/
public synchronized void setInvokerOptimizationEnabled(boolean enabled)
{
if (invokerOptimization == enabled)
return;
if (!enabled)
invokerMaster = null;
invokerOptimization = enabled;
}

/**
* Internal engine method to return serial number for generated classes
* to ensure name uniqueness.
*/
public final synchronized int newClassSerialNumber()
{
return ++generatedClassSerial;
}
}
27 changes: 9 additions & 18 deletions src/org/mozilla/javascript/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ public static EvaluatorException reportRuntimeError(String message) {
*
* @return the initialized scope
*/
public GlobalScope initStandardObjects()
public ScriptableObject initStandardObjects()
{
return initStandardObjects(false);
}
Expand Down Expand Up @@ -699,11 +699,11 @@ public GlobalScope initStandardObjects()
* cannot be modified.
* @return the initialized scope
*/
public GlobalScope initStandardObjects(boolean sealed)
public ScriptableObject initStandardObjects(boolean sealed)
{
GlobalScope global = new GlobalScope();
initStandardObjects(global, sealed);
return global;
NativeObject scope = new NativeObject();
initStandardObjects(scope, sealed);
return scope;
}

/**
Expand All @@ -718,10 +718,6 @@ public GlobalScope initStandardObjects(boolean sealed)
*
* This method does not affect the Context it is called upon.
*
* If the explicit scope argument is not null and is not an instance of
* {@link GlobalScope} or its subclasses, parts of the standard library
* will be run slower.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @return the initialized scope
Expand Down Expand Up @@ -750,10 +746,6 @@ public ScriptableObject initStandardObjects(ScriptableObject scope)
* the current ECMA/ISO language specification, but is likely for
* the next version.
*
* If the explicit scope argument is not null and is not an instance of
* {@link GlobalScope} or its subclasses, parts of the standard library
* will be run slower.
*
* @param scope the scope to initialize, or null, in which case a new
* object will be created to serve as the scope
* @param sealed whether or not to create sealed standard objects that
Expand All @@ -765,10 +757,9 @@ public ScriptableObject initStandardObjects(ScriptableObject scope,
boolean sealed)
{
if (scope == null) {
scope = new GlobalScope();
} else if (!(scope instanceof GlobalScope)) {
GlobalScope.embed(scope);
scope = new NativeObject();
}
(new ClassCache()).associate(scope);

BaseFunction.init(this, scope, sealed);
NativeObject.init(this, scope, sealed);
Expand Down Expand Up @@ -1529,8 +1520,8 @@ public void setCompileFunctionsWithDynamicScope(boolean flag) {

/**
* @deprecated To enable/disable caching for a particular top scope,
* use {@link GlobalScope#get(Scriptable)} and
* {@link GlobalScope#setCachingEnabled(boolean)}.
* use {@link ClassCache#get(Scriptable)} and
* {@link ClassCache#setReflectionCachingEnabled(boolean)}.
* The function is kept only for compatibility and does nothing.
*/
public static void setCachingEnabled(boolean cachingEnabled)
Expand Down
6 changes: 3 additions & 3 deletions src/org/mozilla/javascript/FunctionObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ public class FunctionObject extends BaseFunction
public FunctionObject(String name, Member methodOrConstructor,
Scriptable scope)
{
GlobalScope global = GlobalScope.get(scope);
ClassCache cache = ClassCache.get(scope);
if (methodOrConstructor instanceof Constructor) {
member = new MemberBox((Constructor) methodOrConstructor, global);
member = new MemberBox((Constructor) methodOrConstructor, cache);
isStatic = true; // well, doesn't take a 'this'
} else {
member = new MemberBox((Method) methodOrConstructor, global);
member = new MemberBox((Method) methodOrConstructor, cache);
isStatic = member.isStatic();
}
String methodName = member.getName();
Expand Down
Loading

0 comments on commit 8047a4a

Please sign in to comment.