Mon, 17 Mar 2014 18:02:00 +0530
8037400: Remove getInitialMap getters and GlobalObject interface
Reviewed-by: lagergren, jlaskey, attila
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 */
26 package jdk.nashorn.internal.runtime;
28 import java.lang.invoke.MethodHandle;
29 import java.lang.invoke.MethodHandles;
30 import java.util.concurrent.Callable;
32 import jdk.nashorn.internal.codegen.CompilerConstants;
33 import jdk.nashorn.internal.lookup.Lookup;
34 import jdk.nashorn.internal.runtime.linker.Bootstrap;
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;
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 {
58 /** User defined getter function slot. */
59 private final int getterSlot;
61 /** User defined setter function slot. */
62 private final int setterSlot;
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);
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);
72 /** Dynamic invoker for getter */
73 private static final Object INVOKE_UA_GETTER = new Object();
75 private static MethodHandle getINVOKE_UA_GETTER() {
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 }
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 }
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 }
114 private UserAccessorProperty(final UserAccessorProperty property) {
115 super(property);
116 this.getterSlot = property.getterSlot;
117 this.setterSlot = property.setterSlot;
118 }
120 /**
121 * Return getter spill slot for this UserAccessorProperty.
122 * @return getter slot
123 */
124 public int getGetterSlot() {
125 return getterSlot;
126 }
128 /**
129 * Return setter spill slot for this UserAccessorProperty.
130 * @return setter slot
131 */
132 public int getSetterSlot() {
133 return setterSlot;
134 }
136 @Override
137 protected Property copy() {
138 return new UserAccessorProperty(this);
139 }
141 @Override
142 public boolean equals(final Object other) {
143 if (!super.equals(other)) {
144 return false;
145 }
147 final UserAccessorProperty uc = (UserAccessorProperty) other;
148 return getterSlot == uc.getterSlot && setterSlot == uc.setterSlot;
149 }
151 @Override
152 public int hashCode() {
153 return super.hashCode() ^ getterSlot ^ setterSlot;
154 }
156 /*
157 * Accessors.
158 */
159 @Override
160 public int getSpillCount() {
161 return 2;
162 }
164 @Override
165 public boolean hasGetterFunction(final ScriptObject obj) {
166 return obj.getSpill(getterSlot) != null;
167 }
169 @Override
170 public boolean hasSetterFunction(final ScriptObject obj) {
171 return obj.getSpill(setterSlot) != null;
172 }
174 @Override
175 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
176 return userAccessorGetter(owner, getGetterSlot(), self);
177 }
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 }
184 @Override
185 public MethodHandle getGetter(final Class<?> type) {
186 return Lookup.filterReturnType(USER_ACCESSOR_GETTER.methodHandle(), type);
187 }
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 }
195 @Override
196 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
197 return USER_ACCESSOR_SETTER.methodHandle();
198 }
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 }
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);
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 }
224 return UNDEFINED;
225 }
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);
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 }
244 }