/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; import static jdk.nashorn.internal.lookup.Lookup.MH; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; import jdk.nashorn.internal.objects.annotations.Constructor; import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.scripts.JO; /** * This class is the implementation of the Nashorn-specific global object named {@code JSAdapter}. It can be * thought of as the {@link java.lang.reflect.Proxy} equivalent for JavaScript. NativeJSAdapter calls specially named * JavaScript methods on an adaptee object when property access/update/call/new/delete is attempted on it. Example: *
* var y = { * __get__ : function (name) { ... } * __has__ : function (name) { ... } * __put__ : function (name, value) {...} * __call__ : function (name, arg1, arg2) {...} * __new__ : function (arg1, arg2) {...} * __delete__ : function (name) { ... } * __getIds__ : function () { ... } * }; * * var x = new JSAdapter(y); * * x.i; // calls y.__get__ * x.foo(); // calls y.__call__ * new x(); // calls y.__new__ * i in x; // calls y.__has__ * x.p = 10; // calls y.__put__ * delete x.p; // calls y.__delete__ * for (i in x) { print(i); } // calls y.__getIds__ **
* JavaScript caller of adapter object is isolated from the fact that the property access/mutation/deletion are really * calls to JavaScript methods on adaptee. *
** JSAdapter constructor can optionally receive an "overrides" object. Properties of overrides object is copied to * JSAdapter instance. When user accessed property is one of these, then adaptee's methods like {@code __get__}, * {@code __put__} etc. are not called for those. This can be used to make certain "preferred" properties that can be * accessed in the usual/faster way avoiding proxy mechanism. Example: *
** var x = new JSAdapter({ foo: 444, bar: 6546 }) { * __get__: function(name) { return name; } * }; * * x.foo; // 444 directly retrieved without __get__ call * x.bar = 'hello'; // "bar" directly set without __put__ call * x.prop // calls __get__("prop") as 'prop' is not overridden ** It is possible to pass a specific prototype for JSAdapter instance by passing three arguments to JSAdapter * constructor. So exact signature of JSAdapter constructor is as follows: *
* JSAdapter([proto], [overrides], adaptee); ** Both proto and overrides are optional - but adaptee is not. When proto is not passed {@code JSAdapter.prototype} is * used. */ @ScriptClass("JSAdapter") public final class NativeJSAdapter extends ScriptObject { /** object get operation */ public static final String __get__ = "__get__"; /** object out operation */ public static final String __put__ = "__put__"; /** object call operation */ public static final String __call__ = "__call__"; /** object new operation */ public static final String __new__ = "__new__"; /** object getIds operation */ public static final String __getIds__ = "__getIds__"; /** object getKeys operation */ public static final String __getKeys__ = "__getKeys__"; /** object getValues operation */ public static final String __getValues__ = "__getValues__"; /** object has operation */ public static final String __has__ = "__has__"; /** object delete operation */ public static final String __delete__ = "__delete__"; // the new extensibility, sealing and freezing operations /** prevent extensions operation */ public static final String __preventExtensions__ = "__preventExtensions__"; /** isExtensible extensions operation */ public static final String __isExtensible__ = "__isExtensible__"; /** seal operation */ public static final String __seal__ = "__seal__"; /** isSealed extensions operation */ public static final String __isSealed__ = "__isSealed__"; /** freeze operation */ public static final String __freeze__ = "__freeze__"; /** isFrozen extensions operation */ public static final String __isFrozen__ = "__isFrozen__"; private final ScriptObject adaptee; private final boolean overrides; private static final MethodHandle IS_JSADAPTOR = findOwnMH("isJSAdaptor", boolean.class, Object.class, Object.class, MethodHandle.class, Object.class, ScriptFunction.class); // initialized by nasgen private static PropertyMap $nasgenmap$; NativeJSAdapter(final Object overrides, final ScriptObject adaptee, final ScriptObject proto, final PropertyMap map) { super(proto, map); this.adaptee = wrapAdaptee(adaptee); if (overrides instanceof ScriptObject) { this.overrides = true; final ScriptObject sobj = (ScriptObject)overrides; this.addBoundProperties(sobj); } else { this.overrides = false; } } private static ScriptObject wrapAdaptee(final ScriptObject adaptee) { return new JO(adaptee, JO.getInitialMap()); } @Override public String getClassName() { return "JSAdapter"; } @Override public int getInt(final Object key) { return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key); } @Override public int getInt(final double key) { return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key); } @Override public int getInt(final long key) { return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key); } @Override public int getInt(final int key) { return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key); } @Override public long getLong(final Object key) { return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key); } @Override public long getLong(final double key) { return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key); } @Override public long getLong(final long key) { return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key); } @Override public long getLong(final int key) { return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key); } @Override public double getDouble(final Object key) { return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key); } @Override public double getDouble(final double key) { return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key); } @Override public double getDouble(final long key) { return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key); } @Override public double getDouble(final int key) { return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key); } @Override public Object get(final Object key) { return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key); } @Override public Object get(final double key) { return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key); } @Override public Object get(final long key) { return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key); } @Override public Object get(final int key) { return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key); } @Override public void set(final Object key, final int value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final Object key, final long value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final Object key, final double value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final Object key, final Object value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final double key, final int value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final double key, final long value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final double key, final double value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final double key, final Object value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final long key, final int value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final long key, final long value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final long key, final double value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final long key, final Object value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final int key, final int value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final int key, final long value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final int key, final double value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public void set(final int key, final Object value, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { super.set(key, value, strict); } else { callAdaptee(__put__, key, value, strict); } } @Override public boolean has(final Object key) { if (overrides && super.hasOwnProperty(key)) { return true; } return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key)); } @Override public boolean has(final int key) { if (overrides && super.hasOwnProperty(key)) { return true; } return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key)); } @Override public boolean has(final long key) { if (overrides && super.hasOwnProperty(key)) { return true; } return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key)); } @Override public boolean has(final double key) { if (overrides && super.hasOwnProperty(key)) { return true; } return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key)); } @Override public boolean delete(final int key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); } @Override public boolean delete(final long key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); } @Override public boolean delete(final double key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); } @Override public boolean delete(final Object key, final boolean strict) { if (overrides && super.hasOwnProperty(key)) { return super.delete(key, strict); } return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict)); } @Override public Iterator