SetMethodCreator.java revision 953:221a84ef44c0
1168404Spjd/* 2168404Spjd * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3168404Spjd * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4168404Spjd * 5168404Spjd * This code is free software; you can redistribute it and/or modify it 6168404Spjd * under the terms of the GNU General Public License version 2 only, as 7168404Spjd * published by the Free Software Foundation. Oracle designates this 8168404Spjd * particular file as subject to the "Classpath" exception as provided 9168404Spjd * by Oracle in the LICENSE file that accompanied this code. 10168404Spjd * 11168404Spjd * This code is distributed in the hope that it will be useful, but WITHOUT 12168404Spjd * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13168404Spjd * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14168404Spjd * version 2 for more details (a copy is included in the LICENSE file that 15168404Spjd * accompanied this code). 16168404Spjd * 17168404Spjd * You should have received a copy of the GNU General Public License version 18168404Spjd * 2 along with this work; if not, write to the Free Software Foundation, 19168404Spjd * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20168404Spjd * 21168404Spjd * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22168404Spjd * or visit www.oracle.com if you need additional information or have any 23219089Spjd * questions. 24249195Smm */ 25255750Sdelphij 26168404Spjdpackage jdk.nashorn.internal.runtime; 27168404Spjd 28168404Spjdimport static jdk.nashorn.internal.lookup.Lookup.MH; 29168404Spjdimport static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; 30168404Spjdimport static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 31255750Sdelphij 32255750Sdelphijimport java.lang.invoke.MethodHandle; 33255750Sdelphijimport java.lang.invoke.SwitchPoint; 34255750Sdelphijimport jdk.internal.dynalink.CallSiteDescriptor; 35255750Sdelphijimport jdk.internal.dynalink.linker.GuardedInvocation; 36168404Spjdimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 37168404Spjdimport jdk.nashorn.internal.runtime.linker.NashornGuards; 38168404Spjd 39168404Spjd/** 40255750Sdelphij * Instances of this class are quite ephemeral; they only exist for the duration of an invocation of 41168404Spjd * {@link ScriptObject#findSetMethod(CallSiteDescriptor, jdk.internal.dynalink.linker.LinkRequest)} and 42168404Spjd * serve as the actual encapsulation of the algorithm for creating an appropriate property setter method. 43168404Spjd */ 44168404Spjdfinal class SetMethodCreator { 45219089Spjd // See constructor parameters for description of fields 46219089Spjd private final ScriptObject sobj; 47219089Spjd private final PropertyMap map; 48219089Spjd private final FindProperty find; 49219089Spjd private final CallSiteDescriptor desc; 50219089Spjd private final Class<?> type; 51219089Spjd private final boolean explicitInstanceOfCheck; 52219089Spjd 53168404Spjd /** 54219089Spjd * Creates a new property setter method creator. 55219089Spjd * @param sobj the object for which we're creating the property setter 56219089Spjd * @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we 57219089Spjd * want to create a setter for. Can be null if the property does not yet exist on the object. 58219089Spjd * @param desc the descriptor of the call site that triggered the property setter lookup 59219089Spjd */ 60219089Spjd SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) { 61219089Spjd this.sobj = sobj; 62219089Spjd this.map = sobj.getMap(); 63219089Spjd this.find = find; 64219089Spjd this.desc = desc; 65219089Spjd this.type = desc.getMethodType().parameterType(1); 66219089Spjd this.explicitInstanceOfCheck = explicitInstanceOfCheck; 67168404Spjd 68168404Spjd } 69168404Spjd 70251631Sdelphij private String getName() { 71168404Spjd return desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 72168404Spjd } 73168404Spjd 74168404Spjd private PropertyMap getMap() { 75168404Spjd return map; 76168404Spjd } 77168404Spjd 78168404Spjd /** 79168404Spjd * Creates the actual guarded invocation that represents the dynamic setter method for the property. 80168404Spjd * @return the actual guarded invocation that represents the dynamic setter method for the property. 81219089Spjd */ 82219089Spjd GuardedInvocation createGuardedInvocation() { 83168404Spjd return createSetMethod().createGuardedInvocation(); 84168404Spjd } 85168404Spjd 86168404Spjd /** 87168404Spjd * This class encapsulates the results of looking up a setter method; it's basically a triple of a method handle, 88219089Spjd * a Property object, and flags for invocation. 89219089Spjd * 90168404Spjd */ 91219089Spjd private class SetMethod { 92219089Spjd private final MethodHandle methodHandle; 93168404Spjd private final Property property; 94168404Spjd 95168404Spjd /** 96168404Spjd * Creates a new lookup result. 97219089Spjd * @param methodHandle the actual method handle 98219089Spjd * @param property the property object. Can be null in case we're creating a new property in the global object. 99168404Spjd */ 100219089Spjd SetMethod(final MethodHandle methodHandle, final Property property) { 101219089Spjd assert methodHandle != null; 102219089Spjd this.methodHandle = methodHandle; 103219089Spjd this.property = property; 104219089Spjd } 105219089Spjd 106219089Spjd /** 107168404Spjd * Composes from its components an actual guarded invocation that represents the dynamic setter method for the property. 108168404Spjd * @return the composed guarded invocation that represents the dynamic setter method for the property. 109168404Spjd */ 110168404Spjd GuardedInvocation createGuardedInvocation() { 111168404Spjd // getGuard() and getException() either both return null, or neither does. The reason for that is that now 112168404Spjd // getGuard returns a map guard that casts its argument to ScriptObject, and if that fails, we need to 113168404Spjd // relink on ClassCastException. 114219089Spjd return new GuardedInvocation(methodHandle, NashornGuards.getGuard(sobj, property, desc, explicitInstanceOfCheck), 115168404Spjd (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class); 116168404Spjd } 117168404Spjd } 118168404Spjd 119168404Spjd private SetMethod createSetMethod() { 120168404Spjd if (find != null) { 121219089Spjd return createExistingPropertySetter(); 122219089Spjd } 123168404Spjd 124168404Spjd checkStrictCreateNewVariable(); 125168404Spjd 126168404Spjd if (sobj.isScope()) { 127168404Spjd return createGlobalPropertySetter(); 128219089Spjd } 129251631Sdelphij 130219089Spjd return createNewPropertySetter(); 131219089Spjd } 132219089Spjd 133219089Spjd private void checkStrictCreateNewVariable() { 134168404Spjd // In strict mode, assignment can not create a new variable. 135168404Spjd // See also ECMA Annex C item 4. ReferenceError is thrown. 136168404Spjd if (NashornCallSiteDescriptor.isScope(desc) && NashornCallSiteDescriptor.isStrict(desc)) { 137168404Spjd throw referenceError("not.defined", getName()); 138168404Spjd } 139219089Spjd } 140168404Spjd 141219089Spjd private SetMethod createExistingPropertySetter() { 142219089Spjd final Property property = find.getProperty(); 143168404Spjd final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); 144219089Spjd 145219089Spjd assert methodHandle != null; 146219089Spjd assert property != null; 147219089Spjd 148219089Spjd final MethodHandle boundHandle; 149219089Spjd if (!property.hasSetterFunction(find.getOwner()) && find.isInherited()) { 150219089Spjd boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength()); 151219089Spjd } else { 152219089Spjd boundHandle = methodHandle; 153219089Spjd } 154219089Spjd return new SetMethod(boundHandle, property); 155219089Spjd } 156219089Spjd 157168404Spjd private SetMethod createGlobalPropertySetter() { 158219089Spjd final ScriptObject global = Context.getGlobal(); 159219089Spjd return new SetMethod(MH.filterArguments(global.addSpill(type, getName()), 0, ScriptObject.GLOBALFILTER), null); 160219089Spjd } 161219089Spjd 162219089Spjd private SetMethod createNewPropertySetter() { 163219089Spjd final SetMethod sm = map.getFieldCount() < map.getFieldMaximum() ? createNewFieldSetter() : createNewSpillPropertySetter(); 164255750Sdelphij final PropertyListeners listeners = map.getListeners(); 165255750Sdelphij if (listeners != null) { 166168404Spjd listeners.propertyAdded(sm.property); 167219089Spjd } 168219089Spjd return sm; 169219089Spjd } 170219089Spjd 171251631Sdelphij private SetMethod createNewSetter(final Property property) { 172168404Spjd final PropertyMap oldMap = getMap(); 173168404Spjd final PropertyMap newMap = getNewMap(property); 174168404Spjd final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc); 175168404Spjd final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); 176168404Spjd 177168404Spjd //fast type specific setter 178168404Spjd final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already 179168404Spjd 180168404Spjd //slow setter, that calls ScriptObject.set with appropraite type and key name 181168404Spjd MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)]; 182168404Spjd slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.isStrict(desc)); 183168404Spjd slowSetter = MH.insertArguments(slowSetter, 1, name); 184168404Spjd slowSetter = MH.asType(slowSetter, slowSetter.type().changeParameterType(0, Object.class)); 185168404Spjd 186168404Spjd assert slowSetter.type().equals(fastSetter.type()) : "slow=" + slowSetter + " != fast=" + fastSetter; 187168404Spjd 188168404Spjd //cas map used as guard, if true that means we can do the set fast 189168404Spjd MethodHandle casMap = MH.insertArguments(ScriptObject.CAS_MAP, 1, oldMap, newMap); 190168404Spjd casMap = MH.dropArguments(casMap, 1, type); 191168404Spjd casMap = MH.asType(casMap, casMap.type().changeParameterType(0, Object.class)); 192168404Spjd final MethodHandle casGuard = MH.guardWithTest(casMap, fastSetter, slowSetter); 193168404Spjd 194168404Spjd //outermost level needs an extendable check. if object can be extended, guard is true and 195168404Spjd //we can run the cas setter. The setter goes to "nop" VOID_RETURN if false or throws an 196168404Spjd //exception if we are in strict mode and object is not extensible 197168404Spjd MethodHandle extCheck = MH.insertArguments(ScriptObject.EXTENSION_CHECK, 1, isStrict, name); 198168404Spjd extCheck = MH.asType(extCheck, extCheck.type().changeParameterType(0, Object.class)); 199168404Spjd extCheck = MH.dropArguments(extCheck, 1, type); 200168404Spjd 201168404Spjd MethodHandle nop = JSType.VOID_RETURN.methodHandle(); 202168404Spjd nop = MH.dropArguments(nop, 0, Object.class, type); 203168404Spjd 204168404Spjd return new SetMethod(MH.asType(MH.guardWithTest(extCheck, casGuard, nop), fastSetter.type()), property); 205168404Spjd } 206251631Sdelphij 207168404Spjd private SetMethod createNewFieldSetter() { 208168404Spjd return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFieldCount(), type)); 209168404Spjd } 210168404Spjd 211168404Spjd private SetMethod createNewSpillPropertySetter() { 212168404Spjd return createNewSetter(new SpillProperty(getName(), 0, getMap().getSpillLength(), type)); 213168404Spjd } 214168404Spjd 215168404Spjd private PropertyMap getNewMap(final Property property) { 216168404Spjd return getMap().addProperty(property); 217168404Spjd } 218168404Spjd} 219168404Spjd