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 static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
29 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter;
30 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter;
31 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldCount;
32 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName;
33 import static jdk.nashorn.internal.lookup.Lookup.MH;
34 import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
35 import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
36 import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes;
37 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
38 import java.io.IOException;
39 import java.io.ObjectInputStream;
40 import java.lang.invoke.MethodHandle;
41 import java.lang.invoke.MethodHandles;
42 import java.lang.invoke.SwitchPoint;
43 import java.util.function.Supplier;
44 import java.util.logging.Level;
45 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
46 import jdk.nashorn.internal.codegen.types.Type;
47 import jdk.nashorn.internal.lookup.Lookup;
48 import jdk.nashorn.internal.objects.Global;
49
50 /**
51 * An AccessorProperty is the most generic property type. An AccessorProperty is
52 * represented as fields in a ScriptObject class.
53 */
54 public class AccessorProperty extends Property {
55 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
56
57 private static final MethodHandle REPLACE_MAP = findOwnMH_S("replaceMap", Object.class, Object.class, PropertyMap.class);
58 private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, AccessorProperty.class, Object.class);
59
60 private static final int NOOF_TYPES = getNumberOfAccessorTypes();
61 private static final long serialVersionUID = 3371720170182154920L;
62
63 /**
64 * Properties in different maps for the same structure class will share their field getters and setters. This could
65 * be further extended to other method handles that are looked up in the AccessorProperty constructor, but right now
66 * these are the most frequently retrieved ones, and lookup of method handle natives only registers in the profiler
67 * for them.
68 */
69 private static ClassValue<Accessors> GETTERS_SETTERS = new ClassValue<Accessors>() {
70 @Override
71 protected Accessors computeValue(final Class<?> structure) {
72 return new Accessors(structure);
73 }
74 };
75
76 private static class Accessors {
77 final MethodHandle[] objectGetters;
78 final MethodHandle[] objectSetters;
79 final MethodHandle[] primitiveGetters;
80 final MethodHandle[] primitiveSetters;
81
82 /**
83 * Normal
84 * @param structure
85 */
86 Accessors(final Class<?> structure) {
87 final int fieldCount = getFieldCount(structure);
88 objectGetters = new MethodHandle[fieldCount];
89 objectSetters = new MethodHandle[fieldCount];
90 primitiveGetters = new MethodHandle[fieldCount];
91 primitiveSetters = new MethodHandle[fieldCount];
92
93 for (int i = 0; i < fieldCount; i++) {
94 final String fieldName = getFieldName(i, Type.OBJECT);
95 final Class<?> typeClass = Type.OBJECT.getTypeClass();
96 objectGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldName, typeClass), Lookup.GET_OBJECT_TYPE);
97 objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE);
98 }
99
100 if (!StructureLoader.isSingleFieldStructure(structure.getName())) {
101 for (int i = 0; i < fieldCount; i++) {
102 final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE);
103 final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass();
104 primitiveGetters[i] = MH.asType(MH.getter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.GET_PRIMITIVE_TYPE);
105 primitiveSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldNamePrimitive, typeClass), Lookup.SET_PRIMITIVE_TYPE);
106 }
107 }
108 }
109 }
110
111 /**
112 * Property getter cache
113 * Note that we can't do the same simple caching for optimistic getters,
114 * due to the fact that they are bound to a program point, which will
115 * produce different boun method handles wrapping the same access mechanism
116 * depending on callsite
117 */
118 private transient MethodHandle[] GETTER_CACHE = new MethodHandle[NOOF_TYPES];
119
120 /**
121 * Create a new accessor property. Factory method used by nasgen generated code.
122 *
123 * @param key {@link Property} key.
124 * @param propertyFlags {@link Property} flags.
125 * @param getter {@link Property} get accessor method.
126 * @param setter {@link Property} set accessor method.
127 *
128 * @return New {@link AccessorProperty} created.
129 */
130 public static AccessorProperty create(final String key, final int propertyFlags, final MethodHandle getter, final MethodHandle setter) {
131 return new AccessorProperty(key, propertyFlags, -1, getter, setter);
132 }
133
134 /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
135 transient MethodHandle primitiveGetter;
136
137 /** Seed setter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
138 transient MethodHandle primitiveSetter;
139
140 /** Seed getter for the Object version of this field */
141 transient MethodHandle objectGetter;
142
143 /** Seed setter for the Object version of this field */
144 transient MethodHandle objectSetter;
145
146 /**
147 * Delegate constructor for bound properties. This is used for properties created by
148 * {@link ScriptRuntime#mergeScope} and the Nashorn {@code Object.bindProperties} method.
149 * The former is used to add a script's defined globals to the current global scope while
150 * still storing them in a JO-prefixed ScriptObject class.
151 *
152 * <p>All properties created by this constructor have the {@link #IS_BOUND} flag set.</p>
153 *
154 * @param property accessor property to rebind
155 * @param delegate delegate object to rebind receiver to
156 */
157 AccessorProperty(final AccessorProperty property, final Object delegate) {
158 super(property, property.getFlags() | IS_BOUND);
159
160 this.primitiveGetter = bindTo(property.primitiveGetter, delegate);
161 this.primitiveSetter = bindTo(property.primitiveSetter, delegate);
162 this.objectGetter = bindTo(property.objectGetter, delegate);
163 this.objectSetter = bindTo(property.objectSetter, delegate);
164 property.GETTER_CACHE = new MethodHandle[NOOF_TYPES];
165 // Properties created this way are bound to a delegate
166 setType(property.getType());
167 }
168
169 /**
170 * SPILL PROPERTY or USER ACCESSOR PROPERTY abstract constructor
171 *
172 * Constructor for spill properties. Array getters and setters will be created on demand.
173 *
174 * @param key the property key
175 * @param flags the property flags
176 * @param slot spill slot
177 * @param primitiveGetter primitive getter
178 * @param primitiveSetter primitive setter
179 * @param objectGetter object getter
180 * @param objectSetter object setter
181 */
182 protected AccessorProperty(
183 final Object key,
184 final int flags,
185 final int slot,
186 final MethodHandle primitiveGetter,
187 final MethodHandle primitiveSetter,
188 final MethodHandle objectGetter,
189 final MethodHandle objectSetter) {
190 super(key, flags, slot);
191 assert getClass() != AccessorProperty.class;
192 this.primitiveGetter = primitiveGetter;
193 this.primitiveSetter = primitiveSetter;
194 this.objectGetter = objectGetter;
195 this.objectSetter = objectSetter;
196 initializeType();
197 }
198
199 /**
200 * NASGEN constructor
201 *
202 * Constructor. Similar to the constructor with both primitive getters and setters, the difference
203 * here being that only one getter and setter (setter is optional for non writable fields) is given
204 * to the constructor, and the rest are created from those. Used e.g. by Nasgen classes
205 *
206 * @param key the property key
207 * @param flags the property flags
208 * @param slot the property field number or spill slot
209 * @param getter the property getter
210 * @param setter the property setter or null if non writable, non configurable
211 */
212 private AccessorProperty(final Object key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
213 super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
214 assert !isSpill();
215
216 // we don't need to prep the setters these will never be invalidated as this is a nasgen
217 // or known type getter/setter. No invalidations will take place
218
219 final Class<?> getterType = getter.type().returnType();
220 final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
221
222 assert setterType == null || setterType == getterType;
223
224 if (getterType == int.class) {
225 primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
226 primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
227 } else if (getterType == double.class) {
228 primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE);
229 primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE);
230 } else {
231 primitiveGetter = primitiveSetter = null;
232 }
233
234 assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE;
235 assert primitiveSetter == null || primitiveSetter.type() == Lookup.SET_PRIMITIVE_TYPE : primitiveSetter;
236
237 objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
238 objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
239
240 setType(getterType);
241 }
242
243 /**
244 * Normal ACCESS PROPERTY constructor given a structure class.
245 * Constructor for dual field AccessorPropertys.
246 *
247 * @param key property key
248 * @param flags property flags
249 * @param structure structure for objects associated with this property
250 * @param slot property field number or spill slot
251 */
252 public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot) {
253 super(key, flags, slot);
254
255 initGetterSetter(structure);
256 initializeType();
257 }
258
259 private void initGetterSetter(final Class<?> structure) {
260 final int slot = getSlot();
261 /*
262 * primitiveGetter and primitiveSetter are only used in dual fields mode. Setting them to null also
263 * works in dual field mode, it only means that the property never has a primitive
264 * representation.
265 */
266
267 if (isParameter() && hasArguments()) {
268 //parameters are always stored in an object array, which may or may not be a good idea
269 final MethodHandle arguments = MH.getter(LOOKUP, structure, "arguments", ScriptObject.class);
270 objectGetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.GET_OBJECT_TYPE);
271 objectSetter = MH.asType(MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, arguments), 1, slot), Lookup.SET_OBJECT_TYPE);
272 primitiveGetter = null;
273 primitiveSetter = null;
274 } else {
275 final Accessors gs = GETTERS_SETTERS.get(structure);
276 objectGetter = gs.objectGetters[slot];
277 primitiveGetter = gs.primitiveGetters[slot];
278 objectSetter = gs.objectSetters[slot];
279 primitiveSetter = gs.primitiveSetters[slot];
280 }
281
282 // Always use dual fields except for single field structures
283 assert hasDualFields() != StructureLoader.isSingleFieldStructure(structure.getName());
284 }
285
286 /**
287 * Constructor
288 *
289 * @param key key
290 * @param flags flags
291 * @param slot field slot index
292 * @param owner owner of property
293 * @param initialValue initial value to which the property can be set
294 */
295 protected AccessorProperty(final Object key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
296 this(key, flags, owner.getClass(), slot);
297 setInitialValue(owner, initialValue);
298 }
299
300 /**
301 * Normal access property constructor that overrides the type
302 * Override the initial type. Used for Object Literals
303 *
304 * @param key key
305 * @param flags flags
306 * @param structure structure to JO subclass
307 * @param slot field slot index
308 * @param initialType initial type of the property
309 */
310 public AccessorProperty(final Object key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
311 this(key, flags, structure, slot);
312 setType(hasDualFields() ? initialType : Object.class);
313 }
314
315 /**
316 * Copy constructor that may change type and in that case clear the cache. Important to do that before
317 * type change or getters will be created already stale.
318 *
319 * @param property property
320 * @param newType new type
321 */
322 protected AccessorProperty(final AccessorProperty property, final Class<?> newType) {
323 super(property, property.getFlags());
324
325 this.GETTER_CACHE = newType != property.getLocalType() ? new MethodHandle[NOOF_TYPES] : property.GETTER_CACHE;
326 this.primitiveGetter = property.primitiveGetter;
327 this.primitiveSetter = property.primitiveSetter;
328 this.objectGetter = property.objectGetter;
329 this.objectSetter = property.objectSetter;
330
331 setType(newType);
332 }
333
334 /**
335 * COPY constructor
336 *
337 * @param property source property
338 */
339 protected AccessorProperty(final AccessorProperty property) {
340 this(property, property.getLocalType());
341 }
342
343 /**
344 * Set initial value of a script object's property
345 * @param owner owner
346 * @param initialValue initial value
347 */
348 protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
349 setType(hasDualFields() ? JSType.unboxedFieldType(initialValue) : Object.class);
350 if (initialValue instanceof Integer) {
351 invokeSetter(owner, ((Integer)initialValue).intValue());
352 } else if (initialValue instanceof Long) {
353 invokeSetter(owner, ((Long)initialValue).longValue());
354 } else if (initialValue instanceof Double) {
355 invokeSetter(owner, ((Double)initialValue).doubleValue());
356 } else {
357 invokeSetter(owner, initialValue);
358 }
359 }
360
361 /**
362 * Initialize the type of a property
363 */
364 protected final void initializeType() {
365 setType(!hasDualFields() ? Object.class : null);
366 }
367
368 private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
369 s.defaultReadObject();
370 // Restore getters array
371 GETTER_CACHE = new MethodHandle[NOOF_TYPES];
372 }
373
374 private static MethodHandle bindTo(final MethodHandle mh, final Object receiver) {
375 if (mh == null) {
376 return null;
377 }
378
379 return MH.dropArguments(MH.bindTo(mh, receiver), 0, Object.class);
380 }
381
382 @Override
383 public Property copy() {
384 return new AccessorProperty(this);
385 }
386
387 @Override
388 public Property copy(final Class<?> newType) {
389 return new AccessorProperty(this, newType);
390 }
391
392 @Override
393 public int getIntValue(final ScriptObject self, final ScriptObject owner) {
394 try {
395 return (int)getGetter(int.class).invokeExact((Object)self);
396 } catch (final Error | RuntimeException e) {
397 throw e;
398 } catch (final Throwable e) {
399 throw new RuntimeException(e);
400 }
401 }
402
403 @Override
404 public double getDoubleValue(final ScriptObject self, final ScriptObject owner) {
405 try {
406 return (double)getGetter(double.class).invokeExact((Object)self);
407 } catch (final Error | RuntimeException e) {
408 throw e;
409 } catch (final Throwable e) {
410 throw new RuntimeException(e);
411 }
412 }
413
414 @Override
415 public Object getObjectValue(final ScriptObject self, final ScriptObject owner) {
416 try {
417 return getGetter(Object.class).invokeExact((Object)self);
418 } catch (final Error | RuntimeException e) {
419 throw e;
420 } catch (final Throwable e) {
421 throw new RuntimeException(e);
422 }
423 }
424
425 /**
426 * Invoke setter for this property with a value
427 * @param self owner
428 * @param value value
429 */
430 protected final void invokeSetter(final ScriptObject self, final int value) {
431 try {
432 getSetter(int.class, self.getMap()).invokeExact((Object)self, value);
433 } catch (final Error | RuntimeException e) {
434 throw e;
435 } catch (final Throwable e) {
436 throw new RuntimeException(e);
437 }
438 }
439
440 /**
441 * Invoke setter for this property with a value
442 * @param self owner
443 * @param value value
444 */
445 protected final void invokeSetter(final ScriptObject self, final double value) {
446 try {
447 getSetter(double.class, self.getMap()).invokeExact((Object)self, value);
448 } catch (final Error | RuntimeException e) {
449 throw e;
450 } catch (final Throwable e) {
451 throw new RuntimeException(e);
452 }
453 }
454
455 /**
456 * Invoke setter for this property with a value
457 * @param self owner
458 * @param value value
459 */
460 protected final void invokeSetter(final ScriptObject self, final Object value) {
461 try {
462 getSetter(Object.class, self.getMap()).invokeExact((Object)self, value);
463 } catch (final Error | RuntimeException e) {
464 throw e;
465 } catch (final Throwable e) {
466 throw new RuntimeException(e);
467 }
468 }
469
470 @Override
471 public void setValue(final ScriptObject self, final ScriptObject owner, final int value, final boolean strict) {
472 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
473 invokeSetter(self, value);
474 }
475
476 @Override
477 public void setValue(final ScriptObject self, final ScriptObject owner, final double value, final boolean strict) {
478 assert isConfigurable() || isWritable() : getKey() + " is not writable or configurable";
479 invokeSetter(self, value);
480 }
481
482 @Override
483 public void setValue(final ScriptObject self, final ScriptObject owner, final Object value, final boolean strict) {
484 //this is sometimes used for bootstrapping, hence no assert. ugly.
485 invokeSetter(self, value);
486 }
487
488 @Override
489 void initMethodHandles(final Class<?> structure) {
490 // sanity check for structure class
491 if (!ScriptObject.class.isAssignableFrom(structure) || !StructureLoader.isStructureClass(structure.getName())) {
492 throw new IllegalArgumentException();
493 }
494 // this method is overridden in SpillProperty
495 assert !isSpill();
496 initGetterSetter(structure);
497 }
498
499 @Override
500 public MethodHandle getGetter(final Class<?> type) {
501 final int i = getAccessorTypeIndex(type);
502
503 assert type == int.class ||
504 type == double.class ||
505 type == Object.class :
506 "invalid getter type " + type + " for " + getKey();
507
508 checkUndeclared();
509
510 //all this does is add a return value filter for object fields only
511 final MethodHandle[] getterCache = GETTER_CACHE;
512 final MethodHandle cachedGetter = getterCache[i];
513 final MethodHandle getter;
514 if (cachedGetter != null) {
515 getter = cachedGetter;
516 } else {
517 getter = debug(
518 createGetter(
519 getLocalType(),
520 type,
521 primitiveGetter,
522 objectGetter,
523 INVALID_PROGRAM_POINT),
524 getLocalType(),
525 type,
526 "get");
527 getterCache[i] = getter;
528 }
529 assert getter.type().returnType() == type && getter.type().parameterType(0) == Object.class;
530 return getter;
531 }
532
533 @Override
534 public MethodHandle getOptimisticGetter(final Class<?> type, final int programPoint) {
535 // nasgen generated primitive fields like Math.PI have only one known unchangeable primitive type
536 if (objectGetter == null) {
537 return getOptimisticPrimitiveGetter(type, programPoint);
538 }
539
540 checkUndeclared();
541
542 return debug(
543 createGetter(
544 getLocalType(),
545 type,
546 primitiveGetter,
547 objectGetter,
548 programPoint),
549 getLocalType(),
550 type,
551 "get");
552 }
553
554 private MethodHandle getOptimisticPrimitiveGetter(final Class<?> type, final int programPoint) {
555 final MethodHandle g = getGetter(getLocalType());
556 return MH.asType(OptimisticReturnFilters.filterOptimisticReturnValue(g, type, programPoint), g.type().changeReturnType(type));
557 }
558
559 private Property getWiderProperty(final Class<?> type) {
560 return copy(type); //invalidate cache of new property
561 }
562
563 private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
564 final PropertyMap newMap = oldMap.replaceProperty(this, newProperty);
565 assert oldMap.size() > 0;
566 assert newMap.size() == oldMap.size();
567 return newMap;
568 }
569
570 private void checkUndeclared() {
571 if ((getFlags() & NEEDS_DECLARATION) != 0) {
572 // a lexically defined variable that hasn't seen its declaration - throw ReferenceError
573 throw ECMAErrors.referenceError("not.defined", getKey().toString());
574 }
575 }
576
577 // the final three arguments are for debug printout purposes only
578 @SuppressWarnings("unused")
579 private static Object replaceMap(final Object sobj, final PropertyMap newMap) {
580 ((ScriptObject)sobj).setMap(newMap);
581 return sobj;
582 }
583
584 @SuppressWarnings("unused")
585 private static Object invalidateSwitchPoint(final AccessorProperty property, final Object obj) {
586 if (!property.builtinSwitchPoint.hasBeenInvalidated()) {
587 SwitchPoint.invalidateAll(new SwitchPoint[] { property.builtinSwitchPoint });
588 }
589 return obj;
590 }
591
592 private MethodHandle generateSetter(final Class<?> forType, final Class<?> type) {
593 return debug(createSetter(forType, type, primitiveSetter, objectSetter), getLocalType(), type, "set");
594 }
595
596 /**
597 * Is this property of the undefined type?
598 * @return true if undefined
599 */
600 protected final boolean isUndefined() {
601 return getLocalType() == null;
602 }
603
604 @Override
605 public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
606 checkUndeclared();
607
608 final int typeIndex = getAccessorTypeIndex(type);
609 final int currentTypeIndex = getAccessorTypeIndex(getLocalType());
610
611 //if we are asking for an object setter, but are still a primitive type, we might try to box it
612 MethodHandle mh;
613 if (needsInvalidator(typeIndex, currentTypeIndex)) {
614 final Property newProperty = getWiderProperty(type);
615 final PropertyMap newMap = getWiderMap(currentMap, newProperty);
616
617 final MethodHandle widerSetter = newProperty.getSetter(type, newMap);
618 final Class<?> ct = getLocalType();
619 mh = MH.filterArguments(widerSetter, 0, MH.insertArguments(debugReplace(ct, type, currentMap, newMap) , 1, newMap));
620 if (ct != null && ct.isPrimitive() && !type.isPrimitive()) {
621 mh = ObjectClassGenerator.createGuardBoxedPrimitiveSetter(ct, generateSetter(ct, ct), mh);
622 }
623 } else {
624 final Class<?> forType = isUndefined() ? type : getLocalType();
625 mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
626 }
627
628 if (isBuiltin()) {
629 mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey().toString()));
630 }
631
632 assert mh.type().returnType() == void.class : mh.type();
633
634 return mh;
635 }
636
637 @Override
638 public final boolean canChangeType() {
639 if (!hasDualFields()) {
640 return false;
641 }
642 // Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
643 return getLocalType() == null || (getLocalType() != Object.class && (isConfigurable() || isWritable()));
644 }
645
646 private boolean needsInvalidator(final int typeIndex, final int currentTypeIndex) {
647 return canChangeType() && typeIndex > currentTypeIndex;
648 }
649
650 private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
651 if (!Context.DEBUG || !Global.hasInstance()) {
652 return mh;
653 }
654
655 final Context context = Context.getContextTrusted();
656 assert context != null;
657
658 return context.addLoggingToHandle(
659 ObjectClassGenerator.class,
660 Level.INFO,
661 mh,
662 0,
663 true,
664 new Supplier<String>() {
665 @Override
666 public String get() {
667 return tag + " '" + getKey() + "' (property="+ Debug.id(this) + ", slot=" + getSlot() + " " + getClass().getSimpleName() + " forType=" + stripName(forType) + ", type=" + stripName(type) + ')';
668 }
669 });
670 }
671
672 private MethodHandle debugReplace(final Class<?> oldType, final Class<?> newType, final PropertyMap oldMap, final PropertyMap newMap) {
673 if (!Context.DEBUG || !Global.hasInstance()) {
674 return REPLACE_MAP;
675 }
676
677 final Context context = Context.getContextTrusted();
678 assert context != null;
679
680 MethodHandle mh = context.addLoggingToHandle(
681 ObjectClassGenerator.class,
682 REPLACE_MAP,
683 new Supplier<String>() {
684 @Override
685 public String get() {
686 return "Type change for '" + getKey() + "' " + oldType + "=>" + newType;
687 }
688 });
689
690 mh = context.addLoggingToHandle(
691 ObjectClassGenerator.class,
692 Level.FINEST,
693 mh,
694 Integer.MAX_VALUE,
695 false,
696 new Supplier<String>() {
697 @Override
698 public String get() {
699 return "Setting map " + Debug.id(oldMap) + " => " + Debug.id(newMap) + " " + oldMap + " => " + newMap;
700 }
701 });
702 return mh;
703 }
704
705 private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) {
706 if (!Context.DEBUG || !Global.hasInstance()) {
707 return invalidator;
708 }
709
710 final Context context = Context.getContextTrusted();
711 assert context != null;
712
713 return context.addLoggingToHandle(
714 ObjectClassGenerator.class,
715 invalidator,
716 new Supplier<String>() {
717 @Override
718 public String get() {
719 return "Field change callback for " + key + " triggered ";
720 }
721 });
722 }
723
724 private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
725 return MH.findStatic(LOOKUP, AccessorProperty.class, name, MH.type(rtype, types));
726 }
727 }
--- EOF ---