1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; 30 31 import java.lang.invoke.MethodHandle; 32 import jdk.dynalink.linker.LinkRequest; 33 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 34 import jdk.nashorn.internal.objects.Global; 35 36 /** 37 * This class represents the result from a find property search. 38 */ 39 public final class FindProperty { 40 /** Object where search began. */ 41 private final ScriptObject self; 42 43 /** Object where search finish. */ 44 private final ScriptObject prototype; 45 46 /** Found property. */ 47 private final Property property; 48 49 /** 50 * Constructor 51 * 52 * @param self script object where search began 53 * @param prototype prototype where property was found, may be {@code self} if not inherited 54 * @param property property that was search result 55 */ 56 public FindProperty(final ScriptObject self, final ScriptObject prototype, final Property property) { 57 this.self = self; 58 this.prototype = prototype; 59 this.property = property; 60 } 61 62 /** 63 * Return a copy of this FindProperty with a different property. 64 * 65 * @param newProperty the new property 66 * @return the new FindProperty instance 67 */ 68 public FindProperty replaceProperty(final Property newProperty) { 69 assert this.property.getKey().equals(newProperty.getKey()); 70 assert this.property.getSlot() == newProperty.getSlot(); 71 return new FindProperty(self, prototype, newProperty); 72 } 73 74 /** 75 * Ask for a getter that returns the given type. The type has nothing to do with the 76 * internal representation of the property. It may be an Object (boxing primitives) or 77 * a primitive (primitive fields with -Dnashorn.fields.dual=true) 78 * @see ObjectClassGenerator 79 * 80 * @param type type of getter, e.g. int.class if we want a function with {@code get()I} signature 81 * @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic 82 * @param request link request 83 * 84 * @return method handle for the getter 85 */ 86 public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) { 87 MethodHandle getter; 88 if (isValid(programPoint)) { 89 getter = property.getOptimisticGetter(type, programPoint); 90 } else { 91 getter = property.getGetter(type); 92 } 93 if (property instanceof UserAccessorProperty) { 94 getter = MH.insertArguments(getter, 1, UserAccessorProperty.getINVOKE_UA_GETTER(type, programPoint)); 95 if (isValid(programPoint) && type.isPrimitive()) { 96 getter = MH.insertArguments(getter, 1, programPoint); 97 } 98 property.setType(type); 99 return insertAccessorsGetter((UserAccessorProperty) property, request, getter); 100 } 101 return getter; 102 } 103 104 /** 105 * Ask for a setter that sets the given type. The type has nothing to do with the 106 * internal representation of the property. It may be an Object (boxing primitives) or 107 * a primitive (primitive fields with -Dnashorn.fields.dual=true) 108 * @see ObjectClassGenerator 109 * 110 * @param type type of setter, e.g. int.class if we want a function with {@code set(I)V} signature 111 * @param strict are we in strict mode 112 * @param request link request 113 * 114 * @return method handle for the getter 115 */ 116 public MethodHandle getSetter(final Class<?> type, final boolean strict, final LinkRequest request) { 117 MethodHandle setter = property.getSetter(type, getOwner().getMap()); 118 if (property instanceof UserAccessorProperty) { 119 setter = MH.insertArguments(setter, 1, UserAccessorProperty.getINVOKE_UA_SETTER(type), strict ? property.getKey() : null); 120 property.setType(type); 121 return insertAccessorsGetter((UserAccessorProperty) property, request, setter); 122 } 123 124 return setter; 125 } 126 127 // Fold an accessor getter into the method handle of a user accessor property. 128 private MethodHandle insertAccessorsGetter(final UserAccessorProperty uap, final LinkRequest request, final MethodHandle mh) { 129 MethodHandle superGetter = uap.getAccessorsGetter(); 130 if (isInherited()) { 131 superGetter = ScriptObject.addProtoFilter(superGetter, getProtoChainLength()); 132 } 133 if (request != null && !(request.getReceiver() instanceof ScriptObject)) { 134 final MethodHandle wrapFilter = Global.getPrimitiveWrapFilter(request.getReceiver()); 135 superGetter = MH.filterArguments(superGetter, 0, wrapFilter.asType(wrapFilter.type().changeReturnType(superGetter.type().parameterType(0)))); 136 } 137 superGetter = MH.asType(superGetter, superGetter.type().changeParameterType(0, Object.class)); 138 139 return MH.foldArguments(mh, superGetter); 140 } 141 142 /** 143 * Return the {@code ScriptObject} owning of the property: this means the prototype. 144 * @return owner of property 145 */ 146 public ScriptObject getOwner() { 147 return prototype; 148 } 149 150 /** 151 * Return the {@code ScriptObject} where the search started. This is usually the ScriptObject the 152 * operation was started on, except for properties found inside a 'with' statement, where it is the 153 * top-level 'with' expression object. 154 * 155 * @return the start object. 156 */ 157 public ScriptObject getSelf() { 158 return self; 159 } 160 161 /** 162 * Return the appropriate receiver for a getter. 163 * @return appropriate receiver 164 */ 165 public ScriptObject getGetterReceiver() { 166 return property != null && property instanceof UserAccessorProperty ? self : prototype; 167 } 168 169 /** 170 * Return the appropriate receiver for a setter. 171 * @return appropriate receiver 172 */ 173 public ScriptObject getSetterReceiver() { 174 return property != null && property.hasSetterFunction(prototype) ? self : prototype; 175 } 176 177 /** 178 * Return the property that was found 179 * @return property 180 */ 181 public Property getProperty() { 182 return property; 183 } 184 185 /** 186 * Check if the property found was inherited, i.e. not directly in the self 187 * @return true if inherited property 188 */ 189 public boolean isInherited() { 190 return self != prototype; 191 } 192 193 /** 194 * Check if the property found was NOT inherited, i.e. defined in the script 195 * object, rather than in the prototype 196 * @return true if not inherited 197 */ 198 public boolean isSelf() { 199 return self == prototype; 200 } 201 202 /** 203 * Check if the property is in the scope 204 * @return true if on scope 205 */ 206 public boolean isScope() { 207 return prototype.isScope(); 208 } 209 210 /** 211 * Get the property value from self as object. 212 * @return the property value 213 */ 214 public int getIntValue() { 215 return property.getIntValue(getGetterReceiver(), getOwner()); 216 } 217 /** 218 * Get the property value from self as object. 219 * @return the property value 220 */ 221 public long getLongValue() { 222 return property.getLongValue(getGetterReceiver(), getOwner()); 223 } 224 /** 225 * Get the property value from self as object. 226 * @return the property value 227 */ 228 public double getDoubleValue() { 229 return property.getDoubleValue(getGetterReceiver(), getOwner()); 230 } 231 /** 232 * Get the property value from self as object. 233 * @return the property value 234 */ 235 public Object getObjectValue() { 236 return property.getObjectValue(getGetterReceiver(), getOwner()); 237 } 238 239 /** 240 * Set the property value in self. 241 * 242 * @param value the new value 243 * @param strict strict flag 244 */ 245 public void setValue(final int value, final boolean strict) { 246 property.setValue(getSetterReceiver(), getOwner(), value, strict); 247 } 248 249 /** 250 * Set the property value in self. 251 * 252 * @param value the new value 253 * @param strict strict flag 254 */ 255 public void setValue(final long value, final boolean strict) { 256 property.setValue(getSetterReceiver(), getOwner(), value, strict); 257 } 258 259 /** 260 * Set the property value in self. 261 * 262 * @param value the new value 263 * @param strict strict flag 264 */ 265 public void setValue(final double value, final boolean strict) { 266 property.setValue(getSetterReceiver(), getOwner(), value, strict); 267 } 268 269 /** 270 * Set the property value in self. 271 * 272 * @param value the new value 273 * @param strict strict flag 274 */ 275 public void setValue(final Object value, final boolean strict) { 276 property.setValue(getSetterReceiver(), getOwner(), value, strict); 277 } 278 279 /** 280 * Get the number of objects in the prototype chain between the {@code self} and the 281 * {@code owner} objects. 282 * @return the prototype chain length 283 */ 284 int getProtoChainLength() { 285 assert self != null; 286 int length = 0; 287 for (ScriptObject obj = self; obj != prototype; obj = obj.getProto()) { 288 assert !(obj instanceof WithObject); 289 ++length; 290 } 291 return length; 292 } 293 294 @Override 295 public String toString() { 296 return "[FindProperty: " + property.getKey() + ']'; 297 } 298 299 }