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