1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 import org.mozilla.javascript.*; 8 9 /** 10 * Example of controlling the JavaScript with multiple scopes and threads. 11 */ 12 public class DynamicScopes { 13 14 static boolean useDynamicScope; 15 16 static class MyFactory extends ContextFactory 17 { 18 @Override hasFeature(Context cx, int featureIndex)19 protected boolean hasFeature(Context cx, int featureIndex) 20 { 21 if (featureIndex == Context.FEATURE_DYNAMIC_SCOPE) { 22 return useDynamicScope; 23 } 24 return super.hasFeature(cx, featureIndex); 25 } 26 } 27 28 static { ContextFactory.initGlobal(new MyFactory())29 ContextFactory.initGlobal(new MyFactory()); 30 } 31 32 33 /** 34 * Main entry point. 35 * 36 * Set up the shared scope and then spawn new threads that execute 37 * relative to that shared scope. Try to run functions with and 38 * without dynamic scope to see the effect. 39 * 40 * The expected output is 41 * <pre> 42 * sharedScope 43 * nested:sharedScope 44 * sharedScope 45 * nested:sharedScope 46 * sharedScope 47 * nested:sharedScope 48 * thread0 49 * nested:thread0 50 * thread1 51 * nested:thread1 52 * thread2 53 * nested:thread2 54 * </pre> 55 * The final three lines may be permuted in any order depending on 56 * thread scheduling. 57 */ main(String[] args)58 public static void main(String[] args) 59 { 60 Context cx = Context.enter(); 61 try { 62 // Precompile source only once 63 String source = "" 64 +"var x = 'sharedScope';\n" 65 +"function f() { return x; }\n" 66 // Dynamic scope works with nested function too 67 +"function initClosure(prefix) {\n" 68 +" return function test() { return prefix+x; }\n" 69 +"}\n" 70 +"var closure = initClosure('nested:');\n" 71 +""; 72 Script script = cx.compileString(source, "sharedScript", 1, null); 73 74 useDynamicScope = false; 75 runScripts(cx, script); 76 useDynamicScope = true; 77 runScripts(cx, script); 78 } finally { 79 Context.exit(); 80 } 81 } 82 runScripts(Context cx, Script script)83 static void runScripts(Context cx, Script script) 84 { 85 // Initialize the standard objects (Object, Function, etc.) 86 // This must be done before scripts can be executed. The call 87 // returns a new scope that we will share. 88 ScriptableObject sharedScope = cx.initStandardObjects(null, true); 89 90 // Now we can execute the precompiled script against the scope 91 // to define x variable and f function in the shared scope. 92 script.exec(cx, sharedScope); 93 94 // Now we spawn some threads that execute a script that calls the 95 // function 'f'. The scope chain looks like this: 96 // <pre> 97 // ------------------ ------------------ 98 // | per-thread scope | -prototype-> | shared scope | 99 // ------------------ ------------------ 100 // ^ 101 // | 102 // parentScope 103 // | 104 // ------------------ 105 // | f's activation | 106 // ------------------ 107 // </pre> 108 // Both the shared scope and the per-thread scope have variables 'x' 109 // defined in them. If 'f' is compiled with dynamic scope enabled, 110 // the 'x' from the per-thread scope will be used. Otherwise, the 'x' 111 // from the shared scope will be used. The 'x' defined in 'g' (which 112 // calls 'f') should not be seen by 'f'. 113 final int threadCount = 3; 114 Thread[] t = new Thread[threadCount]; 115 for (int i=0; i < threadCount; i++) { 116 String source2 = "" 117 +"function g() { var x = 'local'; return f(); }\n" 118 +"java.lang.System.out.println(g());\n" 119 +"function g2() { var x = 'local'; return closure(); }\n" 120 +"java.lang.System.out.println(g2());\n" 121 +""; 122 t[i] = new Thread(new PerThread(sharedScope, source2, 123 "thread" + i)); 124 } 125 for (int i=0; i < threadCount; i++) 126 t[i].start(); 127 // Don't return in this thread until all the spawned threads have 128 // completed. 129 for (int i=0; i < threadCount; i++) { 130 try { 131 t[i].join(); 132 } catch (InterruptedException e) { 133 } 134 } 135 } 136 137 static class PerThread implements Runnable { 138 PerThread(Scriptable sharedScope, String source, String x)139 PerThread(Scriptable sharedScope, String source, String x) { 140 this.sharedScope = sharedScope; 141 this.source = source; 142 this.x = x; 143 } 144 run()145 public void run() { 146 // We need a new Context for this thread. 147 Context cx = Context.enter(); 148 try { 149 // We can share the scope. 150 Scriptable threadScope = cx.newObject(sharedScope); 151 threadScope.setPrototype(sharedScope); 152 153 // We want "threadScope" to be a new top-level 154 // scope, so set its parent scope to null. This 155 // means that any variables created by assignments 156 // will be properties of "threadScope". 157 threadScope.setParentScope(null); 158 159 // Create a JavaScript property of the thread scope named 160 // 'x' and save a value for it. 161 threadScope.put("x", threadScope, x); 162 cx.evaluateString(threadScope, source, "threadScript", 1, null); 163 } finally { 164 Context.exit(); 165 } 166 } 167 private Scriptable sharedScope; 168 private String source; 169 private String x; 170 } 171 172 } 173 174