aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/mozilla/projects/rhino/embedding_tutorial/index.html
blob: b7cf0168f51b76a5a2bce3251dd93964a7cbb6d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
---
title: 'Tutorial: Embedding Rhino'
slug: Mozilla/Projects/Rhino/Embedding_tutorial
translation_of: Mozilla/Projects/Rhino/Embedding_tutorial
---
<p>Embedding Rhino can be done simply with good results. With more effort on the part of the embedder, the objects exposed to scripts can be customized further.</p>
<p>This tutorial leads you through the steps from a simple embedding to more customized, complex embeddings. Fully compilable examples are provided along the way.</p>
<p>The examples live in the <code>rhino/examples</code> directory in the distribution and in <code>mozilla/js/rhino/examples</code> in cvs. This document will link to them using <a href="http://lxr.mozilla.org/">lxr</a>.</p>
<p>In this document, JavaScript code will be in <span class="java_js_code">green</span>, Java code will be in <span class="java_js_code">green,</span> and shell logs will be in <span class="shell_logs_code">purple</span>.</p>
<h2 id="In_this_document">In this document:</h2>
<ul class="toc">
 <li><a href="#runScript">RunScript: A simple embedding</a>
  <ul>
   <li><a href="#enteringContext">Entering a Context</a></li>
   <li><a href="#initializing">Initializing standard objects</a></li>
   <li><a href="#collecting">Collecting the arguments</a></li>
   <li><a href="#evaluating">Evaluating a script</a></li>
   <li><a href="#print">Printing the result</a></li>
   <li><a href="#exit">Exiting the Context</a></li>
  </ul>
 </li>
 <li><a href="#expose">Expose Java APIs</a>
  <ul>
   <li><a href="#useJava">Using Java APIs</a></li>
   <li><a href="#implementingInterfaces">Implementing interfaces</a></li>
   <li><a href="#addJava">Adding Java objects</a></li>
  </ul>
 </li>
 <li><a href="#usingJSObjs">Using JavaScript objects from Java</a>
  <ul>
   <li><a href="#usingJSvars">Using JavaScript variables</a></li>
   <li><a href="#callingJSfuns">Calling JavaScript functions</a></li>
  </ul>
 </li>
 <li><a href="#javaScriptHostObjects">JavaScript host objects</a>
  <ul>
   <li><a href="#definingHostObjects">Defining Host Objects</a></li>
   <li><a href="#counter">Counter example</a>
    <ul>
     <li><a href="#counterCtors">Counter's constructors</a></li>
     <li><a href="#classname">Class name</a></li>
     <li><a href="#dynamic">Dynamic properties</a></li>
     <li><a href="#definingMethods">Defining JavaScript "methods"</a></li>
     <li><a href="#addingCounter">Adding Counter to RunScript</a></li>
    </ul>
   </li>
  </ul>
 </li>
</ul>
<h2 id="RunScript_A_simple_embedding"><a id="runScript" name="runScript">RunScript: A simple embedding</a></h2>
<p>About the simplest embedding of Rhino possible is the <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/RunScript.java">RunScript example</a>. All it does it read a script from the command line, execute it, and print a result.</p>
<p>Here's an example use of RunScript from a shell command line:</p>
<pre class="code shell_logs_code">$ java RunScript "Math.cos(Math.PI)"
-1
$ java RunScript "function f(x){return x+1} f(7)"
8
</pre>
<p>Note that you'll have to have both the Rhino classes and the RunScript example class file in the classpath. Let's step through the body of <code>main</code> one line at time.</p>
<h3 id="Entering_a_Context"><a id="enteringContext" name="enteringContext">Entering a Context</a></h3>
<p>The code</p>
<pre class="code java_js_code">Context cx = Context.enter();
</pre>
<p>Creates and enters a <code>Context</code>. A <code>Context</code> stores information about the execution environment of a script.</p>
<h3 id="Initializing_standard_objects"><a name="initializing">Initializing standard objects</a></h3>
<p>The code</p>
<pre class="code java_js_code">Scriptable scope = cx.initStandardObjects();
</pre>
<p>Initializes the standard objects (<code>Object</code>, <code>Function</code>, etc.) This must be done before scripts can be executed. The <var>null</var> parameter tells <code>initStandardObjects</code> to create and return a scope object that we use in later calls.</p>
<h3 id="Collecting_the_arguments"><a id="collecting" name="collecting">Collecting the arguments</a></h3>
<p>This code is standard Java and not specific to Rhino. It just collects all the arguments and concatenates them together.</p>
<pre class="code java_js_code">String s = "";
for (int i=0; i &lt; args.length; i++) {
    s += args[i];
}
</pre>
<h3 id="Evaluating_a_script"><a id="evaluating" name="evaluating">Evaluating a script</a></h3>
<p>The code</p>
<pre class="code java_js_code">Object result = cx.evaluateString(scope, s, "&lt;cmd&gt;", 1, null);
</pre>
<p>uses the Context <code>cx</code> to evaluate a string. Evaluation of the script looks up variables in <var>scope</var>, and errors will be reported with the filename <code>&lt;cmd&gt;</code> and line number 1.</p>
<h3 id="Printing_the_result"><a id="print" name="print">Printing the result</a></h3>
<p>The code</p>
<pre class="code java_js_code">System.out.println(cx.toString(result));
</pre>
<p>prints the result of evaluating the script (contained in the variable <var>result</var>). <var>result</var> could be a string, JavaScript object, or other values. The <code>toString</code> method converts any JavaScript value to a string.</p>
<h3 id="Exiting_the_Context"><a id="exit" name="exit">Exiting the Context</a></h3>
<p>The code</p>
<pre class="code java_js_code">} finally {
    Context.exit();
}
</pre>
<p>exits the Context. This removes the association between the Context and the current thread and is an essential cleanup action. There should be a call to <code>exit</code> for every call to <code>enter</code>. To make sure that it is called even if an exception is thrown, it is put into the finally block corresponding to the try block starting after <code>Context.enter()</code>.</p>
<h2 id="Expose_Java_APIs"><a id="expose" name="expose">Expose Java APIs</a></h2>
<h3 id="Using_Java_APIs"><a id="useJava" name="useJava">Using Java APIs</a></h3>
<p>No additional code in the embedding needed! The JavaScript feature called
 <i>
  LiveConnect</i>
 allows JavaScript programs to interact with Java objects:</p>
