UserAccessorProperty.java revision 953:221a84ef44c0
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 26package jdk.nashorn.internal.runtime; 27 28import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 29import static jdk.nashorn.internal.lookup.Lookup.MH; 30import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 31import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC; 32import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex; 33import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 34 35import java.lang.invoke.MethodHandle; 36import java.lang.invoke.MethodHandles; 37import java.util.concurrent.Callable; 38import jdk.nashorn.internal.codegen.CompilerConstants; 39import jdk.nashorn.internal.lookup.Lookup; 40import jdk.nashorn.internal.runtime.linker.Bootstrap; 41 42/** 43 * Property with user defined getters/setters. Actual getter and setter 44 * functions are stored in underlying ScriptObject. Only the 'slot' info is 45 * stored in the property. 46 * 47 * The slots here denote either ScriptObject embed field number or spill 48 * array index. For spill array index, we use slot value of 49 * (index + ScriptObject.embedSize). See also ScriptObject.getEmbedOrSpill 50 * method. Negative slot value means that the corresponding getter or setter 51 * is null. Note that always two slots are allocated in ScriptObject - but 52 * negative (less by 1) slot number is stored for null getter or setter. 53 * This is done so that when the property is redefined with a different 54 * getter and setter (say, both non-null), we'll have spill slots to store 55 * those. When a slot is negative, (-slot - 1) is the embed/spill index. 56 */ 57public final class UserAccessorProperty extends SpillProperty { 58 59 private static final long serialVersionUID = -5928687246526840321L; 60 61 static class Accessors { 62 Object getter; 63 Object setter; 64 65 Accessors(final Object getter, final Object setter) { 66 set(getter, setter); 67 } 68 69 final void set(final Object getter, final Object setter) { 70 this.getter = getter; 71 this.setter = setter; 72 } 73 74 @Override 75 public String toString() { 76 return "[getter=" + getter + " setter=" + setter + ']'; 77 } 78 } 79 80 /** Getter method handle */ 81 private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, 82 "userAccessorGetter", Object.class, Accessors.class, Object.class); 83 84 /** Setter method handle */ 85 private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, 86 "userAccessorSetter", void.class, Accessors.class, String.class, Object.class, Object.class); 87 88 /** Dynamic invoker for getter */ 89 private static final Object INVOKE_UA_GETTER = new Object(); 90 91 private static MethodHandle getINVOKE_UA_GETTER() { 92 93 return Context.getGlobal().getDynamicInvoker(INVOKE_UA_GETTER, 94 new Callable<MethodHandle>() { 95 @Override 96 public MethodHandle call() { 97 return Bootstrap.createDynamicInvoker("dyn:call", Object.class, 98 Object.class, Object.class); 99 } 100 }); 101 } 102 103 /** Dynamic invoker for setter */ 104 private static Object INVOKE_UA_SETTER = new Object(); 105 106 private static MethodHandle getINVOKE_UA_SETTER() { 107 return Context.getGlobal().getDynamicInvoker(INVOKE_UA_SETTER, 108 new Callable<MethodHandle>() { 109 @Override 110 public MethodHandle call() { 111 return Bootstrap.createDynamicInvoker("dyn:call", void.class, 112 Object.class, Object.class, Object.class); 113 } 114 }); 115 } 116 117 /** 118 * Constructor 119 * 120 * @param key property key 121 * @param flags property flags 122 * @param getterSlot getter slot, starting at first embed 123 * @param setterSlot setter slot, starting at first embed 124 */ 125 UserAccessorProperty(final String key, final int flags, final int slot) { 126 super(key, flags, slot); 127 } 128 129 private UserAccessorProperty(final UserAccessorProperty property) { 130 super(property); 131 } 132 133 private UserAccessorProperty(final UserAccessorProperty property, final Class<?> newType) { 134 super(property, newType); 135 } 136 137 @Override 138 public Property copy() { 139 return new UserAccessorProperty(this); 140 } 141 142 @Override 143 public Property copy(final Class<?> newType) { 144 return new UserAccessorProperty(this, newType); 145 } 146 147 void setAccessors(final ScriptObject sobj, final PropertyMap map, final Accessors gs) { 148 try { 149 //invoke the getter and find out 150 super.getSetter(Object.class, map).invokeExact((Object)sobj, (Object)gs); 151 } catch (final Error | RuntimeException t) { 152 throw t; 153 } catch (final Throwable t) { 154 throw new RuntimeException(t); 155 } 156 } 157 158 //pick the getter setter out of the correct spill slot in sobj 159 Accessors getAccessors(final ScriptObject sobj) { 160 try { 161 //invoke the super getter with this spill slot 162 //get the getter setter from the correct spill slot 163 final Object gs = super.getGetter(Object.class).invokeExact((Object)sobj); 164 return (Accessors)gs; 165 } catch (final Error | RuntimeException t) { 166 throw t; 167 } catch (final Throwable t) { 168 throw new RuntimeException(t); 169 } 170 } 171 172 @Override 173 public Class<?> getCurrentType() { 174 return Object.class; 175 } 176 177 @Override 178 public boolean hasGetterFunction(final ScriptObject sobj) { 179 return getAccessors(sobj).getter != null; 180 } 181 182 @Override 183 public boolean hasSetterFunction(final ScriptObject sobj) { 184 return getAccessors(sobj).setter != null; 185 } 186 187 @Override 188 public int getIntValue(final ScriptObject self, final ScriptObject owner) { 189 return (int)getObjectValue(self, owner); 190 } 191 192 @Override 193 public long getLongValue(final ScriptObject self, final ScriptObject owner) { 194 return (long)getObjectValue(self, owner); 195 } 196 197 @Override 198 public double getDoubleValue(final ScriptObject self, final ScriptObject owner) { 199 return (double)getObjectValue(self, owner); 200 } 201 202 @Override 203 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { 204 return userAccessorGetter(getAccessors((owner != null) ? owner : self), self); 205 } 206 207 @Override 208 public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) { 209 setValue(self, owner, value, strict); 210 } 211 212 @Override 213 public void setValue(final ScriptObject self, final ScriptObject owner, final long value, final boolean strict) { 214 setValue(self, owner, value, strict); 215 } 216 217 @Override 218 public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) { 219 setValue(self, owner, value, strict); 220 } 221 222 @Override 223 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { 224 userAccessorSetter(getAccessors((owner != null) ? owner : self), strict ? getKey() : null, self, value); 225 } 226 227 @Override 228 public MethodHandle getGetter(final Class<?> type) { 229 //this returns a getter on the format (Accessors, Object receiver) 230 return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); 231 } 232 233 @Override 234 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { 235 //fortype is always object, but in the optimistic world we have to throw 236 //unwarranted optimism exception for narrower types. We can improve this 237 //by checking for boxed types and unboxing them, but it is doubtful that 238 //this gives us any performance, as UserAccessorProperties are typically not 239 //primitives. Are there? TODO: investigate later. For now we just throw an 240 //exception for narrower types than object 241 242 if (type.isPrimitive()) { 243 final MethodHandle getter = getGetter(Object.class); 244 final MethodHandle mh = 245 MH.asType( 246 MH.filterReturnValue( 247 getter, 248 MH.insertArguments( 249 CONVERT_OBJECT_OPTIMISTIC.get(getAccessorTypeIndex(type)), 250 1, 251 programPoint)), 252 getter.type().changeReturnType(type)); 253 254 return mh; 255 } 256 257 assert type == Object.class; 258 return getGetter(type); 259 } 260 261 @Override 262 void initMethodHandles(final Class<?> structure) { 263 throw new UnsupportedOperationException(); 264 } 265 266 @Override 267 public ScriptFunction getGetterFunction(final ScriptObject sobj) { 268 final Object value = getAccessors(sobj).getter; 269 return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; 270 } 271 272 @Override 273 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 274 return USER_ACCESSOR_SETTER.methodHandle(); 275 } 276 277 @Override 278 public ScriptFunction getSetterFunction(final ScriptObject sobj) { 279 final Object value = getAccessors(sobj).setter; 280 return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; 281 } 282 283 // User defined getter and setter are always called by "dyn:call". Note that the user 284 // getter/setter may be inherited. If so, proto is bound during lookup. In either 285 // inherited or self case, slot is also bound during lookup. Actual ScriptFunction 286 // to be called is retrieved everytime and applied. 287 static Object userAccessorGetter(final Accessors gs, final Object self) { 288 final Object func = gs.getter; 289 if (func instanceof ScriptFunction) { 290 try { 291 return getINVOKE_UA_GETTER().invokeExact(func, self); 292 } catch (final Error | RuntimeException t) { 293 throw t; 294 } catch (final Throwable t) { 295 throw new RuntimeException(t); 296 } 297 } 298 299 return UNDEFINED; 300 } 301 302 static void userAccessorSetter(final Accessors gs, final String name, final Object self, final Object value) { 303 final Object func = gs.setter; 304 if (func instanceof ScriptFunction) { 305 try { 306 getINVOKE_UA_SETTER().invokeExact(func, self, value); 307 } catch (final Error | RuntimeException t) { 308 throw t; 309 } catch (final Throwable t) { 310 throw new RuntimeException(t); 311 } 312 } else if (name != null) { 313 throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 314 } 315 } 316 317} 318