History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/script/javascript/ExternalScriptable.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/script/javascript/ExternalScriptable.java+467−0
@@ -0,0 +1,467 @@
1+/*
2+ * Copyright (c) 2005, 2006, 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+import sun.org.mozilla.javascript.internal.*;
28+import javax.script.*;
29+import java.util.*;
30+
31+/**
32+ * ExternalScriptable is an implementation of Scriptable
33+ * backed by a JSR 223 ScriptContext instance.
34+ *
35+ * @author Mike Grogan
36+ * @author A. Sundararajan
37+ * @since 1.6
38+ */
39+
40+final class ExternalScriptable implements Scriptable {
41+ /* Underlying ScriptContext that we use to store
42+ * named variables of this scope.
43+ */
44+ private ScriptContext context;
45+
46+ /* JavaScript allows variables to be named as numbers (indexed
47+ * properties). This way arrays, objects (scopes) are treated uniformly.
48+ * Note that JSR 223 API supports only String named variables and
49+ * so we can't store these in Bindings. Also, JavaScript allows name
50+ * of the property name to be even empty String! Again, JSR 223 API
51+ * does not support empty name. So, we use the following fallback map
52+ * to store such variables of this scope. This map is not exposed to
53+ * JSR 223 API. We can just script objects "as is" and need not convert.
54+ */
55+ private Map<Object, Object> indexedProps;
56+
57+ // my prototype
58+ private Scriptable prototype;
59+ // my parent scope, if any
60+ private Scriptable parent;
61+
62+ ExternalScriptable(ScriptContext context) {
63+ this(context, new HashMap<Object, Object>());
64+ }
65+
66+ ExternalScriptable(ScriptContext context, Map<Object, Object> indexedProps) {
67+ if (context == null) {
68+ throw new NullPointerException("context is null");
69+ }
70+ this.context = context;
71+ this.indexedProps = indexedProps;
72+ }
73+
74+ ScriptContext getContext() {
75+ return context;
76+ }
77+
78+ private boolean isEmpty(String name) {
79+ return name.equals("");
80+ }
81+
82+ /**
83+ * Return the name of the class.
84+ */
85+ public String getClassName() {
86+ return "Global";
87+ }
88+
89+ /**
90+ * Returns the value of the named property or NOT_FOUND.
91+ *
92+ * If the property was created using defineProperty, the
93+ * appropriate getter method is called.
94+ *
95+ * @param name the name of the property
96+ * @param start the object in which the lookup began
97+ * @return the value of the property (may be null), or NOT_FOUND
98+ */
99+ public synchronized Object get(String name, Scriptable start) {
100+ if (isEmpty(name)) {
101+ if (indexedProps.containsKey(name)) {
102+ return indexedProps.get(name);
103+ } else {
104+ return NOT_FOUND;
105+ }
106+ } else {
107+ synchronized (context) {
108+ int scope = context.getAttributesScope(name);
109+ if (scope != -1) {
110+ Object value = context.getAttribute(name, scope);
111+ return Context.javaToJS(value, this);
112+ } else {
113+ return NOT_FOUND;
114+ }
115+ }
116+ }
117+ }
118+
119+ /**
120+ * Returns the value of the indexed property or NOT_FOUND.
121+ *
122+ * @param index the numeric index for the property
123+ * @param start the object in which the lookup began
124+ * @return the value of the property (may be null), or NOT_FOUND
125+ */
126+ public synchronized Object get(int index, Scriptable start) {
127+ Integer key = new Integer(index);
128+ if (indexedProps.containsKey(index)) {
129+ return indexedProps.get(key);
130+ } else {
131+ return NOT_FOUND;
132+ }
133+ }
134+
135+ /**
136+ * Returns true if the named property is defined.
137+ *
138+ * @param name the name of the property
139+ * @param start the object in which the lookup began
140+ * @return true if and only if the property was found in the object
141+ */
142+ public synchronized boolean has(String name, Scriptable start) {
143+ if (isEmpty(name)) {
144+ return indexedProps.containsKey(name);
145+ } else {
146+ synchronized (context) {
147+ return context.getAttributesScope(name) != -1;
148+ }
149+ }
150+ }
151+
152+ /**
153+ * Returns true if the property index is defined.
154+ *
155+ * @param index the numeric index for the property
156+ * @param start the object in which the lookup began
157+ * @return true if and only if the property was found in the object
158+ */
159+ public synchronized boolean has(int index, Scriptable start) {
160+ Integer key = new Integer(index);
161+ return indexedProps.containsKey(key);
162+ }
163+
164+ /**
165+ * Sets the value of the named property, creating it if need be.
166+ *
167+ * @param name the name of the property
168+ * @param start the object whose property is being set
169+ * @param value value to set the property to
170+ */
171+ public void put(String name, Scriptable start, Object value) {
172+ if (start == this) {
173+ synchronized (this) {
174+ if (isEmpty(name)) {
175+ indexedProps.put(name, value);
176+ } else {
177+ synchronized (context) {
178+ int scope = context.getAttributesScope(name);
179+ if (scope == -1) {
180+ scope = ScriptContext.ENGINE_SCOPE;
181+ }
182+ context.setAttribute(name, jsToJava(value), scope);
183+ }
184+ }
185+ }
186+ } else {
187+ start.put(name, start, value);
188+ }
189+ }
190+
191+ /**
192+ * Sets the value of the indexed property, creating it if need be.
193+ *
194+ * @param index the numeric index for the property
195+ * @param start the object whose property is being set
196+ * @param value value to set the property to
197+ */
198+ public void put(int index, Scriptable start, Object value) {
199+ if (start == this) {
200+ synchronized (this) {
201+ indexedProps.put(new Integer(index), value);
202+ }
203+ } else {
204+ start.put(index, start, value);
205+ }
206+ }
207+
208+ /**
209+ * Removes a named property from the object.
210+ *
211+ * If the property is not found, no action is taken.
212+ *
213+ * @param name the name of the property
214+ */
215+ public synchronized void delete(String name) {
216+ if (isEmpty(name)) {
217+ indexedProps.remove(name);
218+ } else {
219+ synchronized (context) {
220+ int scope = context.getAttributesScope(name);
221+ if (scope != -1) {
222+ context.removeAttribute(name, scope);
223+ }
224+ }
225+ }
226+ }
227+
228+ /**
229+ * Removes the indexed property from the object.
230+ *
231+ * If the property is not found, no action is taken.
232+ *
233+ * @param index the numeric index for the property
234+ */
235+ public void delete(int index) {
236+ indexedProps.remove(new Integer(index));
237+ }
238+
239+ /**
240+ * Get the prototype of the object.
241+ * @return the prototype
242+ */
243+ public Scriptable getPrototype() {
244+ return prototype;
245+ }
246+
247+ /**
248+ * Set the prototype of the object.
249+ * @param prototype the prototype to set
250+ */
251+ public void setPrototype(Scriptable prototype) {
252+ this.prototype = prototype;
253+ }
254+
255+ /**
256+ * Get the parent scope of the object.
257+ * @return the parent scope
258+ */
259+ public Scriptable getParentScope() {
260+ return parent;
261+ }
262+
263+ /**
264+ * Set the parent scope of the object.
265+ * @param parent the parent scope to set
266+ */
267+ public void setParentScope(Scriptable parent) {
268+ this.parent = parent;
269+ }
270+
271+ /**
272+ * Get an array of property ids.
273+ *
274+ * Not all property ids need be returned. Those properties
275+ * whose ids are not returned are considered non-enumerable.
276+ *
277+ * @return an array of Objects. Each entry in the array is either
278+ * a java.lang.String or a java.lang.Number
279+ */
280+ public synchronized Object[] getIds() {
281+ String[] keys = getAllKeys();
282+ int size = keys.length + indexedProps.size();
283+ Object[] res = new Object[size];
284+ System.arraycopy(keys, 0, res, 0, keys.length);
285+ int i = keys.length;
286+ // now add all indexed properties
287+ for (Object index : indexedProps.keySet()) {
288+ res[i++] = index;
289+ }
290+ return res;
291+ }
292+
293+ /**
294+ * Get the default value of the object with a given hint.
295+ * The hints are String.class for type String, Number.class for type
296+ * Number, Scriptable.class for type Object, and Boolean.class for
297+ * type Boolean. <p>
298+ *
299+ * A <code>hint</code> of null means "no hint".
300+ *
301+ * See ECMA 8.6.2.6.
302+ *
303+ * @param hint the type hint
304+ * @return the default value
305+ */
306+ public Object getDefaultValue(Class typeHint) {
307+ for (int i=0; i < 2; i++) {
308+ boolean tryToString;
309+ if (typeHint == ScriptRuntime.StringClass) {
310+ tryToString = (i == 0);
311+ } else {
312+ tryToString = (i == 1);
313+ }
314+
315+ String methodName;
316+ Object[] args;
317+ if (tryToString) {
318+ methodName = "toString";
319+ args = ScriptRuntime.emptyArgs;
320+ } else {
321+ methodName = "valueOf";
322+ args = new Object[1];
323+ String hint;
324+ if (typeHint == null) {
325+ hint = "undefined";
326+ } else if (typeHint == ScriptRuntime.StringClass) {
327+ hint = "string";
328+ } else if (typeHint == ScriptRuntime.ScriptableClass) {
329+ hint = "object";
330+ } else if (typeHint == ScriptRuntime.FunctionClass) {
331+ hint = "function";
332+ } else if (typeHint == ScriptRuntime.BooleanClass
333+ || typeHint == Boolean.TYPE)
334+ {
335+ hint = "boolean";
336+ } else if (typeHint == ScriptRuntime.NumberClass ||
337+ typeHint == ScriptRuntime.ByteClass ||
338+ typeHint == Byte.TYPE ||
339+ typeHint == ScriptRuntime.ShortClass ||
340+ typeHint == Short.TYPE ||
341+ typeHint == ScriptRuntime.IntegerClass ||
342+ typeHint == Integer.TYPE ||
343+ typeHint == ScriptRuntime.FloatClass ||
344+ typeHint == Float.TYPE ||
345+ typeHint == ScriptRuntime.DoubleClass ||
346+ typeHint == Double.TYPE)
347+ {
348+ hint = "number";
349+ } else {
350+ throw Context.reportRuntimeError(
351+ "Invalid JavaScript value of type " +
352+ typeHint.toString());
353+ }
354+ args[0] = hint;
355+ }
356+ Object v = ScriptableObject.getProperty(this, methodName);
357+ if (!(v instanceof Function))
358+ continue;
359+ Function fun = (Function) v;
360+ Context cx = RhinoScriptEngine.enterContext();
361+ try {
362+ v = fun.call(cx, fun.getParentScope(), this, args);
363+ } finally {
364+ cx.exit();
365+ }
366+ if (v != null) {
367+ if (!(v instanceof Scriptable)) {
368+ return v;
369+ }
370+ if (typeHint == ScriptRuntime.ScriptableClass
371+ || typeHint == ScriptRuntime.FunctionClass)
372+ {
373+ return v;
374+ }
375+ if (tryToString && v instanceof Wrapper) {
376+ // Let a wrapped java.lang.String pass for a primitive
377+ // string.
378+ Object u = ((Wrapper)v).unwrap();
379+ if (u instanceof String)
380+ return u;
381+ }
382+ }
383+ }
384+ // fall through to error
385+ String arg = (typeHint == null) ? "undefined" : typeHint.getName();
386+ throw Context.reportRuntimeError(
387+ "Cannot find default value for object " + arg);
388+ }
389+
390+ /**
391+ * Implements the instanceof operator.
392+ *
393+ * @param instance The value that appeared on the LHS of the instanceof
394+ * operator
395+ * @return true if "this" appears in value's prototype chain
396+ *
397+ */
398+ public boolean hasInstance(Scriptable instance) {
399+ // Default for JS objects (other than Function) is to do prototype
400+ // chasing.
401+ Scriptable proto = instance.getPrototype();
402+ while (proto != null) {
403+ if (proto.equals(this)) return true;
404+ proto = proto.getPrototype();
405+ }
406+ return false;
407+ }
408+
409+ private String[] getAllKeys() {
410+ ArrayList<String> list = new ArrayList<String>();
411+ synchronized (context) {
412+ for (int scope : context.getScopes()) {
413+ Bindings bindings = context.getBindings(scope);
414+ if (bindings != null) {
415+ list.ensureCapacity(bindings.size());
416+ for (String key : bindings.keySet()) {
417+ list.add(key);
418+ }
419+ }
420+ }
421+ }
422+ String[] res = new String[list.size()];
423+ list.toArray(res);
424+ return res;
425+ }
426+
427+ /**
428+ * We convert script values to the nearest Java value.
429+ * We unwrap wrapped Java objects so that access from
430+ * Bindings.get() would return "workable" value for Java.
431+ * But, at the same time, we need to make few special cases
432+ * and hence the following function is used.
433+ */
434+ private Object jsToJava(Object jsObj) {
435+ if (jsObj instanceof Wrapper) {
436+ Wrapper njb = (Wrapper) jsObj;
437+ /* importClass feature of ImporterTopLevel puts
438+ * NativeJavaClass in global scope. If we unwrap
439+ * it, importClass won't work.
440+ */
441+ if (njb instanceof NativeJavaClass) {
442+ return njb;
443+ }
444+
445+ /* script may use Java primitive wrapper type objects
446+ * (such as java.lang.Integer, java.lang.Boolean etc)
447+ * explicitly. If we unwrap, then these script objects
448+ * will become script primitive types. For example,
449+ *
450+ * var x = new java.lang.Double(3.0); print(typeof x);
451+ *
452+ * will print 'number'. We don't want that to happen.
453+ */
454+ Object obj = njb.unwrap();
455+ if (obj instanceof Number || obj instanceof String ||
456+ obj instanceof Boolean || obj instanceof Character) {
457+ // special type wrapped -- we just leave it as is.
458+ return njb;
459+ } else {
460+ // return unwrapped object for any other object.
461+ return obj;
462+ }
463+ } else { // not-a-Java-wrapper
464+ return jsObj;
465+ }
466+ }
467+}