src/jdk/nashorn/internal/runtime/ScriptObject.java

Print this page




 126     private int flags;
 127 
 128     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
 129     public Object[] spill;
 130 
 131     /** Indexed array data. */
 132     private ArrayData arrayData;
 133 
 134     static final MethodHandle GETPROTO           = findOwnMH("getProto", ScriptObject.class);
 135     static final MethodHandle SETPROTOCHECK      = findOwnMH("setProtoCheck", void.class, Object.class);
 136     static final MethodHandle MEGAMORPHIC_GET    = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
 137 
 138     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
 139     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 140     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 141     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
 142 
 143     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
 144     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
 145 


 146     /** Method handle for getting a function argument at a given index. Used from MapCreator */
 147     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
 148 
 149     /** Method handle for setting a function argument at a given index. Used from MapCreator */
 150     public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
 151 
 152     /** Method handle for getting the proto of a ScriptObject */
 153     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 154 
 155     /** Method handle for setting the proto of a ScriptObject */
 156     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
 157 
 158     /** Method handle for setting the proto of a ScriptObject after checking argument */
 159     public static final Call SET_PROTO_CHECK    = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
 160 
 161     /** Method handle for setting the user accessors of a ScriptObject */
 162     public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
 163 
 164     /**
 165      * Constructor


1695      * @param request the link request
1696      *
1697      * @return GuardedInvocation to be invoked at call site.
1698      */
1699     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1700         // R(P0, P1, ...)
1701         final MethodType callType = desc.getMethodType();
1702         // use type Object(P0) for the getter
1703         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1704         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1705 
1706         // Object(P0) => Object(P0, P1, ...)
1707         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1708         // R(Object, P0, P1, ...)
1709         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1710         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1711         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1712     }
1713 
1714     /**






































1715      * Find the appropriate GET method for an invoke dynamic call.
1716      *
1717      * @param desc     the call site descriptor
1718      * @param request  the link request
1719      * @param operator operator for get: getProp, getMethod, getElem etc
1720      *
1721      * @return GuardedInvocation to be invoked at call site.
1722      */
1723     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1724         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1725         if (request.isCallSiteUnstable()) {
1726             return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
1727         }
1728 
1729         final FindProperty find = findProperty(name, true);
1730         MethodHandle methodHandle;
1731 
1732         if (find == null) {
1733             if (PROTO_PROPERTY_NAME.equals(name)) {
1734                 return new GuardedInvocation(GETPROTO, NashornGuards.getScriptObjectGuard());
1735             }
1736 
1737             if ("getProp".equals(operator)) {
1738                 return noSuchProperty(desc, request);
1739             } else if ("getMethod".equals(operator)) {
1740                 return noSuchMethod(desc, request);
1741             } else if ("getElem".equals(operator)) {
1742                 return createEmptyGetter(desc, name);
1743             }
1744             throw new AssertionError(); // never invoked with any other operation
1745         }
1746 
1747         final Class<?> returnType = desc.getMethodType().returnType();
1748         final Property property = find.getProperty();
1749         methodHandle = find.getGetter(returnType);
1750 

1751         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
1752         final MethodHandle guard = NashornGuards.getMapGuard(getMap());
1753 
1754         if (methodHandle != null) {
1755             assert methodHandle.type().returnType().equals(returnType);
1756             if (find.isSelf()) {
1757                 return new GuardedInvocation(methodHandle, ObjectClassGenerator.OBJECT_FIELDS_ONLY &&
1758                         NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType() ? null : guard);
1759             }
1760 
1761             final ScriptObject prototype = find.getOwner();
1762 
1763             if (!property.hasGetterFunction(prototype)) {
1764                 methodHandle = bindTo(methodHandle, prototype);


1765             }
1766             return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
1767         }
1768 
1769         assert !NashornCallSiteDescriptor.isFastScope(desc);
1770         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
1771     }
1772 
1773     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
1774         final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
1775         final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
1776         return new GuardedInvocation(invoker, guard);
1777     }
1778 
1779     @SuppressWarnings("unused")
1780     private Object megamorphicGet(final String key, final boolean isMethod) {
1781         final FindProperty find = findProperty(key, true);
1782 
1783         if (find != null) {
1784             return getObjectValue(find);
1785         }
1786 


1816             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
1817         }
1818 
1819         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
1820     }
1821 
1822     private static MethodHandle getScriptObjectGuard(final MethodType type) {
1823         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
1824     }
1825 
1826     /**
1827      * Find the appropriate SET method for an invoke dynamic call.
1828      *
1829      * @param desc    the call site descriptor
1830      * @param request the link request
1831      *
1832      * @return GuardedInvocation to be invoked at call site.
1833      */
1834     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1835         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1836         if (request.isCallSiteUnstable()) {
1837             return findMegaMorphicSetMethod(desc, name);
1838         }
1839 
1840         final boolean scope = isScope();
1841         /*
1842          * If doing property set on a scope object, we should stop proto search on the first
1843          * non-scope object. Without this, for example, when assigning "toString" on global scope,
1844          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
1845          *
1846          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
1847          */
1848         FindProperty find = findProperty(name, true, scope, this);
1849 
1850         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
1851         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
1852             // We should still check if inherited data property is not writable
1853             if (isExtensible() && !find.getProperty().isWritable()) {
1854                 return createEmptySetMethod(desc, "property.not.writable", false);
1855             }
1856             // Otherwise, forget the found property


