Wed, 16 Jan 2013 17:58:51 +0530
8006412: Improve toString method of ScriptObjectMirror class
Reviewed-by: jlaskey, lagergren
1 /*
2 * Copyright (c) 2010, 2013, 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 */
26 package jdk.nashorn.api.scripting;
28 import java.util.AbstractMap;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.concurrent.Callable;
38 import jdk.nashorn.internal.runtime.Context;
39 import jdk.nashorn.internal.runtime.ScriptFunction;
40 import jdk.nashorn.internal.runtime.ScriptObject;
41 import jdk.nashorn.internal.runtime.ScriptRuntime;
42 import netscape.javascript.JSObject;
44 /**
45 * Mirror object that wraps a given ScriptObject instance. User can
46 * access ScriptObject via the java.util.Map interface.
47 */
48 final class ScriptObjectMirror extends JSObject implements Map<Object, Object> {
49 private final ScriptObject sobj;
50 private final ScriptObject global;
52 ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) {
53 this.sobj = sobj;
54 this.global = global;
55 }
57 @Override
58 public boolean equals(final Object other) {
59 if (other instanceof ScriptObjectMirror) {
60 return sobj.equals(((ScriptObjectMirror)other).sobj);
61 }
63 return false;
64 }
66 @Override
67 public int hashCode() {
68 return sobj.hashCode();
69 }
71 @Override
72 public String toString() {
73 return inGlobal(new Callable<String>() {
74 @Override
75 public String call() {
76 return ScriptRuntime.safeToString(sobj);
77 }
78 });
79 }
81 private <V> V inGlobal(final Callable<V> callable) {
82 final ScriptObject oldGlobal = Context.getGlobal();
83 final boolean globalChanged = (oldGlobal != global);
84 if (globalChanged) {
85 NashornScriptEngine.setNashornGlobal(global);
86 }
87 try {
88 return callable.call();
89 } catch (final RuntimeException e) {
90 throw e;
91 } catch (final Exception e) {
92 throw new AssertionError("Cannot happen", e);
93 } finally {
94 if (globalChanged) {
95 NashornScriptEngine.setNashornGlobal(oldGlobal);
96 }
97 }
98 }
100 // JSObject methods
101 @Override
102 public Object call(final String methodName, final Object args[]) {
103 final Object val = sobj.get(methodName);
104 final ScriptObject oldGlobal = Context.getGlobal();
105 final boolean globalChanged = (oldGlobal != global);
107 if (val instanceof ScriptFunction) {
108 final Object[] modifiedArgs = unwrapArray(args, global);
109 if (modifiedArgs != null) {
110 for (int i = 0; i < modifiedArgs.length; i++) {
111 final Object arg = modifiedArgs[i];
112 if (arg instanceof ScriptObject) {
113 modifiedArgs[i] = wrap(arg, oldGlobal);
114 }
115 }
116 }
118 try {
119 if (globalChanged) {
120 NashornScriptEngine.setNashornGlobal(global);
121 }
122 return wrap(((ScriptFunction)val).invoke(sobj, modifiedArgs), global);
123 } catch (final RuntimeException | Error e) {
124 throw e;
125 } catch (final Throwable t) {
126 throw new RuntimeException(t);
127 } finally {
128 if (globalChanged) {
129 NashornScriptEngine.setNashornGlobal(oldGlobal);
130 }
131 }
132 }
134 throw new RuntimeException("No such method: " + methodName);
135 }
137 @Override
138 public Object eval(final String s) {
139 return inGlobal(new Callable<Object>() {
140 @Override
141 public Object call() {
142 return wrap(global.getContext().eval(global, s, null, null, false), global);
143 }
144 });
145 }
147 @Override
148 public Object getMember(final String name) {
149 return get(name);
150 }
152 @Override
153 public Object getSlot(final int index) {
154 return get(Integer.valueOf(index));
155 }
157 @Override
158 public void removeMember(final String name) {
159 remove(name);
160 }
162 @Override
163 public void setMember(final String name, final Object value) {
164 put(name, wrap(value, Context.getGlobal()));
165 }
167 @Override
168 public void setSlot(final int index, final Object value) {
169 put(Integer.valueOf(index), wrap(value, Context.getGlobal()));
170 }
172 @Override
173 public void clear() {
174 inGlobal(new Callable<Object>() {
175 @Override public Object call() {
176 sobj.clear();
177 return null;
178 }});
179 }
181 @Override
182 public boolean containsKey(final Object key) {
183 return inGlobal(new Callable<Boolean>() {
184 @Override public Boolean call() {
185 return sobj.containsKey(unwrap(key, global));
186 }});
187 }
189 @Override
190 public boolean containsValue(final Object value) {
191 return inGlobal(new Callable<Boolean>() {
192 @Override public Boolean call() {
193 return sobj.containsValue(unwrap(value, global));
194 }});
195 }
197 @Override
198 public Set<Map.Entry<Object, Object>> entrySet() {
199 return inGlobal(new Callable<Set<Map.Entry<Object, Object>>>() {
200 @Override public Set<Map.Entry<Object, Object>> call() {
201 final Iterator<String> iter = sobj.propertyIterator();
202 final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
204 while (iter.hasNext()) {
205 final Object key = wrap(iter.next(), global);
206 final Object value = wrap(sobj.get(key), global);
207 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
208 }
210 return Collections.unmodifiableSet(entries);
211 }
212 });
213 }
215 @Override
216 public Object get(final Object key) {
217 return inGlobal(new Callable<Object>() { @Override public Object call() {
218 return wrap(sobj.get(key), global);
219 }});
220 }
222 @Override
223 public boolean isEmpty() {
224 return inGlobal(new Callable<Boolean>() { @Override public Boolean call() {
225 return sobj.isEmpty();
226 }});
227 }
229 @Override
230 public Set<Object> keySet() {
231 return inGlobal(new Callable<Set<Object>>() { @Override public Set<Object> call() {
232 final Iterator<String> iter = sobj.propertyIterator();
233 final Set<Object> keySet = new HashSet<>();
235 while (iter.hasNext()) {
236 keySet.add(wrap(iter.next(), global));
237 }
239 return Collections.unmodifiableSet(keySet);
240 }});
241 }
243 @Override
244 public Object put(final Object key, final Object value) {
245 return inGlobal(new Callable<Object>() {
246 @Override public Object call() {
247 return sobj.put(unwrap(key, global), unwrap(value, global));
248 }});
249 }
251 @Override
252 public void putAll(final Map<?, ?> map) {
253 final boolean strict = sobj.getContext()._strict;
254 inGlobal(new Callable<Object>() { @Override public Object call() {
255 for (final Map.Entry<?, ?> entry : map.entrySet()) {
256 sobj.set(unwrap(entry.getKey(), global), unwrap(entry.getValue(), global), strict);
257 }
258 return null;
259 }});
260 }
262 @Override
263 public Object remove(final Object key) {
264 return inGlobal(new Callable<Object>() {
265 @Override public Object call() {
266 return wrap(sobj.remove(unwrap(key, global)), global);
267 }
268 });
269 }
271 @Override
272 public int size() {
273 return inGlobal(new Callable<Integer>() {
274 @Override public Integer call() {
275 return sobj.size();
276 }
277 });
278 }
280 @Override
281 public Collection<Object> values() {
282 return inGlobal(new Callable<Collection<Object>>() { @Override public Collection<Object> call() {
283 final List<Object> values = new ArrayList<>(size());
284 final Iterator<Object> iter = sobj.valueIterator();
286 while (iter.hasNext()) {
287 values.add(wrap(iter.next(), global));
288 }
290 return Collections.unmodifiableList(values);
291 }});
292 }
294 static Object wrap(final Object obj, final ScriptObject homeGlobal) {
295 return (obj instanceof ScriptObject) ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj;
296 }
298 static Object unwrap(final Object obj, final ScriptObject homeGlobal) {
299 if (obj instanceof ScriptObjectMirror) {
300 final ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
301 return (mirror.global == homeGlobal)? mirror.sobj : obj;
302 }
304 return obj;
305 }
307 static Object[] wrapArray(final Object[] args, final ScriptObject homeGlobal) {
308 if (args == null || args.length == 0) {
309 return args;
310 }
312 final Object[] newArgs = new Object[args.length];
313 int index = 0;
314 for (final Object obj : args) {
315 newArgs[index] = wrap(obj, homeGlobal);
316 index++;
317 }
318 return newArgs;
319 }
321 static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) {
322 if (args == null || args.length == 0) {
323 return args;
324 }
326 final Object[] newArgs = new Object[args.length];
327 int index = 0;
328 for (final Object obj : args) {
329 newArgs[index] = unwrap(obj, homeGlobal);
330 index++;
331 }
332 return newArgs;
333 }
334 }