25
26 package jdk.nashorn.api.scripting;
27
28 import java.security.AccessControlContext;
29 import java.security.AccessController;
30 import java.security.Permissions;
31 import java.security.PrivilegedAction;
32 import java.security.ProtectionDomain;
33 import java.util.AbstractMap;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Iterator;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.concurrent.Callable;
43 import javax.script.Bindings;
44 import jdk.nashorn.internal.runtime.Context;
45 import jdk.nashorn.internal.runtime.ScriptFunction;
46 import jdk.nashorn.internal.runtime.ScriptObject;
47 import jdk.nashorn.internal.runtime.ScriptRuntime;
48
49 /**
50 * Mirror object that wraps a given ScriptObject instance. User can
51 * access ScriptObject via the javax.script.Bindings interface or
52 * netscape.javascript.JSObject interface.
53 */
54 public final class ScriptObjectMirror extends JSObject implements Bindings {
55 private static AccessControlContext getContextAccCtxt() {
56 final Permissions perms = new Permissions();
57 perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT));
58 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
59 }
60
61 private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt();
62
63 private final ScriptObject sobj;
64 private final ScriptObject global;
65
66 @Override
67 public boolean equals(final Object other) {
68 if (other instanceof ScriptObjectMirror) {
69 return sobj.equals(((ScriptObjectMirror)other).sobj);
70 }
71
72 return false;
73 }
74
75 @Override
76 public int hashCode() {
77 return sobj.hashCode();
78 }
79
80 @Override
81 public String toString() {
82 return inGlobal(new Callable<String>() {
83 @Override
84 public String call() {
85 return ScriptRuntime.safeToString(sobj);
86 }
87 });
88 }
89
90 // JSObject methods
91 @Override
92 public Object call(final String functionName, final Object... args) {
93 final ScriptObject oldGlobal = Context.getGlobal();
94 final boolean globalChanged = (oldGlobal != global);
95
96 try {
97 if (globalChanged) {
98 Context.setGlobal(global);
99 }
100
101 final Object val = functionName == null? sobj : sobj.get(functionName);
102 if (val instanceof ScriptFunction) {
103 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
104 return wrap(ScriptRuntime.checkAndApply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global);
105 } else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
106 return ((ScriptObjectMirror)val).call(null, args);
107 }
108
109 throw new NoSuchMethodException("No such function " + ((functionName != null)? functionName : ""));
110 } catch (final RuntimeException | Error e) {
111 throw e;
112 } catch (final Throwable t) {
113 throw new RuntimeException(t);
114 } finally {
115 if (globalChanged) {
116 Context.setGlobal(oldGlobal);
117 }
118 }
119 }
120
121 @Override
122 public Object newObject(final String functionName, final Object... args) {
123 final ScriptObject oldGlobal = Context.getGlobal();
124 final boolean globalChanged = (oldGlobal != global);
125
126 try {
127 if (globalChanged) {
128 Context.setGlobal(global);
129 }
130
131 final Object val = functionName == null? sobj : sobj.get(functionName);
132 if (val instanceof ScriptFunction) {
133 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
134 return wrap(ScriptRuntime.checkAndConstruct((ScriptFunction)val, unwrapArray(modArgs, global)), global);
135 } else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
136 return ((ScriptObjectMirror)val).newObject(null, args);
137 }
138
139 throw new RuntimeException("not a constructor " + ((functionName != null)? functionName : ""));
140 } catch (final RuntimeException | Error e) {
141 throw e;
142 } catch (final Throwable t) {
143 throw new RuntimeException(t);
144 } finally {
145 if (globalChanged) {
146 Context.setGlobal(oldGlobal);
147 }
148 }
149 }
150
151 @Override
152 public Object eval(final String s) {
153 return inGlobal(new Callable<Object>() {
154 @Override
180 @Override public Object call() {
181 return wrap(sobj.get(index), global);
182 }
183 });
184 }
185
186 @Override
187 public void removeMember(final String name) {
188 remove(name);
189 }
190
191 @Override
192 public void setMember(final String name, final Object value) {
193 put(name, value);
194 }
195
196 @Override
197 public void setSlot(final int index, final Object value) {
198 inGlobal(new Callable<Void>() {
199 @Override public Void call() {
200 sobj.set(index, unwrap(value, global), global.isStrictContext());
201 return null;
202 }
203 });
204 }
205
206 // javax.script.Bindings methods
207
208 @Override
209 public void clear() {
210 inGlobal(new Callable<Object>() {
211 @Override public Object call() {
212 sobj.clear();
213 return null;
214 }
215 });
216 }
217
218 @Override
219 public boolean containsKey(final Object key) {
220 return inGlobal(new Callable<Boolean>() {
221 @Override public Boolean call() {
222 return sobj.containsKey(unwrap(key, global));
223 }
224 });
225 }
226
227 @Override
228 public boolean containsValue(final Object value) {
229 return inGlobal(new Callable<Boolean>() {
230 @Override public Boolean call() {
231 return sobj.containsValue(unwrap(value, global));
232 }
275 @Override public Set<String> call() {
276 final Iterator<String> iter = sobj.propertyIterator();
277 final Set<String> keySet = new LinkedHashSet<>();
278
279 while (iter.hasNext()) {
280 keySet.add(iter.next());
281 }
282
283 return Collections.unmodifiableSet(keySet);
284 }
285 });
286 }
287
288 @Override
289 public Object put(final String key, final Object value) {
290 final ScriptObject oldGlobal = Context.getGlobal();
291 final boolean globalChanged = (oldGlobal != global);
292 return inGlobal(new Callable<Object>() {
293 @Override public Object call() {
294 final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
295 return translateUndefined(wrap(sobj.put(key, unwrap(modValue, global)), global));
296 }
297 });
298 }
299
300 @Override
301 public void putAll(final Map<? extends String, ? extends Object> map) {
302 final ScriptObject oldGlobal = Context.getGlobal();
303 final boolean globalChanged = (oldGlobal != global);
304 inGlobal(new Callable<Object>() {
305 @Override public Object call() {
306 final boolean strict = global.isStrictContext();
307 for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
308 final Object value = entry.getValue();
309 final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
310 sobj.set(entry.getKey(), unwrap(modValue, global), strict);
311 }
312 return null;
313 }
314 });
315 }
316
317 @Override
318 public Object remove(final Object key) {
319 return inGlobal(new Callable<Object>() {
320 @Override public Object call() {
321 return wrap(sobj.remove(unwrap(key, global)), global);
322 }
323 });
324 }
325
326 /**
327 * Delete a property from this object.
328 *
329 * @param key the property to be deleted
330 *
331 * @return if the delete was successful or not
332 */
333 public boolean delete(final Object key) {
334 return inGlobal(new Callable<Boolean>() {
335 @Override public Boolean call() {
336 return sobj.delete(unwrap(key, global));
337 }
338 });
339 }
340
341 @Override
342 public int size() {
343 return inGlobal(new Callable<Integer>() {
344 @Override public Integer call() {
345 return sobj.size();
346 }
347 });
348 }
349
350 @Override
351 public Collection<Object> values() {
352 return inGlobal(new Callable<Collection<Object>>() {
353 @Override public Collection<Object> call() {
354 final List<Object> values = new ArrayList<>(size());
355 final Iterator<Object> iter = sobj.valueIterator();
356
620 * @return unwrapped array
621 */
622 public static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) {
623 if (args == null || args.length == 0) {
624 return args;
625 }
626
627 final Object[] newArgs = new Object[args.length];
628 int index = 0;
629 for (final Object obj : args) {
630 newArgs[index] = unwrap(obj, homeGlobal);
631 index++;
632 }
633 return newArgs;
634 }
635
636 // package-privates below this.
637
638 ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) {
639 assert sobj != null : "ScriptObjectMirror on null!";
640 assert global != null : "null global for ScriptObjectMirror!";
641
642 this.sobj = sobj;
643 this.global = global;
644 }
645
646 // accessors for script engine
647 ScriptObject getScriptObject() {
648 return sobj;
649 }
650
651 ScriptObject getHomeGlobal() {
652 return global;
653 }
654
655 static Object translateUndefined(Object obj) {
656 return (obj == ScriptRuntime.UNDEFINED)? null : obj;
657 }
658
659 // internals only below this.
660 private <V> V inGlobal(final Callable<V> callable) {
661 final ScriptObject oldGlobal = Context.getGlobal();
662 final boolean globalChanged = (oldGlobal != global);
663 if (globalChanged) {
|
25
26 package jdk.nashorn.api.scripting;
27
28 import java.security.AccessControlContext;
29 import java.security.AccessController;
30 import java.security.Permissions;
31 import java.security.PrivilegedAction;
32 import java.security.ProtectionDomain;
33 import java.util.AbstractMap;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.Iterator;
38 import java.util.LinkedHashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Set;
42 import java.util.concurrent.Callable;
43 import javax.script.Bindings;
44 import jdk.nashorn.internal.runtime.Context;
45 import jdk.nashorn.internal.runtime.GlobalObject;
46 import jdk.nashorn.internal.runtime.ScriptFunction;
47 import jdk.nashorn.internal.runtime.ScriptObject;
48 import jdk.nashorn.internal.runtime.ScriptRuntime;
49
50 /**
51 * Mirror object that wraps a given ScriptObject instance. User can
52 * access ScriptObject via the javax.script.Bindings interface or
53 * netscape.javascript.JSObject interface.
54 */
55 public final class ScriptObjectMirror extends JSObject implements Bindings {
56 private static AccessControlContext getContextAccCtxt() {
57 final Permissions perms = new Permissions();
58 perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT));
59 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
60 }
61
62 private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt();
63
64 private final ScriptObject sobj;
65 private final ScriptObject global;
66 private final boolean strict;
67
68 @Override
69 public boolean equals(final Object other) {
70 if (other instanceof ScriptObjectMirror) {
71 return sobj.equals(((ScriptObjectMirror)other).sobj);
72 }
73
74 return false;
75 }
76
77 @Override
78 public int hashCode() {
79 return sobj.hashCode();
80 }
81
82 @Override
83 public String toString() {
84 return inGlobal(new Callable<String>() {
85 @Override
86 public String call() {
87 return ScriptRuntime.safeToString(sobj);
88 }
89 });
90 }
91
92 // JSObject methods
93 @Override
94 public Object call(final String functionName, final Object... args) {
95 final ScriptObject oldGlobal = Context.getGlobal();
96 final boolean globalChanged = (oldGlobal != global);
97
98 try {
99 if (globalChanged) {
100 Context.setGlobal(global);
101 }
102
103 final Object val = functionName == null? sobj : sobj.get(functionName);
104 if (val instanceof ScriptFunction) {
105 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
106 return wrap(ScriptRuntime.apply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global);
107 } else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
108 return ((ScriptObjectMirror)val).call(null, args);
109 }
110
111 throw new NoSuchMethodException("No such function " + ((functionName != null)? functionName : ""));
112 } catch (final RuntimeException | Error e) {
113 throw e;
114 } catch (final Throwable t) {
115 throw new RuntimeException(t);
116 } finally {
117 if (globalChanged) {
118 Context.setGlobal(oldGlobal);
119 }
120 }
121 }
122
123 @Override
124 public Object newObject(final String functionName, final Object... args) {
125 final ScriptObject oldGlobal = Context.getGlobal();
126 final boolean globalChanged = (oldGlobal != global);
127
128 try {
129 if (globalChanged) {
130 Context.setGlobal(global);
131 }
132
133 final Object val = functionName == null? sobj : sobj.get(functionName);
134 if (val instanceof ScriptFunction) {
135 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
136 return wrap(ScriptRuntime.construct((ScriptFunction)val, unwrapArray(modArgs, global)), global);
137 } else if (val instanceof ScriptObjectMirror && ((ScriptObjectMirror)val).isFunction()) {
138 return ((ScriptObjectMirror)val).newObject(null, args);
139 }
140
141 throw new RuntimeException("not a constructor " + ((functionName != null)? functionName : ""));
142 } catch (final RuntimeException | Error e) {
143 throw e;
144 } catch (final Throwable t) {
145 throw new RuntimeException(t);
146 } finally {
147 if (globalChanged) {
148 Context.setGlobal(oldGlobal);
149 }
150 }
151 }
152
153 @Override
154 public Object eval(final String s) {
155 return inGlobal(new Callable<Object>() {
156 @Override
182 @Override public Object call() {
183 return wrap(sobj.get(index), global);
184 }
185 });
186 }
187
188 @Override
189 public void removeMember(final String name) {
190 remove(name);
191 }
192
193 @Override
194 public void setMember(final String name, final Object value) {
195 put(name, value);
196 }
197
198 @Override
199 public void setSlot(final int index, final Object value) {
200 inGlobal(new Callable<Void>() {
201 @Override public Void call() {
202 sobj.set(index, unwrap(value, global), strict);
203 return null;
204 }
205 });
206 }
207
208 // javax.script.Bindings methods
209
210 @Override
211 public void clear() {
212 inGlobal(new Callable<Object>() {
213 @Override public Object call() {
214 sobj.clear(strict);
215 return null;
216 }
217 });
218 }
219
220 @Override
221 public boolean containsKey(final Object key) {
222 return inGlobal(new Callable<Boolean>() {
223 @Override public Boolean call() {
224 return sobj.containsKey(unwrap(key, global));
225 }
226 });
227 }
228
229 @Override
230 public boolean containsValue(final Object value) {
231 return inGlobal(new Callable<Boolean>() {
232 @Override public Boolean call() {
233 return sobj.containsValue(unwrap(value, global));
234 }
277 @Override public Set<String> call() {
278 final Iterator<String> iter = sobj.propertyIterator();
279 final Set<String> keySet = new LinkedHashSet<>();
280
281 while (iter.hasNext()) {
282 keySet.add(iter.next());
283 }
284
285 return Collections.unmodifiableSet(keySet);
286 }
287 });
288 }
289
290 @Override
291 public Object put(final String key, final Object value) {
292 final ScriptObject oldGlobal = Context.getGlobal();
293 final boolean globalChanged = (oldGlobal != global);
294 return inGlobal(new Callable<Object>() {
295 @Override public Object call() {
296 final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
297 return translateUndefined(wrap(sobj.put(key, unwrap(modValue, global), strict), global));
298 }
299 });
300 }
301
302 @Override
303 public void putAll(final Map<? extends String, ? extends Object> map) {
304 final ScriptObject oldGlobal = Context.getGlobal();
305 final boolean globalChanged = (oldGlobal != global);
306 inGlobal(new Callable<Object>() {
307 @Override public Object call() {
308 for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
309 final Object value = entry.getValue();
310 final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
311 sobj.set(entry.getKey(), unwrap(modValue, global), strict);
312 }
313 return null;
314 }
315 });
316 }
317
318 @Override
319 public Object remove(final Object key) {
320 return inGlobal(new Callable<Object>() {
321 @Override public Object call() {
322 return wrap(sobj.remove(unwrap(key, global), strict), global);
323 }
324 });
325 }
326
327 /**
328 * Delete a property from this object.
329 *
330 * @param key the property to be deleted
331 *
332 * @return if the delete was successful or not
333 */
334 public boolean delete(final Object key) {
335 return inGlobal(new Callable<Boolean>() {
336 @Override public Boolean call() {
337 return sobj.delete(unwrap(key, global), strict);
338 }
339 });
340 }
341
342 @Override
343 public int size() {
344 return inGlobal(new Callable<Integer>() {
345 @Override public Integer call() {
346 return sobj.size();
347 }
348 });
349 }
350
351 @Override
352 public Collection<Object> values() {
353 return inGlobal(new Callable<Collection<Object>>() {
354 @Override public Collection<Object> call() {
355 final List<Object> values = new ArrayList<>(size());
356 final Iterator<Object> iter = sobj.valueIterator();
357
621 * @return unwrapped array
622 */
623 public static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) {
624 if (args == null || args.length == 0) {
625 return args;
626 }
627
628 final Object[] newArgs = new Object[args.length];
629 int index = 0;
630 for (final Object obj : args) {
631 newArgs[index] = unwrap(obj, homeGlobal);
632 index++;
633 }
634 return newArgs;
635 }
636
637 // package-privates below this.
638
639 ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) {
640 assert sobj != null : "ScriptObjectMirror on null!";
641 assert global instanceof GlobalObject : "global is not a GlobalObject";
642
643 this.sobj = sobj;
644 this.global = global;
645 this.strict = ((GlobalObject)global).isStrictContext();
646 }
647
648 // accessors for script engine
649 ScriptObject getScriptObject() {
650 return sobj;
651 }
652
653 ScriptObject getHomeGlobal() {
654 return global;
655 }
656
657 static Object translateUndefined(Object obj) {
658 return (obj == ScriptRuntime.UNDEFINED)? null : obj;
659 }
660
661 // internals only below this.
662 private <V> V inGlobal(final Callable<V> callable) {
663 final ScriptObject oldGlobal = Context.getGlobal();
664 final boolean globalChanged = (oldGlobal != global);
665 if (globalChanged) {
|