2742             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
2743                 array = array.delete(oldLength, (longIndex - 1));
2744             }
2745 
2746             setArray(array);
2747         }
2748     }
2749 
2750     /**
2751      * This is the most generic of all Object setters. Most of the others use this in some form.
2752      * TODO: should be further specialized
2753      *
2754      * @param find    found property
2755      * @param strict  are we in strict mode
2756      * @param key     property key
2757      * @param value   property value
2758      */
2759     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
2760         FindProperty f = find;
2761 
2762         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty)) {

2763             f = null;
2764         }
2765 
2766         if (f != null) {
2767             if (!f.getProperty().isWritable()) {
2768                 if (strict) {
2769                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
2770                 }
2771 
2772                 return;
2773             }
2774 
2775             f.setObjectValue(value, strict);
2776 
2777         } else if (!isExtensible()) {
2778             if (strict) {
2779                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
2780             }
2781         } else {
2782             spill(key, value);




 126     private int flags;
 127 
 128     /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
 129     public Object[] spill;
 130 
 131     /** Indexed array data. */
 132     private ArrayData arrayData;
 133 
 134     static final MethodHandle GETPROTO           = findOwnMH("getProto", ScriptObject.class);
 135     static final MethodHandle SETPROTOCHECK      = findOwnMH("setProtoCheck", void.class, Object.class);
 136     static final MethodHandle MEGAMORPHIC_GET    = findOwnMH("megamorphicGet", Object.class, String.class, boolean.class);
 137 
 138     static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
 139     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 140     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
 141     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
 142 
 143     private static final MethodHandle TRUNCATINGFILTER   = findOwnMH("truncatingFilter", Object[].class, int.class, Object[].class);
 144     private static final MethodHandle KNOWNFUNCPROPGUARD = findOwnMH("knownFunctionPropertyGuard", boolean.class, Object.class, PropertyMap.class, MethodHandle.class, Object.class, ScriptFunction.class);
 145 
 146     private static final ArrayList<MethodHandle> protoFilters = new ArrayList<>();
 147 
 148     /** Method handle for getting a function argument at a given index. Used from MapCreator */
 149     public static final Call GET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
 150 
 151     /** Method handle for setting a function argument at a given index. Used from MapCreator */
 152     public static final Call SET_ARGUMENT       = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArgument", void.class, int.class, Object.class);
 153 
 154     /** Method handle for getting the proto of a ScriptObject */
 155     public static final Call GET_PROTO          = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
 156 
 157     /** Method handle for setting the proto of a ScriptObject */
 158     public static final Call SET_PROTO          = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
 159 
 160     /** Method handle for setting the proto of a ScriptObject after checking argument */
 161     public static final Call SET_PROTO_CHECK    = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
 162 
 163     /** Method handle for setting the user accessors of a ScriptObject */
 164     public static final Call SET_USER_ACCESSORS = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setUserAccessors", void.class, String.class, ScriptFunction.class, ScriptFunction.class);
 165 
 166     /**
 167      * Constructor


1697      * @param request the link request
1698      *
1699      * @return GuardedInvocation to be invoked at call site.
1700      */
1701     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1702         // R(P0, P1, ...)
1703         final MethodType callType = desc.getMethodType();
1704         // use type Object(P0) for the getter
1705         final CallSiteDescriptor getterType = desc.changeMethodType(MethodType.methodType(Object.class, callType.parameterType(0)));
1706         final GuardedInvocation getter = findGetMethod(getterType, request, "getMethod");
1707 
1708         // Object(P0) => Object(P0, P1, ...)
1709         final MethodHandle argDroppingGetter = MH.dropArguments(getter.getInvocation(), 1, callType.parameterList().subList(1, callType.parameterCount()));
1710         // R(Object, P0, P1, ...)
1711         final MethodHandle invoker = Bootstrap.createDynamicInvoker("dyn:call", callType.insertParameterTypes(0, argDroppingGetter.type().returnType()));
1712         // Fold Object(P0, P1, ...) into R(Object, P0, P1, ...) => R(P0, P1, ...)
1713         return getter.replaceMethods(MH.foldArguments(invoker, argDroppingGetter), getter.getGuard());
1714     }
1715 
1716     /**
1717      * Test whether this object contains in its prototype chain or is itself a with-object.
1718      * @return true if a with-object was found
1719      */
1720     final boolean hasWithScope() {
1721         if (isScope()) {
1722             for (ScriptObject obj = this; obj != null; obj = obj.getProto()) {
1723                 if (obj instanceof WithObject) {
1724                     return true;
1725                 }
1726             }
1727         }
1728         return false;
1729     }
1730 
1731     /**
1732      * Add a filter to the first argument of {@code methodHandle} that calls its {@link #getProto()} method
1733      * {@code depth} times.
1734      * @param methodHandle a method handle
1735      * @param depth        distance to target prototype
1736      * @return the filtered method handle
1737      */
1738     static MethodHandle addProtoFilter(final MethodHandle methodHandle, final int depth) {
1739         if (depth == 0) {
1740             return methodHandle;
1741         }
1742         final int listIndex = depth - 1; // We don't need 0-deep walker
1743         MethodHandle filter = listIndex < protoFilters.size() ? protoFilters.get(listIndex) : null;
1744 
1745         if(filter == null) {
1746             filter = addProtoFilter(GETPROTO, depth - 1);
1747             protoFilters.add(null);
1748             protoFilters.set(listIndex, filter);
1749         }
1750 
1751         return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
1752     }
1753 
1754     /**
1755      * Find the appropriate GET method for an invoke dynamic call.
1756      *
1757      * @param desc     the call site descriptor
1758      * @param request  the link request
1759      * @param operator operator for get: getProp, getMethod, getElem etc
1760      *
1761      * @return GuardedInvocation to be invoked at call site.
1762      */
1763     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
1764         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1765         if (request.isCallSiteUnstable() || hasWithScope()) {
1766             return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
1767         }
1768 
1769         final FindProperty find = findProperty(name, true);
1770         MethodHandle methodHandle;
1771 
1772         if (find == null) {
1773             if (PROTO_PROPERTY_NAME.equals(name)) {
1774                 return new GuardedInvocation(GETPROTO, NashornGuards.getScriptObjectGuard());
1775             }
1776 
1777             if ("getProp".equals(operator)) {
1778                 return noSuchProperty(desc, request);
1779             } else if ("getMethod".equals(operator)) {
1780                 return noSuchMethod(desc, request);
1781             } else if ("getElem".equals(operator)) {
1782                 return createEmptyGetter(desc, name);
1783             }
1784             throw new AssertionError(); // never invoked with any other operation
1785         }
1786 
1787         final Class<?> returnType = desc.getMethodType().returnType();
1788         final Property property = find.getProperty();
1789         methodHandle = find.getGetter(returnType);
1790 
1791         final boolean noGuard = ObjectClassGenerator.OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
1792         // getMap() is fine as we have the prototype switchpoint depending on where the property was found
1793         final MethodHandle guard = noGuard ? null : NashornGuards.getMapGuard(getMap());
1794 
1795         if (methodHandle != null) {
1796             assert methodHandle.type().returnType().equals(returnType);
1797             if (find.isSelf()) {
1798                 return new GuardedInvocation(methodHandle, guard);

1799             }
1800 
1801             if (!property.hasGetterFunction(find.getOwner())) {
1802                 // If not a scope bind to actual prototype as changing prototype will change the property map.
1803                 // For scopes we install a filter that replaces the self object with the prototype owning the property.
1804                 methodHandle = isScope() ?
1805                         addProtoFilter(methodHandle, find.getProtoChainLength()) :
1806                         bindTo(methodHandle, find.getOwner());
1807             }
1808             return new GuardedInvocation(methodHandle, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), guard);
1809         }
1810 
1811         assert !NashornCallSiteDescriptor.isFastScope(desc);
1812         return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
1813     }
1814 
1815     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
1816         final MethodHandle invoker = MH.insertArguments(MEGAMORPHIC_GET, 1, name, isMethod);
1817         final MethodHandle guard = getScriptObjectGuard(desc.getMethodType());
1818         return new GuardedInvocation(invoker, guard);
1819     }
1820 
1821     @SuppressWarnings("unused")
1822     private Object megamorphicGet(final String key, final boolean isMethod) {
1823         final FindProperty find = findProperty(key, true);
1824 
1825         if (find != null) {
1826             return getObjectValue(find);
1827         }
1828 


1858             name += Character.toUpperCase(returnTypeName.charAt(0)) + returnTypeName.substring(1, returnTypeName.length());
1859         }
1860 
1861         return new GuardedInvocation(findOwnMH(name, returnClass, keyClass), getScriptObjectGuard(callType));
1862     }
1863 
1864     private static MethodHandle getScriptObjectGuard(final MethodType type) {
1865         return ScriptObject.class.isAssignableFrom(type.parameterType(0)) ? null : NashornGuards.getScriptObjectGuard();
1866     }
1867 
1868     /**
1869      * Find the appropriate SET method for an invoke dynamic call.
1870      *
1871      * @param desc    the call site descriptor
1872      * @param request the link request
1873      *
1874      * @return GuardedInvocation to be invoked at call site.
1875      */
1876     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
1877         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
1878         if (request.isCallSiteUnstable() || hasWithScope()) {
1879             return findMegaMorphicSetMethod(desc, name);
1880         }
1881 
1882         final boolean scope = isScope();
1883         /*
1884          * If doing property set on a scope object, we should stop proto search on the first
1885          * non-scope object. Without this, for example, when assigning "toString" on global scope,
1886          * we'll end up assigning it on it's proto - which is Object.prototype.toString !!
1887          *
1888          * toString = function() { print("global toString"); } // don't affect Object.prototype.toString
1889          */
1890         FindProperty find = findProperty(name, true, scope, this);
1891 
1892         // If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
1893         if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
1894             // We should still check if inherited data property is not writable
1895             if (isExtensible() && !find.getProperty().isWritable()) {
1896                 return createEmptySetMethod(desc, "property.not.writable", false);
1897             }
1898             // Otherwise, forget the found property


