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.codegen;
27 
28 import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
30 
31 import java.util.LinkedHashSet;
32 import java.util.List;
33 import java.util.Set;
34 import jdk.nashorn.internal.codegen.types.Type;
35 import jdk.nashorn.internal.ir.Expression;
36 import jdk.nashorn.internal.ir.LiteralNode;
37 import jdk.nashorn.internal.runtime.JSType;
38 import jdk.nashorn.internal.runtime.Property;
39 import jdk.nashorn.internal.runtime.PropertyMap;
40 import jdk.nashorn.internal.runtime.ScriptObject;
41 import jdk.nashorn.internal.runtime.ScriptRuntime;
42 import jdk.nashorn.internal.runtime.arrays.ArrayData;
43 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
44 import jdk.nashorn.internal.scripts.JD;
45 import jdk.nashorn.internal.scripts.JO;
46 
47 /**
48  * An object creator that uses spill properties.
49  */
50 public final class SpillObjectCreator extends ObjectCreator<Expression> {
51 
52     /**
53      * Constructor
54      *
55      * @param codegen  code generator
56      * @param tuples   tuples for key, symbol, value
57      */
SpillObjectCreator(final CodeGenerator codegen, final List<MapTuple<Expression>> tuples)58     SpillObjectCreator(final CodeGenerator codegen, final List<MapTuple<Expression>> tuples) {
59         super(codegen, tuples, false, false);
60         makeMap();
61     }
62 
63     @Override
createObject(final MethodEmitter method)64     public void createObject(final MethodEmitter method) {
65         assert !isScope() : "spill scope objects are not currently supported";
66 
67         final int          length        = tuples.size();
68         final boolean      dualFields    = codegen.useDualFields();
69         final int          spillLength   = ScriptObject.spillAllocationLength(length);
70         final long[]       jpresetValues = dualFields ? new long[spillLength] : null;
71         final Object[]     opresetValues = new Object[spillLength];
72         final Class<?>     objectClass   = getAllocatorClass();
73         ArrayData          arrayData     = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY);
74 
75         // Compute constant property values
76         int pos = 0;
77         for (final MapTuple<Expression> tuple : tuples) {
78             final String     key   = tuple.key;
79             final Expression value = tuple.value;
80 
81             //this is a nop of tuple.key isn't e.g. "apply" or another special name
82             method.invalidateSpecialName(tuple.key);
83 
84             if (value != null) {
85                 final Object constantValue = LiteralNode.objectAsConstant(value);
86                 if (constantValue != LiteralNode.POSTSET_MARKER) {
87                     final Property property = propertyMap.findProperty(key);
88                     if (property != null) {
89                         // normal property key
90                         property.setType(dualFields ? JSType.unboxedFieldType(constantValue) : Object.class);
91                         final int slot = property.getSlot();
92                         if (dualFields && constantValue instanceof Number) {
93                             jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
94                         } else {
95                             opresetValues[slot] = constantValue;
96                         }
97                     } else {
98                         // array index key
99                         final long oldLength = arrayData.length();
100                         final int  index     = ArrayIndex.getArrayIndex(key);
101                         final long longIndex = ArrayIndex.toLongIndex(index);
102 
103                         assert ArrayIndex.isValidArrayIndex(index);
104 
105                         if (longIndex >= oldLength) {
106                             arrayData = arrayData.ensure(longIndex);
107                         }
108 
109                         //avoid blowing up the array if we can
110                         if (constantValue instanceof Integer) {
111                             arrayData = arrayData.set(index, ((Integer)constantValue).intValue(), false);
112                         } else if (constantValue instanceof Double) {
113                             arrayData = arrayData.set(index, ((Double)constantValue).doubleValue(), false);
114                         } else {
115                             arrayData = arrayData.set(index, constantValue, false);
116                         }
117 
118                         if (longIndex > oldLength) {
119                             arrayData = arrayData.delete(oldLength, longIndex - 1);
120                         }
121                     }
122                 }
123             }
124             pos++;
125         }
126 
127         // create object and invoke constructor
128         method._new(objectClass).dup();
129         codegen.loadConstant(propertyMap);
130 
131         // load primitive value spill array
132         if (dualFields) {
133             codegen.loadConstant(jpresetValues);
134         } else {
135             method.loadNull();
136         }
137         // load object value spill array
138         codegen.loadConstant(opresetValues);
139 
140         // instantiate the script object with spill objects
141         method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class));
142 
143         // Set prefix array data if any
144         if (arrayData.length() > 0) {
145             method.dup();
146             codegen.loadConstant(arrayData);
147             method.invoke(virtualCallNoLookup(ScriptObject.class, "setArray", void.class, ArrayData.class));
148         }
149     }
150 
151     @Override
populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end)152     public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) {
153         final int  callSiteFlags = codegen.getCallSiteFlags();
154         method.load(objectType, objectSlot);
155 
156         // set postfix values
157         for (int i = start; i < end; i++) {
158             final MapTuple<Expression> tuple = tuples.get(i);
159 
160             if (LiteralNode.isConstant(tuple.value)) {
161                 continue;
162             }
163 
164             final Property property = propertyMap.findProperty(tuple.key);
165 
166             if (property == null) {
167                 final int index = ArrayIndex.getArrayIndex(tuple.key);
168                 assert ArrayIndex.isValidArrayIndex(index);
169                 method.dup();
170                 loadIndex(method, ArrayIndex.toLongIndex(index));
171                 loadTuple(method, tuple, false);
172                 method.dynamicSetIndex(callSiteFlags);
173             } else {
174                 assert property.getKey() instanceof String; // symbol keys not yet supported in object literals
175                 method.dup();
176                 loadTuple(method, tuple, false);
177                 method.dynamicSet((String) property.getKey(), codegen.getCallSiteFlags(), false);
178             }
179         }
180     }
181 
182     @Override
makeMap()183     protected PropertyMap makeMap() {
184         assert propertyMap == null : "property map already initialized";
185         final Class<? extends ScriptObject> clazz = getAllocatorClass();
186         propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields());
187         return propertyMap;
188     }
189 
190     @Override
loadValue(final Expression expr, final Type type)191     protected void loadValue(final Expression expr, final Type type) {
192         // Use generic type in order to avoid conversion between object types
193         codegen.loadExpressionAsType(expr, Type.generic(type));
194     }
195 
196     @Override
getAllocatorClass()197     protected Class<? extends ScriptObject> getAllocatorClass() {
198         return codegen.useDualFields() ? JD.class : JO.class;
199     }
200 }
201