Thu, 24 May 2018 16:39:31 +0800
Merge
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package jdk.nashorn.internal.runtime; |
attila@963 | 27 | import static jdk.nashorn.internal.lookup.Lookup.MH; |
attila@962 | 28 | import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
attila@962 | 29 | import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
hannesw@1074 | 30 | import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; |
hannesw@1074 | 31 | import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; |
attila@962 | 32 | |
aoqi@0 | 33 | import java.lang.invoke.MethodHandle; |
aoqi@0 | 34 | import java.lang.invoke.MethodHandles; |
hannesw@1006 | 35 | import java.lang.invoke.MethodType; |
attila@1517 | 36 | import java.util.concurrent.Callable; |
aoqi@0 | 37 | import jdk.nashorn.internal.lookup.Lookup; |
aoqi@0 | 38 | import jdk.nashorn.internal.runtime.linker.Bootstrap; |
hannesw@1074 | 39 | import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; |
aoqi@0 | 40 | |
aoqi@0 | 41 | /** |
aoqi@0 | 42 | * Property with user defined getters/setters. Actual getter and setter |
aoqi@0 | 43 | * functions are stored in underlying ScriptObject. Only the 'slot' info is |
aoqi@0 | 44 | * stored in the property. |
aoqi@0 | 45 | */ |
attila@963 | 46 | public final class UserAccessorProperty extends SpillProperty { |
aoqi@0 | 47 | |
attila@963 | 48 | private static final long serialVersionUID = -5928687246526840321L; |
aoqi@0 | 49 | |
hannesw@1006 | 50 | static final class Accessors { |
attila@963 | 51 | Object getter; |
attila@963 | 52 | Object setter; |
attila@963 | 53 | |
attila@963 | 54 | Accessors(final Object getter, final Object setter) { |
attila@963 | 55 | set(getter, setter); |
attila@963 | 56 | } |
attila@963 | 57 | |
attila@963 | 58 | final void set(final Object getter, final Object setter) { |
attila@963 | 59 | this.getter = getter; |
attila@963 | 60 | this.setter = setter; |
attila@963 | 61 | } |
attila@963 | 62 | |
attila@963 | 63 | @Override |
attila@963 | 64 | public String toString() { |
attila@963 | 65 | return "[getter=" + getter + " setter=" + setter + ']'; |
attila@963 | 66 | } |
attila@963 | 67 | } |
hannesw@828 | 68 | |
hannesw@1006 | 69 | private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); |
aoqi@0 | 70 | |
aoqi@0 | 71 | /** Getter method handle */ |
hannesw@1074 | 72 | private final static MethodHandle INVOKE_OBJECT_GETTER = findOwnMH_S("invokeObjectGetter", Object.class, Accessors.class, MethodHandle.class, Object.class); |
hannesw@1074 | 73 | private final static MethodHandle INVOKE_INT_GETTER = findOwnMH_S("invokeIntGetter", int.class, Accessors.class, MethodHandle.class, int.class, Object.class); |
hannesw@1074 | 74 | private final static MethodHandle INVOKE_NUMBER_GETTER = findOwnMH_S("invokeNumberGetter", double.class, Accessors.class, MethodHandle.class, int.class, Object.class); |
aoqi@0 | 75 | |
aoqi@0 | 76 | /** Setter method handle */ |
hannesw@1074 | 77 | private final static MethodHandle INVOKE_OBJECT_SETTER = findOwnMH_S("invokeObjectSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, Object.class); |
hannesw@1074 | 78 | private final static MethodHandle INVOKE_INT_SETTER = findOwnMH_S("invokeIntSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, int.class); |
hannesw@1074 | 79 | private final static MethodHandle INVOKE_NUMBER_SETTER = findOwnMH_S("invokeNumberSetter", void.class, Accessors.class, MethodHandle.class, String.class, Object.class, double.class); |
aoqi@0 | 80 | |
attila@1517 | 81 | private static final Object OBJECT_GETTER_INVOKER_KEY = new Object(); |
attila@1517 | 82 | private static MethodHandle getObjectGetterInvoker() { |
attila@1517 | 83 | return Context.getGlobal().getDynamicInvoker(OBJECT_GETTER_INVOKER_KEY, new Callable<MethodHandle>() { |
attila@1517 | 84 | @Override |
attila@1517 | 85 | public MethodHandle call() throws Exception { |
attila@1517 | 86 | return getINVOKE_UA_GETTER(Object.class, INVALID_PROGRAM_POINT); |
attila@1517 | 87 | } |
attila@1517 | 88 | }); |
attila@1517 | 89 | } |
aoqi@0 | 90 | |
hannesw@1074 | 91 | static MethodHandle getINVOKE_UA_GETTER(final Class<?> returnType, final int programPoint) { |
hannesw@1074 | 92 | if (UnwarrantedOptimismException.isValid(programPoint)) { |
hannesw@1074 | 93 | final int flags = NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC | programPoint << CALLSITE_PROGRAM_POINT_SHIFT; |
hannesw@1074 | 94 | return Bootstrap.createDynamicInvoker("dyn:call", flags, returnType, Object.class, Object.class); |
hannesw@1074 | 95 | } else { |
hannesw@1074 | 96 | return Bootstrap.createDynamicInvoker("dyn:call", Object.class, Object.class, Object.class); |
hannesw@1074 | 97 | } |
aoqi@0 | 98 | } |
aoqi@0 | 99 | |
attila@1517 | 100 | private static final Object OBJECT_SETTER_INVOKER_KEY = new Object(); |
attila@1517 | 101 | private static MethodHandle getObjectSetterInvoker() { |
attila@1517 | 102 | return Context.getGlobal().getDynamicInvoker(OBJECT_SETTER_INVOKER_KEY, new Callable<MethodHandle>() { |
attila@1517 | 103 | @Override |
attila@1517 | 104 | public MethodHandle call() throws Exception { |
attila@1517 | 105 | return getINVOKE_UA_SETTER(Object.class); |
attila@1517 | 106 | } |
attila@1517 | 107 | }); |
attila@1517 | 108 | } |
attila@1517 | 109 | |
hannesw@1074 | 110 | static MethodHandle getINVOKE_UA_SETTER(final Class<?> valueType) { |
hannesw@1074 | 111 | return Bootstrap.createDynamicInvoker("dyn:call", void.class, Object.class, Object.class, valueType); |
aoqi@0 | 112 | } |
aoqi@0 | 113 | |
aoqi@0 | 114 | /** |
aoqi@0 | 115 | * Constructor |
aoqi@0 | 116 | * |
attila@963 | 117 | * @param key property key |
attila@963 | 118 | * @param flags property flags |
attila@963 | 119 | * @param slot spill slot |
aoqi@0 | 120 | */ |
attila@963 | 121 | UserAccessorProperty(final String key, final int flags, final int slot) { |
attila@963 | 122 | super(key, flags, slot); |
aoqi@0 | 123 | } |
aoqi@0 | 124 | |
aoqi@0 | 125 | private UserAccessorProperty(final UserAccessorProperty property) { |
aoqi@0 | 126 | super(property); |
aoqi@0 | 127 | } |
aoqi@0 | 128 | |
attila@963 | 129 | private UserAccessorProperty(final UserAccessorProperty property, final Class<?> newType) { |
attila@963 | 130 | super(property, newType); |
aoqi@0 | 131 | } |
aoqi@0 | 132 | |
aoqi@0 | 133 | @Override |
attila@963 | 134 | public Property copy() { |
aoqi@0 | 135 | return new UserAccessorProperty(this); |
aoqi@0 | 136 | } |
aoqi@0 | 137 | |
aoqi@0 | 138 | @Override |
attila@963 | 139 | public Property copy(final Class<?> newType) { |
attila@963 | 140 | return new UserAccessorProperty(this, newType); |
attila@963 | 141 | } |
attila@963 | 142 | |
attila@963 | 143 | void setAccessors(final ScriptObject sobj, final PropertyMap map, final Accessors gs) { |
attila@963 | 144 | try { |
attila@963 | 145 | //invoke the getter and find out |
attila@963 | 146 | super.getSetter(Object.class, map).invokeExact((Object)sobj, (Object)gs); |
attila@963 | 147 | } catch (final Error | RuntimeException t) { |
attila@963 | 148 | throw t; |
attila@963 | 149 | } catch (final Throwable t) { |
attila@963 | 150 | throw new RuntimeException(t); |
aoqi@0 | 151 | } |
attila@963 | 152 | } |
aoqi@0 | 153 | |
attila@963 | 154 | //pick the getter setter out of the correct spill slot in sobj |
attila@963 | 155 | Accessors getAccessors(final ScriptObject sobj) { |
attila@963 | 156 | try { |
attila@963 | 157 | //invoke the super getter with this spill slot |
attila@963 | 158 | //get the getter setter from the correct spill slot |
attila@963 | 159 | final Object gs = super.getGetter(Object.class).invokeExact((Object)sobj); |
attila@963 | 160 | return (Accessors)gs; |
attila@963 | 161 | } catch (final Error | RuntimeException t) { |
attila@963 | 162 | throw t; |
attila@963 | 163 | } catch (final Throwable t) { |
attila@963 | 164 | throw new RuntimeException(t); |
attila@963 | 165 | } |
aoqi@0 | 166 | } |
aoqi@0 | 167 | |
aoqi@0 | 168 | @Override |
hannesw@1074 | 169 | protected Class<?> getLocalType() { |
attila@963 | 170 | return Object.class; |
aoqi@0 | 171 | } |
aoqi@0 | 172 | |
aoqi@0 | 173 | @Override |
attila@963 | 174 | public boolean hasGetterFunction(final ScriptObject sobj) { |
attila@963 | 175 | return getAccessors(sobj).getter != null; |
aoqi@0 | 176 | } |
aoqi@0 | 177 | |
aoqi@0 | 178 | @Override |
attila@963 | 179 | public boolean hasSetterFunction(final ScriptObject sobj) { |
attila@963 | 180 | return getAccessors(sobj).setter != null; |
attila@963 | 181 | } |
attila@963 | 182 | |
attila@963 | 183 | @Override |
attila@963 | 184 | public int getIntValue(final ScriptObject self, final ScriptObject owner) { |
attila@963 | 185 | return (int)getObjectValue(self, owner); |
attila@963 | 186 | } |
attila@963 | 187 | |
attila@963 | 188 | @Override |
attila@963 | 189 | public double getDoubleValue(final ScriptObject self, final ScriptObject owner) { |
attila@963 | 190 | return (double)getObjectValue(self, owner); |
aoqi@0 | 191 | } |
aoqi@0 | 192 | |
aoqi@0 | 193 | @Override |
aoqi@0 | 194 | public Object getObjectValue(final ScriptObject self, final ScriptObject owner) { |
hannesw@1074 | 195 | try { |
attila@1517 | 196 | return invokeObjectGetter(getAccessors((owner != null) ? owner : self), getObjectGetterInvoker(), self); |
hannesw@1074 | 197 | } catch (final Error | RuntimeException t) { |
hannesw@1074 | 198 | throw t; |
hannesw@1074 | 199 | } catch (final Throwable t) { |
hannesw@1074 | 200 | throw new RuntimeException(t); |
hannesw@1074 | 201 | } |
aoqi@0 | 202 | } |
aoqi@0 | 203 | |
aoqi@0 | 204 | @Override |
attila@963 | 205 | public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) { |
attila@963 | 206 | setValue(self, owner, (Object) value, strict); |
attila@963 | 207 | } |
attila@963 | 208 | |
attila@963 | 209 | @Override |
attila@963 | 210 | public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) { |
attila@963 | 211 | setValue(self, owner, (Object) value, strict); |
attila@963 | 212 | } |
attila@963 | 213 | |
attila@963 | 214 | @Override |
attila@963 | 215 | public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) { |
hannesw@1074 | 216 | try { |
attila@1517 | 217 | invokeObjectSetter(getAccessors((owner != null) ? owner : self), getObjectSetterInvoker(), strict ? getKey() : null, self, value); |
hannesw@1074 | 218 | } catch (final Error | RuntimeException t) { |
hannesw@1074 | 219 | throw t; |
hannesw@1074 | 220 | } catch (final Throwable t) { |
hannesw@1074 | 221 | throw new RuntimeException(t); |
hannesw@1074 | 222 | } |
aoqi@0 | 223 | } |
aoqi@0 | 224 | |
aoqi@0 | 225 | @Override |
aoqi@0 | 226 | public MethodHandle getGetter(final Class<?> type) { |
attila@963 | 227 | //this returns a getter on the format (Accessors, Object receiver) |
hannesw@1074 | 228 | return Lookup.filterReturnType(INVOKE_OBJECT_GETTER, type); |
hannesw@828 | 229 | } |
hannesw@828 | 230 | |
hannesw@828 | 231 | @Override |
attila@963 | 232 | public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) { |
hannesw@1074 | 233 | if (type == int.class) { |
hannesw@1074 | 234 | return INVOKE_INT_GETTER; |
hannesw@1074 | 235 | } else if (type == double.class) { |
hannesw@1074 | 236 | return INVOKE_NUMBER_GETTER; |
hannesw@1074 | 237 | } else { |
hannesw@1074 | 238 | assert type == Object.class; |
hannesw@1074 | 239 | return INVOKE_OBJECT_GETTER; |
attila@963 | 240 | } |
aoqi@0 | 241 | } |
aoqi@0 | 242 | |
aoqi@0 | 243 | @Override |
aoqi@0 | 244 | void initMethodHandles(final Class<?> structure) { |
aoqi@0 | 245 | throw new UnsupportedOperationException(); |
aoqi@0 | 246 | } |
aoqi@0 | 247 | |
aoqi@0 | 248 | @Override |
attila@963 | 249 | public ScriptFunction getGetterFunction(final ScriptObject sobj) { |
attila@963 | 250 | final Object value = getAccessors(sobj).getter; |
attila@963 | 251 | return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; |
aoqi@0 | 252 | } |
aoqi@0 | 253 | |
aoqi@0 | 254 | @Override |
aoqi@0 | 255 | public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) { |
hannesw@1074 | 256 | if (type == int.class) { |
hannesw@1074 | 257 | return INVOKE_INT_SETTER; |
hannesw@1074 | 258 | } else if (type == double.class) { |
hannesw@1074 | 259 | return INVOKE_NUMBER_SETTER; |
hannesw@1074 | 260 | } else { |
hannesw@1074 | 261 | assert type == Object.class; |
hannesw@1074 | 262 | return INVOKE_OBJECT_SETTER; |
hannesw@1074 | 263 | } |
aoqi@0 | 264 | } |
aoqi@0 | 265 | |
aoqi@0 | 266 | @Override |
attila@963 | 267 | public ScriptFunction getSetterFunction(final ScriptObject sobj) { |
attila@963 | 268 | final Object value = getAccessors(sobj).setter; |
attila@963 | 269 | return (value instanceof ScriptFunction) ? (ScriptFunction)value : null; |
hannesw@828 | 270 | } |
hannesw@828 | 271 | |
hannesw@1006 | 272 | /** |
hannesw@1006 | 273 | * Get the getter for the {@code Accessors} object. |
hannesw@1006 | 274 | * This is the the super {@code Object} type getter with {@code Accessors} return type. |
hannesw@1006 | 275 | * |
hannesw@1006 | 276 | * @return The getter handle for the Accessors |
hannesw@1006 | 277 | */ |
hannesw@1006 | 278 | MethodHandle getAccessorsGetter() { |
hannesw@1006 | 279 | return super.getGetter(Object.class).asType(MethodType.methodType(Accessors.class, Object.class)); |
aoqi@0 | 280 | } |
aoqi@0 | 281 | |
aoqi@0 | 282 | // User defined getter and setter are always called by "dyn:call". Note that the user |
aoqi@0 | 283 | // getter/setter may be inherited. If so, proto is bound during lookup. In either |
aoqi@0 | 284 | // inherited or self case, slot is also bound during lookup. Actual ScriptFunction |
aoqi@0 | 285 | // to be called is retrieved everytime and applied. |
hannesw@1074 | 286 | @SuppressWarnings("unused") |
hannesw@1074 | 287 | private static Object invokeObjectGetter(final Accessors gs, final MethodHandle invoker, final Object self) throws Throwable { |
attila@963 | 288 | final Object func = gs.getter; |
aoqi@0 | 289 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 290 | return invoker.invokeExact(func, self); |
aoqi@0 | 291 | } |
aoqi@0 | 292 | |
aoqi@0 | 293 | return UNDEFINED; |
aoqi@0 | 294 | } |
aoqi@0 | 295 | |
hannesw@1074 | 296 | @SuppressWarnings("unused") |
hannesw@1074 | 297 | private static int invokeIntGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable { |
hannesw@1074 | 298 | final Object func = gs.getter; |
hannesw@1074 | 299 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 300 | return (int) invoker.invokeExact(func, self); |
hannesw@1074 | 301 | } |
aoqi@0 | 302 | |
hannesw@1074 | 303 | throw new UnwarrantedOptimismException(UNDEFINED, programPoint); |
hannesw@1074 | 304 | } |
hannesw@1074 | 305 | |
hannesw@1074 | 306 | @SuppressWarnings("unused") |
hannesw@1074 | 307 | private static double invokeNumberGetter(final Accessors gs, final MethodHandle invoker, final int programPoint, final Object self) throws Throwable { |
hannesw@1074 | 308 | final Object func = gs.getter; |
hannesw@1074 | 309 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 310 | return (double) invoker.invokeExact(func, self); |
hannesw@1074 | 311 | } |
hannesw@1074 | 312 | |
hannesw@1074 | 313 | throw new UnwarrantedOptimismException(UNDEFINED, programPoint); |
hannesw@1074 | 314 | } |
hannesw@1074 | 315 | |
hannesw@1074 | 316 | @SuppressWarnings("unused") |
hannesw@1074 | 317 | private static void invokeObjectSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final Object value) throws Throwable { |
attila@963 | 318 | final Object func = gs.setter; |
hannesw@828 | 319 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 320 | invoker.invokeExact(func, self, value); |
hannesw@1074 | 321 | } else if (name != null) { |
aoqi@0 | 322 | throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); |
aoqi@0 | 323 | } |
aoqi@0 | 324 | } |
aoqi@0 | 325 | |
hannesw@1074 | 326 | @SuppressWarnings("unused") |
hannesw@1074 | 327 | private static void invokeIntSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final int value) throws Throwable { |
hannesw@1074 | 328 | final Object func = gs.setter; |
hannesw@1074 | 329 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 330 | invoker.invokeExact(func, self, value); |
hannesw@1074 | 331 | } else if (name != null) { |
hannesw@1074 | 332 | throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); |
hannesw@1074 | 333 | } |
hannesw@1074 | 334 | } |
hannesw@1074 | 335 | |
hannesw@1074 | 336 | @SuppressWarnings("unused") |
hannesw@1074 | 337 | private static void invokeNumberSetter(final Accessors gs, final MethodHandle invoker, final String name, final Object self, final double value) throws Throwable { |
hannesw@1074 | 338 | final Object func = gs.setter; |
hannesw@1074 | 339 | if (func instanceof ScriptFunction) { |
hannesw@1074 | 340 | invoker.invokeExact(func, self, value); |
attila@963 | 341 | } else if (name != null) { |
hannesw@828 | 342 | throw typeError("property.has.no.setter", name, ScriptRuntime.safeToString(self)); |
hannesw@828 | 343 | } |
hannesw@828 | 344 | } |
hannesw@828 | 345 | |
hannesw@1006 | 346 | private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) { |
hannesw@1006 | 347 | return MH.findStatic(LOOKUP, UserAccessorProperty.class, name, MH.type(rtype, types)); |
hannesw@1006 | 348 | } |
hannesw@1006 | 349 | |
aoqi@0 | 350 | } |