src/jdk/nashorn/api/scripting/ScriptObjectMirror.java

Tue, 02 Jun 2015 10:55:17 +0200

author
attila
date
Tue, 02 Jun 2015 10:55:17 +0200
changeset 1394
07f32a26bc1e
parent 1250
9ee1fc3f6136
child 1399
22640d19073c
permissions
-rw-r--r--

8066773: JSON-friendly wrapper for objects
Reviewed-by: jlaskey, lagergren, sundar

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.api.scripting;
jlaskey@3 27
sundar@763 28 import java.nio.ByteBuffer;
sundar@492 29 import java.security.AccessControlContext;
sundar@41 30 import java.security.AccessController;
sundar@492 31 import java.security.Permissions;
sundar@41 32 import java.security.PrivilegedAction;
sundar@492 33 import java.security.ProtectionDomain;
jlaskey@3 34 import java.util.AbstractMap;
jlaskey@3 35 import java.util.ArrayList;
jlaskey@3 36 import java.util.Collection;
jlaskey@3 37 import java.util.Collections;
sundar@492 38 import java.util.Iterator;
sundar@321 39 import java.util.LinkedHashSet;
jlaskey@3 40 import java.util.List;
jlaskey@3 41 import java.util.Map;
sundar@1231 42 import java.util.Objects;
jlaskey@3 43 import java.util.Set;
jlaskey@3 44 import java.util.concurrent.Callable;
sundar@38 45 import javax.script.Bindings;
sundar@771 46 import jdk.nashorn.internal.objects.Global;
attila@663 47 import jdk.nashorn.internal.runtime.ConsString;
jlaskey@3 48 import jdk.nashorn.internal.runtime.Context;
attila@1250 49 import jdk.nashorn.internal.runtime.ECMAException;
attila@1394 50 import jdk.nashorn.internal.runtime.JSONListAdapter;
attila@644 51 import jdk.nashorn.internal.runtime.JSType;
jlaskey@3 52 import jdk.nashorn.internal.runtime.ScriptFunction;
jlaskey@3 53 import jdk.nashorn.internal.runtime.ScriptObject;
sundar@33 54 import jdk.nashorn.internal.runtime.ScriptRuntime;
attila@962 55 import jdk.nashorn.internal.runtime.arrays.ArrayData;
hannesw@1020 56 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
jlaskey@3 57
jlaskey@3 58 /**
sundar@546 59 * Mirror object that wraps a given Nashorn Script object.
sundar@1212 60 *
sundar@1212 61 * @since 1.8u40
jlaskey@3 62 */
sundar@1212 63 @jdk.Exported
sundar@648 64 public final class ScriptObjectMirror extends AbstractJSObject implements Bindings {
sundar@492 65 private static AccessControlContext getContextAccCtxt() {
sundar@492 66 final Permissions perms = new Permissions();
sundar@492 67 perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT));
sundar@492 68 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
sundar@492 69 }
sundar@492 70
sundar@492 71 private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt();
sundar@492 72
jlaskey@3 73 private final ScriptObject sobj;
sundar@771 74 private final Global global;
sundar@541 75 private final boolean strict;
attila@1394 76 private final boolean jsonCompatible;
jlaskey@3 77
jlaskey@3 78 @Override
jlaskey@3 79 public boolean equals(final Object other) {
jlaskey@3 80 if (other instanceof ScriptObjectMirror) {
jlaskey@3 81 return sobj.equals(((ScriptObjectMirror)other).sobj);
jlaskey@3 82 }
jlaskey@3 83
jlaskey@3 84 return false;
jlaskey@3 85 }
jlaskey@3 86
jlaskey@3 87 @Override
jlaskey@3 88 public int hashCode() {
jlaskey@3 89 return sobj.hashCode();
jlaskey@3 90 }
jlaskey@3 91
sundar@33 92 @Override
sundar@33 93 public String toString() {
sundar@33 94 return inGlobal(new Callable<String>() {
sundar@33 95 @Override
sundar@33 96 public String call() {
sundar@33 97 return ScriptRuntime.safeToString(sobj);
sundar@33 98 }
sundar@33 99 });
sundar@33 100 }
sundar@33 101
jlaskey@3 102 // JSObject methods
sundar@546 103
jlaskey@3 104 @Override
sundar@546 105 public Object call(final Object thiz, final Object... args) {
sundar@771 106 final Global oldGlobal = Context.getGlobal();
jlaskey@3 107 final boolean globalChanged = (oldGlobal != global);
jlaskey@3 108
sundar@88 109 try {
sundar@88 110 if (globalChanged) {
sundar@456 111 Context.setGlobal(global);
jlaskey@3 112 }
jlaskey@3 113
sundar@546 114 if (sobj instanceof ScriptFunction) {
attila@1394 115 final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args;
attila@1394 116 final Object self = globalChanged? wrapLikeMe(thiz, oldGlobal) : thiz;
attila@1394 117 return wrapLikeMe(ScriptRuntime.apply((ScriptFunction)sobj, unwrap(self, global), unwrapArray(modArgs, global)));
jlaskey@3 118 }
jlaskey@3 119
sundar@546 120 throw new RuntimeException("not a function: " + toString());
sundar@748 121 } catch (final NashornException ne) {
sundar@748 122 throw ne.initEcmaError(global);
sundar@88 123 } catch (final RuntimeException | Error e) {
sundar@88 124 throw e;
sundar@88 125 } catch (final Throwable t) {
sundar@88 126 throw new RuntimeException(t);
sundar@88 127 } finally {
sundar@88 128 if (globalChanged) {
sundar@456 129 Context.setGlobal(oldGlobal);
sundar@88 130 }
sundar@88 131 }
jlaskey@3 132 }
jlaskey@3 133
jlaskey@3 134 @Override
sundar@546 135 public Object newObject(final Object... args) {
sundar@771 136 final Global oldGlobal = Context.getGlobal();
sundar@376 137 final boolean globalChanged = (oldGlobal != global);
sundar@376 138
sundar@376 139 try {
sundar@376 140 if (globalChanged) {
sundar@456 141 Context.setGlobal(global);
sundar@376 142 }
sundar@376 143
sundar@546 144 if (sobj instanceof ScriptFunction) {
attila@1394 145 final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args;
attila@1394 146 return wrapLikeMe(ScriptRuntime.construct((ScriptFunction)sobj, unwrapArray(modArgs, global)));
sundar@376 147 }
sundar@376 148
sundar@546 149 throw new RuntimeException("not a constructor: " + toString());
sundar@748 150 } catch (final NashornException ne) {
sundar@748 151 throw ne.initEcmaError(global);
sundar@376 152 } catch (final RuntimeException | Error e) {
sundar@376 153 throw e;
sundar@376 154 } catch (final Throwable t) {
sundar@376 155 throw new RuntimeException(t);
sundar@376 156 } finally {
sundar@376 157 if (globalChanged) {
sundar@456 158 Context.setGlobal(oldGlobal);
sundar@376 159 }
sundar@376 160 }
sundar@376 161 }
sundar@376 162
sundar@376 163 @Override
jlaskey@3 164 public Object eval(final String s) {
jlaskey@3 165 return inGlobal(new Callable<Object>() {
jlaskey@3 166 @Override
jlaskey@3 167 public Object call() {
sundar@41 168 final Context context = AccessController.doPrivileged(
sundar@41 169 new PrivilegedAction<Context>() {
sundar@41 170 @Override
sundar@41 171 public Context run() {
sundar@41 172 return Context.getContext();
sundar@41 173 }
sundar@492 174 }, GET_CONTEXT_ACC_CTXT);
attila@1394 175 return wrapLikeMe(context.eval(global, s, sobj, null, false));
jlaskey@3 176 }
jlaskey@3 177 });
jlaskey@3 178 }
jlaskey@3 179
attila@963 180 /**
attila@963 181 * Call member function
attila@963 182 * @param functionName function name
attila@963 183 * @param args arguments
attila@963 184 * @return return value of function
attila@963 185 */
sundar@546 186 public Object callMember(final String functionName, final Object... args) {
sundar@1231 187 Objects.requireNonNull(functionName);
sundar@771 188 final Global oldGlobal = Context.getGlobal();
sundar@546 189 final boolean globalChanged = (oldGlobal != global);
sundar@546 190
sundar@546 191 try {
sundar@546 192 if (globalChanged) {
sundar@546 193 Context.setGlobal(global);
sundar@546 194 }
sundar@546 195
sundar@546 196 final Object val = sobj.get(functionName);
sundar@546 197 if (val instanceof ScriptFunction) {
attila@1394 198 final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args;
attila@1394 199 return wrapLikeMe(ScriptRuntime.apply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)));
sundar@546 200 } else if (val instanceof JSObject && ((JSObject)val).isFunction()) {
sundar@546 201 return ((JSObject)val).call(sobj, args);
sundar@546 202 }
sundar@546 203
sundar@546 204 throw new NoSuchMethodException("No such function " + functionName);
sundar@748 205 } catch (final NashornException ne) {
sundar@748 206 throw ne.initEcmaError(global);
sundar@546 207 } catch (final RuntimeException | Error e) {
sundar@546 208 throw e;
sundar@546 209 } catch (final Throwable t) {
sundar@546 210 throw new RuntimeException(t);
sundar@546 211 } finally {
sundar@546 212 if (globalChanged) {
sundar@546 213 Context.setGlobal(oldGlobal);
sundar@546 214 }
sundar@546 215 }
sundar@546 216 }
sundar@546 217
sundar@546 218 @Override
jlaskey@3 219 public Object getMember(final String name) {
sundar@1231 220 Objects.requireNonNull(name);
sundar@38 221 return inGlobal(new Callable<Object>() {
sundar@38 222 @Override public Object call() {
attila@1394 223 return wrapLikeMe(sobj.get(name));
sundar@38 224 }
sundar@38 225 });
jlaskey@3 226 }
jlaskey@3 227
jlaskey@3 228 @Override
jlaskey@3 229 public Object getSlot(final int index) {
sundar@38 230 return inGlobal(new Callable<Object>() {
sundar@38 231 @Override public Object call() {
attila@1394 232 return wrapLikeMe(sobj.get(index));
sundar@38 233 }
sundar@38 234 });
jlaskey@3 235 }
jlaskey@3 236
jlaskey@3 237 @Override
sundar@546 238 public boolean hasMember(final String name) {
sundar@1231 239 Objects.requireNonNull(name);
sundar@546 240 return inGlobal(new Callable<Boolean>() {
sundar@546 241 @Override public Boolean call() {
sundar@546 242 return sobj.has(name);
sundar@546 243 }
sundar@546 244 });
sundar@546 245 }
sundar@546 246
sundar@546 247 @Override
sundar@546 248 public boolean hasSlot(final int slot) {
sundar@546 249 return inGlobal(new Callable<Boolean>() {
sundar@546 250 @Override public Boolean call() {
sundar@546 251 return sobj.has(slot);
sundar@546 252 }
sundar@546 253 });
sundar@546 254 }
sundar@546 255
sundar@546 256 @Override
jlaskey@3 257 public void removeMember(final String name) {
sundar@1231 258 Objects.requireNonNull(name);
jlaskey@3 259 remove(name);
jlaskey@3 260 }
jlaskey@3 261
jlaskey@3 262 @Override
jlaskey@3 263 public void setMember(final String name, final Object value) {
sundar@1231 264 Objects.requireNonNull(name);
sundar@88 265 put(name, value);
jlaskey@3 266 }
jlaskey@3 267
jlaskey@3 268 @Override
jlaskey@3 269 public void setSlot(final int index, final Object value) {
sundar@38 270 inGlobal(new Callable<Void>() {
sundar@38 271 @Override public Void call() {
hannesw@1020 272 sobj.set(index, unwrap(value, global), getCallSiteFlags());
sundar@38 273 return null;
sundar@38 274 }
sundar@38 275 });
jlaskey@3 276 }
jlaskey@3 277
sundar@763 278 /**
sundar@763 279 * Nashorn extension: setIndexedPropertiesToExternalArrayData.
sundar@763 280 * set indexed properties be exposed from a given nio ByteBuffer.
sundar@763 281 *
sundar@763 282 * @param buf external buffer - should be a nio ByteBuffer
sundar@763 283 */
sundar@763 284 public void setIndexedPropertiesToExternalArrayData(final ByteBuffer buf) {
sundar@763 285 inGlobal(new Callable<Void>() {
sundar@763 286 @Override public Void call() {
sundar@763 287 sobj.setArray(ArrayData.allocate(buf));
sundar@763 288 return null;
sundar@763 289 }
sundar@763 290 });
sundar@763 291 }
sundar@763 292
sundar@763 293
sundar@546 294 @Override
sundar@546 295 public boolean isInstance(final Object obj) {
sundar@546 296 if (! (obj instanceof ScriptObjectMirror)) {
sundar@546 297 return false;
sundar@546 298 }
sundar@546 299
sundar@546 300 final ScriptObjectMirror instance = (ScriptObjectMirror)obj;
sundar@546 301 // if not belongs to my global scope, return false
sundar@546 302 if (global != instance.global) {
sundar@546 303 return false;
sundar@546 304 }
sundar@546 305
sundar@546 306 return inGlobal(new Callable<Boolean>() {
sundar@546 307 @Override public Boolean call() {
sundar@546 308 return sobj.isInstance(instance.sobj);
sundar@546 309 }
sundar@546 310 });
sundar@546 311 }
sundar@546 312
sundar@546 313 @Override
sundar@546 314 public String getClassName() {
sundar@546 315 return sobj.getClassName();
sundar@546 316 }
sundar@546 317
sundar@546 318 @Override
sundar@546 319 public boolean isFunction() {
sundar@546 320 return sobj instanceof ScriptFunction;
sundar@546 321 }
sundar@546 322
sundar@546 323 @Override
sundar@546 324 public boolean isStrictFunction() {
sundar@546 325 return isFunction() && ((ScriptFunction)sobj).isStrict();
sundar@546 326 }
sundar@546 327
sundar@546 328 @Override
sundar@546 329 public boolean isArray() {
sundar@546 330 return sobj.isArray();
sundar@546 331 }
sundar@546 332
sundar@437 333 // javax.script.Bindings methods
sundar@437 334
jlaskey@3 335 @Override
jlaskey@3 336 public void clear() {
jlaskey@3 337 inGlobal(new Callable<Object>() {
jlaskey@3 338 @Override public Object call() {
sundar@541 339 sobj.clear(strict);
jlaskey@3 340 return null;
sundar@38 341 }
sundar@38 342 });
jlaskey@3 343 }
jlaskey@3 344
jlaskey@3 345 @Override
jlaskey@3 346 public boolean containsKey(final Object key) {
attila@1221 347 checkKey(key);
jlaskey@3 348 return inGlobal(new Callable<Boolean>() {
jlaskey@3 349 @Override public Boolean call() {
attila@1221 350 return sobj.containsKey(key);
sundar@38 351 }
sundar@38 352 });
jlaskey@3 353 }
jlaskey@3 354
jlaskey@3 355 @Override
jlaskey@3 356 public boolean containsValue(final Object value) {
jlaskey@3 357 return inGlobal(new Callable<Boolean>() {
jlaskey@3 358 @Override public Boolean call() {
jlaskey@3 359 return sobj.containsValue(unwrap(value, global));
sundar@38 360 }
sundar@38 361 });
jlaskey@3 362 }
jlaskey@3 363
jlaskey@3 364 @Override
sundar@38 365 public Set<Map.Entry<String, Object>> entrySet() {
sundar@38 366 return inGlobal(new Callable<Set<Map.Entry<String, Object>>>() {
sundar@38 367 @Override public Set<Map.Entry<String, Object>> call() {
jlaskey@3 368 final Iterator<String> iter = sobj.propertyIterator();
sundar@321 369 final Set<Map.Entry<String, Object>> entries = new LinkedHashSet<>();
jlaskey@3 370
jlaskey@3 371 while (iter.hasNext()) {
sundar@38 372 final String key = iter.next();
attila@1394 373 final Object value = translateUndefined(wrapLikeMe(sobj.get(key)));
jlaskey@3 374 entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
jlaskey@3 375 }
jlaskey@3 376
jlaskey@3 377 return Collections.unmodifiableSet(entries);
jlaskey@3 378 }
jlaskey@3 379 });
jlaskey@3 380 }
jlaskey@3 381
jlaskey@3 382 @Override
jlaskey@3 383 public Object get(final Object key) {
attila@1221 384 checkKey(key);
sundar@38 385 return inGlobal(new Callable<Object>() {
sundar@38 386 @Override public Object call() {
attila@1394 387 return translateUndefined(wrapLikeMe(sobj.get(key)));
sundar@38 388 }
sundar@38 389 });
jlaskey@3 390 }
jlaskey@3 391
jlaskey@3 392 @Override
jlaskey@3 393 public boolean isEmpty() {
sundar@38 394 return inGlobal(new Callable<Boolean>() {
sundar@38 395 @Override public Boolean call() {
sundar@38 396 return sobj.isEmpty();
sundar@38 397 }
sundar@38 398 });
jlaskey@3 399 }
jlaskey@3 400
jlaskey@3 401 @Override
sundar@38 402 public Set<String> keySet() {
sundar@38 403 return inGlobal(new Callable<Set<String>>() {
sundar@38 404 @Override public Set<String> call() {
sundar@38 405 final Iterator<String> iter = sobj.propertyIterator();
sundar@321 406 final Set<String> keySet = new LinkedHashSet<>();
jlaskey@3 407
sundar@38 408 while (iter.hasNext()) {
sundar@38 409 keySet.add(iter.next());
sundar@38 410 }
sundar@38 411
sundar@38 412 return Collections.unmodifiableSet(keySet);
jlaskey@3 413 }
sundar@38 414 });
jlaskey@3 415 }
jlaskey@3 416
jlaskey@3 417 @Override
sundar@38 418 public Object put(final String key, final Object value) {
attila@1221 419 checkKey(key);
sundar@456 420 final ScriptObject oldGlobal = Context.getGlobal();
sundar@88 421 final boolean globalChanged = (oldGlobal != global);
jlaskey@3 422 return inGlobal(new Callable<Object>() {
jlaskey@3 423 @Override public Object call() {
attila@1394 424 final Object modValue = globalChanged? wrapLikeMe(value, oldGlobal) : value;
attila@1394 425 return translateUndefined(wrapLikeMe(sobj.put(key, unwrap(modValue, global), strict)));
sundar@38 426 }
sundar@38 427 });
jlaskey@3 428 }
jlaskey@3 429
jlaskey@3 430 @Override
sundar@38 431 public void putAll(final Map<? extends String, ? extends Object> map) {
sundar@1231 432 Objects.requireNonNull(map, "map is null");
sundar@456 433 final ScriptObject oldGlobal = Context.getGlobal();
sundar@88 434 final boolean globalChanged = (oldGlobal != global);
sundar@38 435 inGlobal(new Callable<Object>() {
sundar@38 436 @Override public Object call() {
sundar@38 437 for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
sundar@88 438 final Object value = entry.getValue();
attila@1394 439 final Object modValue = globalChanged? wrapLikeMe(value, oldGlobal) : value;
attila@1221 440 final String key = entry.getKey();
attila@1221 441 checkKey(key);
attila@1221 442 sobj.set(key, unwrap(modValue, global), getCallSiteFlags());
sundar@38 443 }
sundar@38 444 return null;
jlaskey@3 445 }
sundar@38 446 });
jlaskey@3 447 }
jlaskey@3 448
jlaskey@3 449 @Override
jlaskey@3 450 public Object remove(final Object key) {
attila@1221 451 checkKey(key);
jlaskey@3 452 return inGlobal(new Callable<Object>() {
jlaskey@3 453 @Override public Object call() {
attila@1394 454 return translateUndefined(wrapLikeMe(sobj.remove(key, strict)));
jlaskey@3 455 }
jlaskey@3 456 });
jlaskey@3 457 }
jlaskey@3 458
sundar@321 459 /**
sundar@321 460 * Delete a property from this object.
sundar@321 461 *
sundar@321 462 * @param key the property to be deleted
sundar@321 463 *
sundar@321 464 * @return if the delete was successful or not
sundar@321 465 */
sundar@321 466 public boolean delete(final Object key) {
sundar@321 467 return inGlobal(new Callable<Boolean>() {
sundar@321 468 @Override public Boolean call() {
sundar@541 469 return sobj.delete(unwrap(key, global), strict);
sundar@321 470 }
sundar@321 471 });
sundar@321 472 }
sundar@321 473
jlaskey@3 474 @Override
jlaskey@3 475 public int size() {
jlaskey@3 476 return inGlobal(new Callable<Integer>() {
jlaskey@3 477 @Override public Integer call() {
jlaskey@3 478 return sobj.size();
jlaskey@3 479 }
jlaskey@3 480 });
jlaskey@3 481 }
jlaskey@3 482
jlaskey@3 483 @Override
jlaskey@3 484 public Collection<Object> values() {
sundar@38 485 return inGlobal(new Callable<Collection<Object>>() {
sundar@38 486 @Override public Collection<Object> call() {
sundar@38 487 final List<Object> values = new ArrayList<>(size());
sundar@38 488 final Iterator<Object> iter = sobj.valueIterator();
jlaskey@3 489
sundar@38 490 while (iter.hasNext()) {
attila@1394 491 values.add(translateUndefined(wrapLikeMe(iter.next())));
sundar@38 492 }
sundar@38 493
sundar@38 494 return Collections.unmodifiableList(values);
jlaskey@3 495 }
sundar@38 496 });
sundar@38 497 }
jlaskey@3 498
sundar@350 499 // Support for ECMAScript Object API on mirrors
sundar@350 500
sundar@350 501 /**
sundar@350 502 * Return the __proto__ of this object.
sundar@350 503 * @return __proto__ object.
sundar@350 504 */
sundar@350 505 public Object getProto() {
sundar@350 506 return inGlobal(new Callable<Object>() {
sundar@350 507 @Override public Object call() {
attila@1394 508 return wrapLikeMe(sobj.getProto());
sundar@350 509 }
sundar@350 510 });
sundar@350 511 }
sundar@350 512
sundar@350 513 /**
sundar@513 514 * Set the __proto__ of this object.
sundar@513 515 * @param proto new proto for this object
sundar@513 516 */
sundar@513 517 public void setProto(final Object proto) {
sundar@513 518 inGlobal(new Callable<Void>() {
sundar@513 519 @Override public Void call() {
sundar@866 520 sobj.setPrototypeOf(unwrap(proto, global));
sundar@513 521 return null;
sundar@513 522 }
sundar@513 523 });
sundar@513 524 }
sundar@513 525
sundar@513 526 /**
sundar@350 527 * ECMA 8.12.1 [[GetOwnProperty]] (P)
sundar@350 528 *
sundar@350 529 * @param key property key
sundar@350 530 *
sundar@350 531 * @return Returns the Property Descriptor of the named own property of this
sundar@350 532 * object, or undefined if absent.
sundar@350 533 */
sundar@350 534 public Object getOwnPropertyDescriptor(final String key) {
sundar@350 535 return inGlobal(new Callable<Object>() {
sundar@350 536 @Override public Object call() {
attila@1394 537 return wrapLikeMe(sobj.getOwnPropertyDescriptor(key));
sundar@350 538 }
sundar@350 539 });
sundar@350 540 }
sundar@350 541
sundar@350 542 /**
sundar@350 543 * return an array of own property keys associated with the object.
sundar@350 544 *
sundar@350 545 * @param all True if to include non-enumerable keys.
sundar@350 546 * @return Array of keys.
sundar@350 547 */
sundar@350 548 public String[] getOwnKeys(final boolean all) {
sundar@350 549 return inGlobal(new Callable<String[]>() {
sundar@350 550 @Override public String[] call() {
sundar@437 551 return sobj.getOwnKeys(all);
sundar@350 552 }
sundar@350 553 });
sundar@350 554 }
sundar@350 555
sundar@350 556 /**
sundar@350 557 * Flag this script object as non extensible
sundar@350 558 *
sundar@350 559 * @return the object after being made non extensible
sundar@350 560 */
sundar@350 561 public ScriptObjectMirror preventExtensions() {
sundar@350 562 return inGlobal(new Callable<ScriptObjectMirror>() {
sundar@350 563 @Override public ScriptObjectMirror call() {
sundar@437 564 sobj.preventExtensions();
sundar@350 565 return ScriptObjectMirror.this;
sundar@350 566 }
sundar@350 567 });
sundar@350 568 }
sundar@350 569
sundar@350 570 /**
sundar@350 571 * Check if this script object is extensible
sundar@350 572 * @return true if extensible
sundar@350 573 */
sundar@350 574 public boolean isExtensible() {
sundar@350 575 return inGlobal(new Callable<Boolean>() {
sundar@350 576 @Override public Boolean call() {
sundar@437 577 return sobj.isExtensible();
sundar@350 578 }
sundar@350 579 });
sundar@350 580 }
sundar@350 581
sundar@350 582 /**
sundar@350 583 * ECMAScript 15.2.3.8 - seal implementation
sundar@350 584 * @return the sealed script object
sundar@350 585 */
sundar@350 586 public ScriptObjectMirror seal() {
sundar@350 587 return inGlobal(new Callable<ScriptObjectMirror>() {
sundar@350 588 @Override public ScriptObjectMirror call() {
sundar@437 589 sobj.seal();
sundar@350 590 return ScriptObjectMirror.this;
sundar@350 591 }
sundar@350 592 });
sundar@350 593 }
sundar@350 594
sundar@350 595 /**
sundar@350 596 * Check whether this script object is sealed
sundar@350 597 * @return true if sealed
sundar@350 598 */
sundar@350 599 public boolean isSealed() {
sundar@350 600 return inGlobal(new Callable<Boolean>() {
sundar@350 601 @Override public Boolean call() {
sundar@437 602 return sobj.isSealed();
sundar@350 603 }
sundar@350 604 });
sundar@350 605 }
sundar@350 606
sundar@350 607 /**
sundar@350 608 * ECMA 15.2.39 - freeze implementation. Freeze this script object
sundar@350 609 * @return the frozen script object
sundar@350 610 */
sundar@350 611 public ScriptObjectMirror freeze() {
sundar@350 612 return inGlobal(new Callable<ScriptObjectMirror>() {
sundar@350 613 @Override public ScriptObjectMirror call() {
sundar@437 614 sobj.freeze();
sundar@350 615 return ScriptObjectMirror.this;
sundar@350 616 }
sundar@350 617 });
sundar@350 618 }
sundar@350 619
sundar@350 620 /**
sundar@350 621 * Check whether this script object is frozen
sundar@350 622 * @return true if frozen
sundar@350 623 */
sundar@350 624 public boolean isFrozen() {
sundar@350 625 return inGlobal(new Callable<Boolean>() {
sundar@350 626 @Override public Boolean call() {
sundar@437 627 return sobj.isFrozen();
sundar@350 628 }
sundar@350 629 });
sundar@350 630 }
sundar@350 631
sundar@437 632 /**
sundar@350 633 * Utility to check if given object is ECMAScript undefined value
sundar@350 634 *
sundar@350 635 * @param obj object to check
sundar@350 636 * @return true if 'obj' is ECMAScript undefined value
sundar@350 637 */
sundar@350 638 public static boolean isUndefined(final Object obj) {
sundar@350 639 return obj == ScriptRuntime.UNDEFINED;
sundar@350 640 }
sundar@350 641
sundar@350 642 /**
attila@1221 643 * Utility to convert this script object to the given type.
sundar@668 644 *
sundar@864 645 * @param <T> destination type to convert to
sundar@668 646 * @param type destination type to convert to
sundar@668 647 * @return converted object
sundar@668 648 */
sundar@668 649 public <T> T to(final Class<T> type) {
sundar@668 650 return inGlobal(new Callable<T>() {
sundar@668 651 @Override
sundar@668 652 public T call() {
sundar@668 653 return type.cast(ScriptUtils.convert(sobj, type));
sundar@668 654 }
sundar@668 655 });
sundar@668 656 }
sundar@668 657
sundar@668 658 /**
attila@663 659 * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings.
sundar@322 660 *
attila@663 661 * @param obj object to be wrapped/converted
attila@663 662 * @param homeGlobal global to which this object belongs. Not used for ConsStrings.
attila@663 663 * @return wrapped/converted object
sundar@322 664 */
sundar@665 665 public static Object wrap(final Object obj, final Object homeGlobal) {
attila@1394 666 return wrap(obj, homeGlobal, false);
attila@1394 667 }
attila@1394 668
attila@1394 669 /**
attila@1394 670 * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings. The
attila@1394 671 * created wrapper will implement the Java {@code List} interface if {@code obj} is a JavaScript
attila@1394 672 * {@code Array} object; this is compatible with Java JSON libraries expectations. Arrays retrieved through its
attila@1394 673 * properties (transitively) will also implement the list interface.
attila@1394 674 *
attila@1394 675 * @param obj object to be wrapped/converted
attila@1394 676 * @param homeGlobal global to which this object belongs. Not used for ConsStrings.
attila@1394 677 * @return wrapped/converted object
attila@1394 678 */
attila@1394 679 public static Object wrapAsJSONCompatible(final Object obj, final Object homeGlobal) {
attila@1394 680 return wrap(obj, homeGlobal, true);
attila@1394 681 }
attila@1394 682
attila@1394 683 /**
attila@1394 684 * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings.
attila@1394 685 *
attila@1394 686 * @param obj object to be wrapped/converted
attila@1394 687 * @param homeGlobal global to which this object belongs. Not used for ConsStrings.
attila@1394 688 * @param jsonCompatible if true, the created wrapper will implement the Java {@code List} interface if
attila@1394 689 * {@code obj} is a JavaScript {@code Array} object. Arrays retrieved through its properties (transitively)
attila@1394 690 * will also implement the list interface.
attila@1394 691 * @return wrapped/converted object
attila@1394 692 */
attila@1394 693 private static Object wrap(final Object obj, final Object homeGlobal, final boolean jsonCompatible) {
attila@663 694 if(obj instanceof ScriptObject) {
attila@1394 695 if (!(homeGlobal instanceof Global)) {
attila@1394 696 return obj;
attila@1394 697 }
attila@1394 698 final ScriptObject sobj = (ScriptObject)obj;
attila@1394 699 final Global global = (Global)homeGlobal;
attila@1394 700 final ScriptObjectMirror mirror = new ScriptObjectMirror(sobj, global, jsonCompatible);
attila@1394 701 if (jsonCompatible && sobj.isArray()) {
attila@1394 702 return new JSONListAdapter(mirror, global);
attila@1394 703 }
attila@1394 704 return mirror;
attila@1394 705 } else if(obj instanceof ConsString) {
attila@663 706 return obj.toString();
attila@1394 707 } else if (jsonCompatible && obj instanceof ScriptObjectMirror) {
attila@1394 708 // Since choosing JSON compatible representation is an explicit decision on user's part, if we're asked to
attila@1394 709 // wrap a mirror that was not JSON compatible, explicitly create its compatible counterpart following the
attila@1394 710 // principle of least surprise.
attila@1394 711 return ((ScriptObjectMirror)obj).asJSONCompatible();
attila@663 712 }
attila@663 713 return obj;
jlaskey@3 714 }
jlaskey@3 715
sundar@322 716 /**
attila@1394 717 * Wraps the passed object with the same jsonCompatible flag as this mirror.
attila@1394 718 * @param obj the object
attila@1394 719 * @param homeGlobal the object's home global.
attila@1394 720 * @return a wrapper for the object.
attila@1394 721 */
attila@1394 722 private Object wrapLikeMe(final Object obj, final Object homeGlobal) {
attila@1394 723 return wrap(obj, homeGlobal, jsonCompatible);
attila@1394 724 }
attila@1394 725
attila@1394 726 /**
attila@1394 727 * Wraps the passed object with the same home global and jsonCompatible flag as this mirror.
attila@1394 728 * @param obj the object
attila@1394 729 * @return a wrapper for the object.
attila@1394 730 */
attila@1394 731 private Object wrapLikeMe(final Object obj) {
attila@1394 732 return wrapLikeMe(obj, global);
attila@1394 733 }
attila@1394 734
attila@1394 735 /**
sundar@322 736 * Unwrap a script object mirror if needed.
sundar@322 737 *
sundar@322 738 * @param obj object to be unwrapped
sundar@322 739 * @param homeGlobal global to which this object belongs
sundar@322 740 * @return unwrapped object
sundar@322 741 */
sundar@665 742 public static Object unwrap(final Object obj, final Object homeGlobal) {
jlaskey@3 743 if (obj instanceof ScriptObjectMirror) {
jlaskey@3 744 final ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
jlaskey@3 745 return (mirror.global == homeGlobal)? mirror.sobj : obj;
attila@1394 746 } else if (obj instanceof JSONListAdapter) {
attila@1394 747 return ((JSONListAdapter)obj).unwrap(homeGlobal);
jlaskey@3 748 }
jlaskey@3 749
jlaskey@3 750 return obj;
jlaskey@3 751 }
jlaskey@3 752
sundar@322 753 /**
sundar@322 754 * Wrap an array of object to script object mirrors if needed.
sundar@322 755 *
sundar@322 756 * @param args array to be unwrapped
sundar@322 757 * @param homeGlobal global to which this object belongs
sundar@322 758 * @return wrapped array
sundar@322 759 */
sundar@665 760 public static Object[] wrapArray(final Object[] args, final Object homeGlobal) {
attila@1394 761 return wrapArray(args, homeGlobal, false);
attila@1394 762 }
attila@1394 763
attila@1394 764 private static Object[] wrapArray(final Object[] args, final Object homeGlobal, final boolean jsonCompatible) {
jlaskey@3 765 if (args == null || args.length == 0) {
jlaskey@3 766 return args;
jlaskey@3 767 }
jlaskey@3 768
jlaskey@3 769 final Object[] newArgs = new Object[args.length];
jlaskey@3 770 int index = 0;
jlaskey@3 771 for (final Object obj : args) {
attila@1394 772 newArgs[index] = wrap(obj, homeGlobal, jsonCompatible);
jlaskey@3 773 index++;
jlaskey@3 774 }
jlaskey@3 775 return newArgs;
jlaskey@3 776 }
jlaskey@3 777
attila@1394 778 private Object[] wrapArrayLikeMe(final Object[] args, final Object homeGlobal) {
attila@1394 779 return wrapArray(args, homeGlobal, jsonCompatible);
attila@1394 780 }
attila@1394 781
sundar@322 782 /**
sundar@322 783 * Unwrap an array of script object mirrors if needed.
sundar@322 784 *
sundar@322 785 * @param args array to be unwrapped
sundar@322 786 * @param homeGlobal global to which this object belongs
sundar@322 787 * @return unwrapped array
sundar@322 788 */
sundar@665 789 public static Object[] unwrapArray(final Object[] args, final Object homeGlobal) {
jlaskey@3 790 if (args == null || args.length == 0) {
jlaskey@3 791 return args;
jlaskey@3 792 }
jlaskey@3 793
jlaskey@3 794 final Object[] newArgs = new Object[args.length];
jlaskey@3 795 int index = 0;
jlaskey@3 796 for (final Object obj : args) {
jlaskey@3 797 newArgs[index] = unwrap(obj, homeGlobal);
jlaskey@3 798 index++;
jlaskey@3 799 }
jlaskey@3 800 return newArgs;
jlaskey@3 801 }
sundar@322 802
attila@963 803 /**
attila@963 804 * Are the given objects mirrors to same underlying object?
attila@963 805 *
attila@963 806 * @param obj1 first object
attila@963 807 * @param obj2 second object
attila@963 808 * @return true if obj1 and obj2 are identical script objects or mirrors of it.
attila@963 809 */
attila@963 810 public static boolean identical(final Object obj1, final Object obj2) {
attila@963 811 final Object o1 = (obj1 instanceof ScriptObjectMirror)?
attila@963 812 ((ScriptObjectMirror)obj1).sobj : obj1;
attila@963 813
attila@963 814 final Object o2 = (obj2 instanceof ScriptObjectMirror)?
attila@963 815 ((ScriptObjectMirror)obj2).sobj : obj2;
attila@963 816
attila@963 817 return o1 == o2;
attila@963 818 }
attila@963 819
sundar@322 820 // package-privates below this.
sundar@437 821
sundar@771 822 ScriptObjectMirror(final ScriptObject sobj, final Global global) {
attila@1394 823 this(sobj, global, false);
attila@1394 824 }
attila@1394 825
attila@1394 826 private ScriptObjectMirror(final ScriptObject sobj, final Global global, final boolean jsonCompatible) {
sundar@472 827 assert sobj != null : "ScriptObjectMirror on null!";
sundar@771 828 assert global != null : "home Global is null";
sundar@472 829
sundar@437 830 this.sobj = sobj;
sundar@437 831 this.global = global;
sundar@771 832 this.strict = global.isStrictContext();
attila@1394 833 this.jsonCompatible = jsonCompatible;
sundar@437 834 }
sundar@437 835
sundar@472 836 // accessors for script engine
sundar@322 837 ScriptObject getScriptObject() {
sundar@322 838 return sobj;
sundar@322 839 }
sundar@322 840
sundar@771 841 Global getHomeGlobal() {
sundar@472 842 return global;
sundar@472 843 }
sundar@472 844
attila@962 845 static Object translateUndefined(final Object obj) {
sundar@322 846 return (obj == ScriptRuntime.UNDEFINED)? null : obj;
sundar@322 847 }
sundar@437 848
hannesw@1020 849 private int getCallSiteFlags() {
hannesw@1020 850 return strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
hannesw@1020 851 }
hannesw@1020 852
sundar@437 853 // internals only below this.
sundar@437 854 private <V> V inGlobal(final Callable<V> callable) {
sundar@771 855 final Global oldGlobal = Context.getGlobal();
sundar@437 856 final boolean globalChanged = (oldGlobal != global);
sundar@437 857 if (globalChanged) {
sundar@456 858 Context.setGlobal(global);
sundar@437 859 }
sundar@437 860 try {
sundar@437 861 return callable.call();
sundar@748 862 } catch (final NashornException ne) {
sundar@748 863 throw ne.initEcmaError(global);
sundar@437 864 } catch (final RuntimeException e) {
sundar@437 865 throw e;
sundar@437 866 } catch (final Exception e) {
sundar@437 867 throw new AssertionError("Cannot happen", e);
sundar@437 868 } finally {
sundar@437 869 if (globalChanged) {
sundar@456 870 Context.setGlobal(oldGlobal);
sundar@437 871 }
sundar@437 872 }
sundar@437 873 }
attila@644 874
attila@1221 875 /**
attila@1221 876 * Ensures the key is not null, empty string, or a non-String object. The contract of the {@link Bindings}
attila@1221 877 * interface requires that these are not accepted as keys.
attila@1221 878 * @param key the key to check
attila@1221 879 * @throws NullPointerException if key is null
attila@1221 880 * @throws ClassCastException if key is not a String
attila@1221 881 * @throws IllegalArgumentException if key is empty string
attila@1221 882 */
attila@1221 883 private static void checkKey(final Object key) {
sundar@1231 884 Objects.requireNonNull(key, "key can not be null");
sundar@1231 885
sundar@1231 886 if (!(key instanceof String)) {
attila@1221 887 throw new ClassCastException("key should be a String. It is " + key.getClass().getName() + " instead.");
attila@1221 888 } else if (((String)key).length() == 0) {
attila@1221 889 throw new IllegalArgumentException("key can not be empty");
attila@1221 890 }
attila@1221 891 }
attila@1221 892
attila@1250 893 @Override @Deprecated
attila@644 894 public double toNumber() {
attila@644 895 return inGlobal(new Callable<Double>() {
attila@644 896 @Override public Double call() {
attila@644 897 return JSType.toNumber(sobj);
attila@644 898 }
attila@644 899 });
attila@644 900 }
attila@1250 901
attila@1250 902 @Override
attila@1250 903 public Object getDefaultValue(final Class<?> hint) {
attila@1250 904 return inGlobal(new Callable<Object>() {
attila@1250 905 @Override public Object call() {
attila@1250 906 try {
attila@1250 907 return sobj.getDefaultValue(hint);
attila@1250 908 } catch (final ECMAException e) {
attila@1250 909 // We're catching ECMAException (likely TypeError), and translating it to
attila@1250 910 // UnsupportedOperationException. This in turn will be translated into TypeError of the
attila@1250 911 // caller's Global by JSType#toPrimitive(JSObject,Class) therefore ensuring that it's
attila@1250 912 // recognized as "instanceof TypeError" in the caller.
attila@1250 913 throw new UnsupportedOperationException(e.getMessage(), e);
attila@1250 914 }
attila@1250 915 }
attila@1250 916 });
attila@1250 917 }
attila@1394 918
attila@1394 919 private ScriptObjectMirror asJSONCompatible() {
attila@1394 920 if (this.jsonCompatible) {
attila@1394 921 return this;
attila@1394 922 }
attila@1394 923 return new ScriptObjectMirror(sobj, global, true);
attila@1394 924 }
jlaskey@3 925 }

mercurial