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
26package com.sun.script.javascript;
27import sun.org.mozilla.javascript.internal.*;
28import javax.script.*;
29import 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
40final 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}