Tue, 22 Jan 2013 22:07:12 +0530
8006678: Avoid too many Context.getGlobal() calls
Reviewed-by: lagergren, jlaskey
attila@15 | 1 | /* |
attila@15 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
attila@15 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
attila@15 | 4 | * |
attila@15 | 5 | * This code is free software; you can redistribute it and/or modify it |
attila@15 | 6 | * under the terms of the GNU General Public License version 2 only, as |
attila@15 | 7 | * published by the Free Software Foundation. Oracle designates this |
attila@15 | 8 | * particular file as subject to the "Classpath" exception as provided |
attila@15 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
attila@15 | 10 | * |
attila@15 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
attila@15 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
attila@15 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
attila@15 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
attila@15 | 15 | * accompanied this code). |
attila@15 | 16 | * |
attila@15 | 17 | * You should have received a copy of the GNU General Public License version |
attila@15 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
attila@15 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
attila@15 | 20 | * |
attila@15 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
attila@15 | 22 | * or visit www.oracle.com if you need additional information or have any |
attila@15 | 23 | * questions. |
attila@15 | 24 | */ |
attila@15 | 25 | |
attila@15 | 26 | package jdk.nashorn.internal.runtime; |
attila@15 | 27 | |
attila@15 | 28 | import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError; |
attila@15 | 29 | import static jdk.nashorn.internal.runtime.linker.Lookup.MH; |
attila@15 | 30 | |
attila@15 | 31 | import java.lang.invoke.MethodHandle; |
attila@15 | 32 | |
attila@15 | 33 | import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator; |
attila@15 | 34 | import jdk.nashorn.internal.runtime.linker.Lookup; |
attila@15 | 35 | import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; |
attila@15 | 36 | import jdk.nashorn.internal.runtime.linker.NashornGuardedInvocation; |
attila@15 | 37 | import jdk.nashorn.internal.runtime.linker.NashornGuards; |
attila@15 | 38 | |
attila@15 | 39 | import org.dynalang.dynalink.CallSiteDescriptor; |
attila@15 | 40 | import org.dynalang.dynalink.linker.GuardedInvocation; |
attila@15 | 41 | |
attila@15 | 42 | /** |
attila@15 | 43 | * Instances of this class are quite ephemeral; they only exist for the duration of an invocation of |
attila@15 | 44 | * {@link ScriptObject#findSetMethod(CallSiteDescriptor, boolean)} and serve as the actual encapsulation of the |
attila@15 | 45 | * algorithm for creating an appropriate property setter method. |
attila@15 | 46 | */ |
attila@15 | 47 | class SetMethodCreator { |
attila@15 | 48 | // See constructor parameters for description of fields |
attila@15 | 49 | private final ScriptObject sobj; |
attila@15 | 50 | private final PropertyMap map; |
attila@15 | 51 | private final FindProperty find; |
attila@15 | 52 | private final CallSiteDescriptor desc; |
attila@15 | 53 | |
attila@15 | 54 | /** |
attila@15 | 55 | * Creates a new property setter method creator. |
attila@15 | 56 | * @param sobj the object for which we're creating the property setter |
attila@15 | 57 | * @param find a result of a {@link ScriptObject#findProperty(String, boolean)} on the object for the property we |
attila@15 | 58 | * want to create a setter for. Can be null if the property does not yet exist on the object. |
attila@15 | 59 | * @param desc the descriptor of the call site that triggered the property setter lookup |
attila@15 | 60 | */ |
attila@15 | 61 | SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc) { |
attila@15 | 62 | this.sobj = sobj; |
attila@15 | 63 | this.map = sobj.getMap(); |
attila@15 | 64 | this.find = find; |
attila@15 | 65 | this.desc = desc; |
attila@15 | 66 | } |
attila@15 | 67 | |
attila@15 | 68 | private String getName() { |
attila@15 | 69 | return desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); |
attila@15 | 70 | } |
attila@15 | 71 | |
attila@15 | 72 | private PropertyMap getMap() { |
attila@15 | 73 | return map; |
attila@15 | 74 | } |
attila@15 | 75 | |
attila@15 | 76 | /** |
attila@15 | 77 | * Creates the actual guarded invocation that represents the dynamic setter method for the property. |
attila@15 | 78 | * @return the actual guarded invocation that represents the dynamic setter method for the property. |
attila@15 | 79 | */ |
attila@15 | 80 | GuardedInvocation createGuardedInvocation() { |
attila@15 | 81 | return createSetMethod().createGuardedInvocation(); |
attila@15 | 82 | } |
attila@15 | 83 | |
attila@15 | 84 | /** |
attila@15 | 85 | * This class encapsulates the results of looking up a setter method; it's basically a triple of a method hanle, |
attila@15 | 86 | * a Property object, and flags for invocation. |
attila@15 | 87 | * |
attila@15 | 88 | */ |
attila@15 | 89 | private class SetMethod { |
attila@15 | 90 | private final MethodHandle methodHandle; |
attila@15 | 91 | private final Property property; |
hannesw@42 | 92 | private final boolean nonStrict; |
attila@15 | 93 | |
attila@15 | 94 | /** |
attila@15 | 95 | * Creates a new lookup result. |
attila@15 | 96 | * @param methodHandle the actual method handle |
attila@15 | 97 | * @param property the property object. Can be null in case we're creating a new property in the global object. |
hannesw@42 | 98 | * @param nonStrict True if an existing property with a non-strict function as property setter is discovered. |
attila@15 | 99 | */ |
hannesw@42 | 100 | SetMethod(final MethodHandle methodHandle, final Property property, final boolean nonStrict) { |
attila@15 | 101 | assert methodHandle != null; |
attila@15 | 102 | this.methodHandle = methodHandle; |
attila@15 | 103 | this.property = property; |
hannesw@42 | 104 | this.nonStrict = nonStrict; |
attila@15 | 105 | } |
attila@15 | 106 | |
attila@15 | 107 | /** |
attila@15 | 108 | * Composes from its components an actual guarded invocation that represents the dynamic setter method for the property. |
attila@15 | 109 | * @return the composed guarded invocation that represents the dynamic setter method for the property. |
attila@15 | 110 | */ |
attila@15 | 111 | GuardedInvocation createGuardedInvocation() { |
hannesw@42 | 112 | return new NashornGuardedInvocation(methodHandle, null, getGuard(), nonStrict); |
attila@15 | 113 | } |
attila@15 | 114 | |
attila@15 | 115 | private MethodHandle getGuard() { |
attila@15 | 116 | return needsNoGuard() ? null : NashornGuards.getMapGuard(getMap()); |
attila@15 | 117 | } |
attila@15 | 118 | |
attila@15 | 119 | private boolean needsNoGuard() { |
attila@15 | 120 | return NashornCallSiteDescriptor.isFastScope(desc) && |
attila@15 | 121 | (ObjectClassGenerator.OBJECT_FIELDS_ONLY || isPropertyTypeStable()); |
attila@15 | 122 | } |
attila@15 | 123 | |
attila@15 | 124 | private boolean isPropertyTypeStable() { |
attila@15 | 125 | return property == null || !property.canChangeType(); |
attila@15 | 126 | } |
attila@15 | 127 | } |
attila@15 | 128 | |
attila@15 | 129 | private SetMethod createSetMethod() { |
attila@15 | 130 | if (find != null) { |
attila@15 | 131 | return createExistingPropertySetter(); |
attila@15 | 132 | } |
attila@15 | 133 | |
attila@15 | 134 | checkStrictCreateNewVariable(); |
attila@15 | 135 | |
attila@15 | 136 | if (sobj.isScope()) { |
attila@15 | 137 | return createGlobalPropertySetter(); |
attila@15 | 138 | } |
attila@15 | 139 | |
attila@15 | 140 | return createNewPropertySetter(); |
attila@15 | 141 | } |
attila@15 | 142 | |
attila@15 | 143 | private void checkStrictCreateNewVariable() { |
attila@15 | 144 | // In strict mode, assignment can not create a new variable. |
attila@15 | 145 | // See also ECMA Annex C item 4. ReferenceError is thrown. |
attila@15 | 146 | if (NashornCallSiteDescriptor.isScope(desc) && NashornCallSiteDescriptor.isStrict(desc)) { |
sundar@44 | 147 | referenceError("not.defined", getName()); |
attila@15 | 148 | } |
attila@15 | 149 | } |
attila@15 | 150 | |
attila@15 | 151 | private SetMethod createExistingPropertySetter() { |
attila@15 | 152 | final Property property = find.getProperty(); |
attila@15 | 153 | final Class<?> type = desc.getMethodType().parameterType(1); |
attila@15 | 154 | final MethodHandle methodHandle = find.getSetter(type, NashornCallSiteDescriptor.isStrict(desc)); |
attila@15 | 155 | |
attila@15 | 156 | assert methodHandle != null; |
attila@15 | 157 | assert property != null; |
attila@15 | 158 | |
attila@15 | 159 | final MethodHandle boundHandle; |
attila@15 | 160 | if (!property.hasSetterFunction() && find.isInherited()) { |
attila@15 | 161 | boundHandle = ScriptObject.bindTo(methodHandle, find.getOwner()); |
attila@15 | 162 | } else { |
attila@15 | 163 | boundHandle = methodHandle; |
attila@15 | 164 | } |
hannesw@42 | 165 | return new SetMethod(boundHandle, property, getExistingSetterNonStrictFlag()); |
attila@15 | 166 | } |
attila@15 | 167 | |
hannesw@42 | 168 | private boolean getExistingSetterNonStrictFlag() { |
attila@15 | 169 | final ScriptFunction setter = find.getSetterFunction(); |
hannesw@42 | 170 | return setter != null && setter.isNonStrictFunction(); |
attila@15 | 171 | } |
attila@15 | 172 | |
attila@15 | 173 | private SetMethod createGlobalPropertySetter() { |
sundar@44 | 174 | final ScriptObject global = Context.getGlobalTrusted(); |
hannesw@42 | 175 | return new SetMethod(ScriptObject.bindTo(global.addSpill(getName()), global), null, false); |
attila@15 | 176 | } |
attila@15 | 177 | |
attila@15 | 178 | private SetMethod createNewPropertySetter() { |
attila@15 | 179 | final int nextEmbed = sobj.findEmbed(); |
attila@15 | 180 | final SetMethod sm; |
attila@15 | 181 | if (nextEmbed >= ScriptObject.EMBED_SIZE) { |
attila@15 | 182 | sm = createNewSpillPropertySetter(); |
attila@15 | 183 | } else { |
attila@15 | 184 | sm = createNewEmbedPropertySetter(nextEmbed); |
attila@15 | 185 | } |
attila@15 | 186 | |
attila@15 | 187 | sobj.notifyPropertyAdded(sobj, sm.property); |
attila@15 | 188 | return sm; |
attila@15 | 189 | } |
attila@15 | 190 | |
attila@15 | 191 | private SetMethod createNewSpillPropertySetter() { |
attila@15 | 192 | final int nextSpill = getMap().getSpillLength(); |
attila@15 | 193 | |
attila@15 | 194 | final Property property = createSpillProperty(nextSpill); |
hannesw@42 | 195 | return new SetMethod(createSpillMethodHandle(nextSpill, property), property, false); |
attila@15 | 196 | } |
attila@15 | 197 | |
attila@15 | 198 | private Property createSpillProperty(final int nextSpill) { |
attila@15 | 199 | final MethodHandle getter = MH.asType(MH.insertArguments(MH.arrayElementGetter(Object[].class), 1, nextSpill), Lookup.GET_OBJECT_TYPE); |
attila@15 | 200 | final MethodHandle setter = MH.asType(MH.insertArguments(MH.arrayElementSetter(Object[].class), 1, nextSpill), Lookup.SET_OBJECT_TYPE); |
attila@15 | 201 | |
attila@15 | 202 | return new SpillProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter); |
attila@15 | 203 | } |
attila@15 | 204 | |
attila@15 | 205 | private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) { |
attila@15 | 206 | final PropertyMap oldMap = getMap(); |
attila@15 | 207 | final PropertyMap newMap = getNewMap(property); |
attila@15 | 208 | |
attila@15 | 209 | final Object[] spill = sobj.spill; |
attila@15 | 210 | if (spill == null) { |
attila@15 | 211 | return MH.insertArguments(ScriptObject.SETSPILLWITHNEW, 0, desc, oldMap, newMap, nextSpill); |
attila@15 | 212 | } else if (nextSpill < spill.length) { |
attila@15 | 213 | return MH.insertArguments(ScriptObject.SETSPILL, 0, desc, oldMap, newMap, nextSpill); |
attila@15 | 214 | } else { |
attila@15 | 215 | final int newLength = (nextSpill + ScriptObject.SPILL_RATE) / ScriptObject.SPILL_RATE * ScriptObject.SPILL_RATE; |
attila@15 | 216 | return MH.insertArguments(ScriptObject.SETSPILLWITHGROW, 0, desc, oldMap, newMap, nextSpill, newLength); |
attila@15 | 217 | } |
attila@15 | 218 | } |
attila@15 | 219 | |
attila@15 | 220 | private SetMethod createNewEmbedPropertySetter(final int nextEmbed) { |
attila@15 | 221 | sobj.useEmbed(nextEmbed); |
attila@15 | 222 | final Property property = new SpillProperty(getName(), 0, nextEmbed, ScriptObject.GET_EMBED[nextEmbed], ScriptObject.SET_EMBED[nextEmbed]); |
attila@15 | 223 | //TODO specfields |
attila@15 | 224 | final MethodHandle methodHandle = MH.insertArguments(ScriptObject.SETEMBED, 0, desc, getMap(), getNewMap(property), property.getSetter(Object.class, getMap()), nextEmbed); |
hannesw@42 | 225 | return new SetMethod(methodHandle, property, false); |
attila@15 | 226 | } |
attila@15 | 227 | |
attila@15 | 228 | private PropertyMap getNewMap(Property property) { |
attila@15 | 229 | return getMap().addProperty(property); |
attila@15 | 230 | } |
attila@15 | 231 | } |