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 java.lang.invoke.MethodHandle; 29 import java.lang.invoke.MethodHandles; 30 import java.util.concurrent.Callable; 31 32 import jdk.nashorn.internal.codegen.CompilerConstants; 33 import jdk.nashorn.internal.lookup.Lookup; 34 import jdk.nashorn.internal.runtime.linker.Bootstrap; 35 36 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall; 37 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 38 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 39 40 /** 41 * Property with user defined getters/setters. Actual getter and setter 42 * functions are stored in underlying ScriptObject. Only the 'slot' info is 43 * stored in the property. 44 * 45 * The slots here denote either ScriptObject embed field number or spill 46 * array index. For spill array index, we use slot value of 47 * (index + ScriptObject.embedSize). See also ScriptObject.getEmbedOrSpill 48 * method. Negative slot value means that the corresponding getter or setter 49 * is null. Note that always two slots are allocated in ScriptObject - but 50 * negative (less by 1) slot number is stored for null getter or setter. 51 * This is done so that when the property is redefined with a different 52 * getter and setter (say, both non-null), we'll have spill slots to store 53 * those. When a slot is negative, (-slot - 1) is the embed/spill index. 54 */ 55 public final class UserAccessorProperty extends Property { 56 57 /** User defined getter function slot. */ 58 private final int getterSlot; 59 60 /** User defined setter function slot. */ 61 private final int setterSlot; 62 63 /** Getter method handle */ 64 private final static CompilerConstants.Call USER_ACCESSOR_GETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, 65 "userAccessorGetter", Object.class, ScriptObject.class, int.class, Object.class); 66 67 /** Setter method handle */ 68 private final static CompilerConstants.Call USER_ACCESSOR_SETTER = staticCall(MethodHandles.lookup(), UserAccessorProperty.class, 69 "userAccessorSetter", void.class, ScriptObject.class, int.class, String.class, Object.class, Object.class); 70 71 /** Dynamic invoker for getter */ 72 private static final Object INVOKE_UA_GETTER = new Object(); 73 74 private static MethodHandle getINVOKE_UA_GETTER() { 75 76 return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(INVOKE_UA_GETTER, 77 new Callable<MethodHandle>() { 78 @Override 79 public MethodHandle call() { 80 return Bootstrap.createDynamicInvoker("dyn:call", Object.class, 81 Object.class, Object.class); 82 } 83 }); 84 } 85 86 /** Dynamic invoker for setter */ 87 private static Object INVOKE_UA_SETTER = new Object(); 88 private static MethodHandle getINVOKE_UA_SETTER() { 89 return ((GlobalObject)Context.getGlobal()).getDynamicInvoker(INVOKE_UA_SETTER, 90 new Callable<MethodHandle>() { 91 @Override 92 public MethodHandle call() { 93 return Bootstrap.createDynamicInvoker("dyn:call", void.class, 94 Object.class, Object.class, Object.class); 95 } 96 }); 97 } 98 99 /** 100 * Constructor 101 * 102 * @param key property key 103 * @param flags property flags 104 * @param getterSlot getter slot, starting at first embed 105 * @param setterSlot setter slot, starting at first embed 106 */ 107 UserAccessorProperty(final String key, final int flags, final int getterSlot, final int setterSlot) { 108 super(key, flags, -1); 109 this.getterSlot = getterSlot; 110 this.setterSlot = setterSlot; 111 } 112 113 private UserAccessorProperty(final UserAccessorProperty property) { 114 super(property); 115 this.getterSlot = property.getterSlot; 116 this.setterSlot = property.setterSlot; 117 } 118 119 /** 120 * Return getter spill slot for this UserAccessorProperty. 121 * @return getter slot 122 */ 123 public int getGetterSlot() { 124 return getterSlot; 125 } 126 127 /** 128 * Return setter spill slot for this UserAccessorProperty. 129 * @return setter slot 130 */ 131 public int getSetterSlot() { 132 return setterSlot; 133 } 134 135 @Override 136 protected Property copy() { 137 return new UserAccessorProperty(this); 138 } 139 140 @Override 141 public boolean equals(final Object other) { 142 if (!super.equals(other)) { 143 return false; 144 } 145 146 final UserAccessorProperty uc = (UserAccessorProperty) other; 147 return getterSlot == uc.getterSlot && setterSlot == uc.setterSlot; 148 } 149 150 @Override 151 public int hashCode() { 152 return super.hashCode() ^ getterSlot ^ setterSlot; 153 } 154 155 /* 156 * Accessors. 157 */ 158 @Override 159 public int getSpillCount() { 160 return 2; 161 } 162 163 @Override 164 public boolean hasGetterFunction(final ScriptObject obj) { 165 return obj.getSpill(getterSlot) != null; 166 } 167 168 @Override 169 public boolean hasSetterFunction(final ScriptObject obj) { 170 return obj.getSpill(setterSlot) != null; 171 } 172 173 @Override 174 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { 175 return userAccessorGetter(owner, getGetterSlot(), self); 176 } 177 178 @Override 179 public void setObjectValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { 180 userAccessorSetter(owner, getSetterSlot(), strict ? getKey() : null, self, value); 181 } 182 183 @Override 184 public MethodHandle getGetter(final Class<?> type) { 185 return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type); 186 } 187 188 @Override 189 public ScriptFunction getGetterFunction(final ScriptObject obj) { 190 final Object value = obj.getSpill(getterSlot); 191 return (value instanceof ScriptFunction) ? (ScriptFunction) value : null; 192 } 193 194 @Override 195 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { 196 return USER_ACCESSOR_SETTER.methodHandle(); 197 } 198 199 @Override 200 public ScriptFunction getSetterFunction(final ScriptObject obj) { 201 final Object value = obj.getSpill(setterSlot); 202 return (value instanceof ScriptFunction) ? (ScriptFunction) value : null; 203 } 204 205 // User defined getter and setter are always called by "dyn:call". Note that the user 206 // getter/setter may be inherited. If so, proto is bound during lookup. In either 207 // inherited or self case, slot is also bound during lookup. Actual ScriptFunction 208 // to be called is retrieved everytime and applied. 209 static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) { 210 final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; 211 final Object func = container.getSpill(slot); 212 213 if (func instanceof ScriptFunction) { 214 try { 215 return getINVOKE_UA_GETTER().invokeExact(func, self); 216 } catch(final Error|RuntimeException t) { 217 throw t; 218 } catch(final Throwable t) { 219 throw new RuntimeException(t); 220 } 221 } 222 223 return UNDEFINED; 224 } 225 226 static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) { 227 final ScriptObject container = (proto != null) ? proto : (ScriptObject)self; 228 final Object func = container.getSpill(slot); 229 230 if (func instanceof ScriptFunction) { 231 try { 232 getINVOKE_UA_SETTER().invokeExact(func, self, value); 233 } catch(final Error|RuntimeException t) { 234 throw t; 235 } catch(final Throwable t) { 236 throw new RuntimeException(t); 237 } 238 } else if (name != null) { 239 throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); 240 } 241 } 242 243 }