2784             if (array.canDelete(oldLength, (longIndex - 1), strict)) {
2785                 array = array.delete(oldLength, (longIndex - 1));
2786             }
2787 
2788             setArray(array);
2789         }
2790     }
2791 
2792     /**
2793      * This is the most generic of all Object setters. Most of the others use this in some form.
2794      * TODO: should be further specialized
2795      *
2796      * @param find    found property
2797      * @param strict  are we in strict mode
2798      * @param key     property key
2799      * @param value   property value
2800      */
2801     public final void setObject(final FindProperty find, final boolean strict, final String key, final Object value) {
2802         FindProperty f = find;
2803 
2804         if (f != null && f.isInherited() && !(f.getProperty() instanceof UserAccessorProperty) && !isScope()) {
2805             // Setting a property should not modify the property in prototype unless this is a scope object.
2806             f = null;
2807         }
2808 
2809         if (f != null) {
2810             if (!f.getProperty().isWritable()) {
2811                 if (strict) {
2812                     throw typeError("property.not.writable", key, ScriptRuntime.safeToString(this));
2813                 }
2814 
2815                 return;
2816             }
2817 
2818             f.setObjectValue(value, strict);
2819 
2820         } else if (!isExtensible()) {
2821             if (strict) {
2822                 throw typeError("object.non.extensible", key, ScriptRuntime.safeToString(this));
2823             }
2824         } else {
2825             spill(key, value);