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);
|