34 |
34 |
35 /** |
35 /** |
36 * Base class for object creation code generation. |
36 * Base class for object creation code generation. |
37 * @param <T> value type |
37 * @param <T> value type |
38 */ |
38 */ |
39 public abstract class ObjectCreator<T> { |
39 public abstract class ObjectCreator<T> implements CodeGenerator.SplitLiteralCreator { |
40 |
40 |
41 /** List of keys & symbols to initiate in this ObjectCreator */ |
41 /** List of keys & symbols to initiate in this ObjectCreator */ |
42 final List<MapTuple<T>> tuples; |
42 final List<MapTuple<T>> tuples; |
43 |
43 |
44 /** Code generator */ |
44 /** Code generator */ |
67 |
67 |
68 /** |
68 /** |
69 * Generate code for making the object. |
69 * Generate code for making the object. |
70 * @param method Script method. |
70 * @param method Script method. |
71 */ |
71 */ |
72 protected abstract void makeObject(final MethodEmitter method); |
72 public void makeObject(final MethodEmitter method) { |
|
73 createObject(method); |
|
74 // We need to store the object in a temporary slot as populateRange expects to load the |
|
75 // object from a slot (as it is also invoked within split methods). Note that this also |
|
76 // helps optimistic continuations to handle the stack in case an optimistic assumption |
|
77 // fails during initialization (see JDK-8079269). |
|
78 final int objectSlot = method.getUsedSlotsWithLiveTemporaries(); |
|
79 final Type objectType = method.peekType(); |
|
80 method.storeTemp(objectType, objectSlot); |
|
81 populateRange(method, objectType, objectSlot, 0, tuples.size()); |
|
82 } |
|
83 |
|
84 /** |
|
85 * Generate code for creating and initializing the object. |
|
86 * @param method the method emitter |
|
87 */ |
|
88 protected abstract void createObject(final MethodEmitter method); |
73 |
89 |
74 /** |
90 /** |
75 * Construct the property map appropriate for the object. |
91 * Construct the property map appropriate for the object. |
76 * @return the newly created property map |
92 * @return the newly created property map |
77 */ |
93 */ |
123 protected boolean hasArguments() { |
139 protected boolean hasArguments() { |
124 return hasArguments; |
140 return hasArguments; |
125 } |
141 } |
126 |
142 |
127 /** |
143 /** |
|
144 * Get the class of objects created by this ObjectCreator |
|
145 * @return class of created object |
|
146 */ |
|
147 abstract protected Class<? extends ScriptObject> getAllocatorClass(); |
|
148 |
|
149 /** |
128 * Technique for loading an initial value. Defined by anonymous subclasses in code gen. |
150 * Technique for loading an initial value. Defined by anonymous subclasses in code gen. |
129 * |
151 * |
130 * @param value Value to load. |
152 * @param value Value to load. |
131 * @param type the type of the value to load |
153 * @param type the type of the value to load |
132 */ |
154 */ |
143 } |
165 } |
144 |
166 |
145 MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) { |
167 MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple) { |
146 return loadTuple(method, tuple, true); |
168 return loadTuple(method, tuple, true); |
147 } |
169 } |
148 |
|
149 /** |
|
150 * If using optimistic typing, let the code generator realize that the newly created object on the stack |
|
151 * when DUP-ed will be the same value. Basically: {NEW, DUP, INVOKESPECIAL init, DUP} will leave a stack |
|
152 * load specification {unknown, unknown} on stack (that is "there's two values on the stack, but neither |
|
153 * comes from a known local load"). If there's an optimistic operation in the literal initializer, |
|
154 * OptimisticOperation.storeStack will allocate two temporary locals for it and store them as |
|
155 * {ASTORE 4, ASTORE 3}. If we instead do {NEW, DUP, INVOKESPECIAL init, ASTORE 3, ALOAD 3, DUP} we end up |
|
156 * with stack load specification {ALOAD 3, ALOAD 3} (as DUP can track that the value it duplicated came |
|
157 * from a local load), so if/when a continuation needs to be recreated from it, it'll be |
|
158 * able to emit ALOAD 3, ALOAD 3 to recreate the stack. If we didn't do this, deoptimization within an |
|
159 * object literal initialization could in rare cases cause an incompatible change in the shape of the |
|
160 * local variable table for the temporaries, e.g. in the following snippet where a variable is reassigned |
|
161 * to a wider type in an object initializer: |
|
162 * <code>var m = 1; var obj = {p0: m, p1: m = "foo", p2: m}</code> |
|
163 * @param method the current method emitter. |
|
164 */ |
|
165 void helpOptimisticRecognizeDuplicateIdentity(final MethodEmitter method) { |
|
166 if (codegen.useOptimisticTypes()) { |
|
167 final Type objectType = method.peekType(); |
|
168 final int tempSlot = method.defineTemporaryLocalVariable(objectType.getSlots()); |
|
169 method.storeHidden(objectType, tempSlot); |
|
170 method.load(objectType, tempSlot); |
|
171 } |
|
172 } |
|
173 } |
170 } |