<pre class="code shell_logs_code">$ java RunScript "java.lang.System.out.println(3)"
3.0
undefined
</pre>
<h3 id="Implementing_interfaces"><a id="implementingInterfaces" name="implementingInterfaces">Implementing interfaces</a></h3>
<p>Using Rhino, JavaScript objects can implement arbitrary Java interfaces. There's no Java code to write -- it's part of Rhino's LiveConnect implementation. For example, we can see how to implement java.lang.Runnable in a Rhino shell session:</p>
<pre class="code shell_logs_code">js&gt; obj = { run: function() { print("hi"); } }
[object Object]
js&gt; obj.run()
hi
js&gt; r = new java.lang.Runnable(obj);
[object Object]
js&gt; t = new java.lang.Thread(r)
Thread[Thread-0,5,main]
js&gt; t.start()
hi
</pre>
<h3 id="Adding_Java_objects"><a id="addJava" name="addJava">Adding Java objects</a></h3>
<p>The next example is <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/RunScript2.java">RunScript2</a>. This is the same as RunScript, but with the addition of two extra lines of code:</p>
<pre class="code java_js_code">Object wrappedOut = Context.javaToJS(System.out, scope);
ScriptableObject.putProperty(scope, "out", wrappedOut);
</pre>
<p>These lines add a global variable <code>out</code> that is a JavaScript reflection of the <code>System.out</code> variable:</p>
<pre class="code shell_logs_code">$ java RunScript2 "out.println(42)"
42.0
undefined
</pre>
<h2 id="Using_JavaScript_objects_from_Java"><a id="usingJSObjs" name="usingJSObjs">Using JavaScript objects from Java</a></h2>
<p>After evaluating a script it's possible to query the scope for variables and functions, extracting values and calling JavaScript functions. This is illustrated in the <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/RunScript3.java">RunScript3</a> example. This example adds the ability to print the value of variable <var>x</var> and the result of calling function <code>f</code>. Both <var>x</var> and <var>f</var> are expected to be defined by the evaluated script. For example,</p>
<pre class="code shell_logs_code">$ java RunScript3 "x = 7"
x = 7
f is undefined or not a function.
$ java RunScript3 "function f(a) { return a; }"
x is not defined.
f("my args") = my arg
</pre>
<h3 id="Using_JavaScript_variables"><a id="usingJSvars" name="usingJSvars">Using JavaScript variables</a></h3>
<p>To print out the value of <var>x</var>, we add the following code:</p>
<pre class="code java_js_code">Object x = scope.get("x", scope);
if (x == Scriptable.NOT_FOUND) {
    System.out.println("x is not defined.");
} else {
    System.out.println("x = " + Context.toString(x));
}
</pre>
<h3 id="Calling_JavaScript_functions"><a id="callingJSfuns" name="callingJSfuns">Calling JavaScript functions</a></h3>
<p>To get the function <var>f</var>, call it, and print the result, we add this code:</p>
<pre class="code java_js_code">Object fObj = scope.get("f", scope);
if (!(fObj instanceof Function)) {
    System.out.println("f is undefined or not a function.");
} else {
    Object functionArgs[] = { "my arg" };
    Function f = (Function)fObj;
    Object result = f.call(cx, scope, scope, functionArgs);
    String report = "f('my args') = " + Context.toString(result);
    System.out.println(report);
}
</pre>
<h2 id="JavaScript_host_objects"><a id="javaScriptHostObjects" name="javaScriptHostObjects">JavaScript host objects</a></h2>
<h3 id="Defining_Host_Objects"><a id="definingHostObjects" name="definingHostObjects">Defining Host Objects</a></h3>
<p>Custom host objects can implement special JavaScript features like dynamic properties.</p>
<h3 id="Counter_example"><a id="counter" name="counter">Counter example</a></h3>
<p>The <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/Counter.java">Counter example</a> is a simple host object. We'll go through it method by method below.</p>
<p>It's easy to try out new host object classes in the shell using its built-in <code>defineClass</code> function. We'll see how to add it to RunScript later. (Note that because the <code>java -jar</code> option preempts the rest of the classpath, we can't use that and access the <code>Counter</code> class.)</p>
<pre class="code shell_logs_code">$ java -cp "js.jar;examples" org.mozilla.javascript.tools.shell.Main
js&gt; defineClass("Counter")
js&gt; c = new Counter(7)
[object Counter]
js&gt; c.count
7
js&gt; c.count
8
js&gt; c.count
9
js&gt; c.resetCount()
js&gt; c.count
0
</pre>
<h3 id="Counter's_constructors"><a id="counterCtors" name="counterCtors">Counter's constructors</a></h3>
<p>The zero-argument constructor is used by Rhino runtime to create instances. For the counter example, no initialization work is needed, so the implementation is empty.</p>
<pre class="code java_js_code">public Counter () { }
</pre>
<p>The method <code>jsConstructor</code> defines the JavaScript constructor that was called with the expression <code>new Counter(7)</code> in the JavaScript code above.</p>
<pre class="code java_js_code">public void jsConstructor(int a) { count
= a; }
</pre>
<h3 id="Class_name"><a id="classname" name="classname">Class name</a></h3>
<p>The class name is defined by the <code>getClassName</code> method. This is used to determine the name of the constructor.</p>
<pre class="code java_js_code">public String getClassName() { return "Counter";
}
</pre>
<h3 id="Dynamic_properties"><a id="dynamic" name="dynamic">Dynamic properties</a></h3>
<p>Dynamic properties are defined by methods beginning with <code>jsGet_</code> or <code>jsSet_</code>. The method <code>jsGet_count</code> defines the
 <i>
  count</i>
 property.</p>
<pre class="code java_js_code">public int jsGet_count() { return count++;
}
</pre>
<p>The expression <code>c.count</code> in the JavaScript code above results in a call to this method.</p>
<h3 id="Defining_JavaScript_methods"><a id="definingMethods" name="definingMethods">Defining JavaScript "methods"</a></h3>
<p>Methods can be defined using the <code>jsFunction_ prefix</code>. Here we define <code>resetCount</code> for JavaScript.</p>
<pre class="code java_js_code">public void jsFunction_resetCount() { count
= 0; }
</pre>
<p>The call <code>c.resetCount()</code> above calls this method.</p>
<h3 id="Adding_Counter_to_RunScript"><a id="addingCounter" name="addingCounter">Adding Counter to RunScript</a></h3>
<p>Now take a look at the <a href="http://lxr.mozilla.org/mozilla/source/js/rhino/examples/RunScript4.java">RunScript4 example</a>. It's the same as RunScript except for two additions. The method <code>ScriptableObject.defineClass</code> uses a Java class to define the Counter "class" in the top-level scope:</p>
<pre class="code java_js_code">ScriptableObject.defineClass(scope, Counter.class);
</pre>
<p>Now we can reference the <code>Counter</code> object from our script:</p>
<pre class="code shell_logs_code">$ java RunScript4 "c = new Counter(3); c.count;
c.count;"
</pre>
<p>It also creates a new instance of the <code>Counter</code> object from within our Java code, constructing it with the value 7, and assigning it to the top-level variable <code>myCounter</code>:</p>
<pre class="code java_js_code">Object[] arg = { new Integer(7) };
Scriptable myCounter = cx.newObject(scope, "Counter", arg);
scope.put("myCounter", scope, myCounter);
</pre>
<p>Now we can reference the <code>myCounter</code> object from our script:</p>
<pre class="code shell_logs_code">$ java RunScript3 'RunScript4 'myCounter.count; myCounter.count'
8
</pre>