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