Thu, 24 May 2018 16:39:31 +0800
Merge
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package jdk.nashorn.internal.codegen; |
aoqi@0 | 27 | |
aoqi@0 | 28 | import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS; |
aoqi@0 | 29 | import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup; |
aoqi@0 | 30 | import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; |
attila@963 | 31 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE; |
attila@963 | 32 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getFieldName; |
aoqi@0 | 33 | import static jdk.nashorn.internal.codegen.ObjectClassGenerator.getPaddedFieldCount; |
aoqi@0 | 34 | import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex; |
aoqi@0 | 35 | import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex; |
aoqi@0 | 36 | |
aoqi@0 | 37 | import java.util.List; |
aoqi@0 | 38 | import jdk.nashorn.internal.codegen.types.Type; |
aoqi@0 | 39 | import jdk.nashorn.internal.ir.Symbol; |
aoqi@0 | 40 | import jdk.nashorn.internal.runtime.Context; |
aoqi@0 | 41 | import jdk.nashorn.internal.runtime.JSType; |
aoqi@0 | 42 | import jdk.nashorn.internal.runtime.PropertyMap; |
aoqi@0 | 43 | import jdk.nashorn.internal.runtime.ScriptObject; |
aoqi@0 | 44 | import jdk.nashorn.internal.runtime.arrays.ArrayIndex; |
aoqi@0 | 45 | |
aoqi@0 | 46 | /** |
aoqi@0 | 47 | * Analyze an object's characteristics for appropriate code generation. This |
aoqi@0 | 48 | * is used for functions and for objects. A field object take a set of values which |
aoqi@0 | 49 | * to assign to the various fields in the object. This is done by the generated code |
aoqi@0 | 50 | * |
aoqi@0 | 51 | * @param <T> the value type for the fields being written on object creation, e.g. Node |
aoqi@0 | 52 | * @see jdk.nashorn.internal.ir.Node |
aoqi@0 | 53 | */ |
attila@963 | 54 | public abstract class FieldObjectCreator<T> extends ObjectCreator<T> { |
aoqi@0 | 55 | |
attila@963 | 56 | private String fieldObjectClassName; |
attila@963 | 57 | private Class<? extends ScriptObject> fieldObjectClass; |
attila@963 | 58 | private int fieldCount; |
attila@963 | 59 | private int paddedFieldCount; |
attila@963 | 60 | private int paramCount; |
aoqi@0 | 61 | |
aoqi@0 | 62 | /** call site flags to be used for invocations */ |
attila@963 | 63 | private final int callSiteFlags; |
attila@963 | 64 | /** are we creating this field object from 'eval' code? */ |
attila@963 | 65 | private final boolean evalCode; |
aoqi@0 | 66 | |
aoqi@0 | 67 | /** |
aoqi@0 | 68 | * Constructor |
aoqi@0 | 69 | * |
aoqi@0 | 70 | * @param codegen code generator |
hannesw@991 | 71 | * @param tuples tuples for fields in object |
aoqi@0 | 72 | */ |
attila@963 | 73 | FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples) { |
attila@963 | 74 | this(codegen, tuples, false, false); |
aoqi@0 | 75 | } |
aoqi@0 | 76 | |
aoqi@0 | 77 | /** |
aoqi@0 | 78 | * Constructor |
aoqi@0 | 79 | * |
aoqi@0 | 80 | * @param codegen code generator |
hannesw@991 | 81 | * @param tuples tuples for fields in object |
aoqi@0 | 82 | * @param isScope is this a scope object |
aoqi@0 | 83 | * @param hasArguments does the created object have an "arguments" property |
aoqi@0 | 84 | */ |
attila@963 | 85 | FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) { |
attila@963 | 86 | super(codegen, tuples, isScope, hasArguments); |
aoqi@0 | 87 | this.callSiteFlags = codegen.getCallSiteFlags(); |
attila@963 | 88 | this.evalCode = codegen.isEvalCode(); |
aoqi@0 | 89 | countFields(); |
aoqi@0 | 90 | findClass(); |
aoqi@0 | 91 | } |
aoqi@0 | 92 | |
aoqi@0 | 93 | @Override |
hannesw@1542 | 94 | public void createObject(final MethodEmitter method) { |
aoqi@0 | 95 | makeMap(); |
attila@963 | 96 | final String className = getClassName(); |
hannesw@1542 | 97 | // NOTE: we must load the actual structure class here, because the API operates with Nashorn Type objects, |
hannesw@1542 | 98 | // and Type objects need a loaded class, for better or worse. We also have to be specific and use the type |
hannesw@1542 | 99 | // of the actual structure class, we can't generalize it to e.g. Type.typeFor(ScriptObject.class) as the |
hannesw@1542 | 100 | // exact type information is needed for generating continuations in rest-of methods. If we didn't do this, |
hannesw@1542 | 101 | // object initializers like { x: arr[i] } would fail during deoptimizing compilation on arr[i], as the |
hannesw@1542 | 102 | // values restored from the RewriteException would be cast to "ScriptObject" instead of to e.g. "JO4", and |
hannesw@1542 | 103 | // subsequently the "PUTFIELD J04.L0" instruction in the continuation code would fail bytecode verification. |
hannesw@1542 | 104 | assert fieldObjectClass != null; |
hannesw@1542 | 105 | method._new(fieldObjectClass).dup(); |
hannesw@1542 | 106 | |
aoqi@0 | 107 | loadMap(method); //load the map |
aoqi@0 | 108 | |
aoqi@0 | 109 | if (isScope()) { |
aoqi@0 | 110 | loadScope(method); |
aoqi@0 | 111 | |
aoqi@0 | 112 | if (hasArguments()) { |
aoqi@0 | 113 | method.loadCompilerConstant(ARGUMENTS); |
attila@963 | 114 | method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class, ARGUMENTS.type())); |
aoqi@0 | 115 | } else { |
attila@963 | 116 | method.invoke(constructorNoLookup(className, PropertyMap.class, ScriptObject.class)); |
aoqi@0 | 117 | } |
aoqi@0 | 118 | } else { |
attila@963 | 119 | method.invoke(constructorNoLookup(className, PropertyMap.class)); |
aoqi@0 | 120 | } |
hannesw@1542 | 121 | } |
aoqi@0 | 122 | |
hannesw@1542 | 123 | @Override |
hannesw@1542 | 124 | public void populateRange(final MethodEmitter method, final Type objectType, final int objectSlot, final int start, final int end) { |
hannesw@1542 | 125 | method.load(objectType, objectSlot); |
aoqi@0 | 126 | // Set values. |
hannesw@1542 | 127 | for (int i = start; i < end; i++) { |
hannesw@1542 | 128 | final MapTuple<T> tuple = tuples.get(i); |
attila@963 | 129 | //we only load when we have both symbols and values (which can be == the symbol) |
attila@963 | 130 | //if we didn't load, we need an array property |
attila@963 | 131 | if (tuple.symbol != null && tuple.value != null) { |
attila@963 | 132 | final int index = getArrayIndex(tuple.key); |
attila@1344 | 133 | method.dup(); |
attila@963 | 134 | if (!isValidArrayIndex(index)) { |
attila@963 | 135 | putField(method, tuple.key, tuple.symbol.getFieldIndex(), tuple); |
attila@963 | 136 | } else { |
attila@963 | 137 | putSlot(method, ArrayIndex.toLongIndex(index), tuple); |
attila@963 | 138 | } |
aoqi@0 | 139 | |
attila@963 | 140 | //this is a nop of tuple.key isn't e.g. "apply" or another special name |
attila@963 | 141 | method.invalidateSpecialName(tuple.key); |
aoqi@0 | 142 | } |
aoqi@0 | 143 | } |
aoqi@0 | 144 | } |
aoqi@0 | 145 | |
aoqi@0 | 146 | @Override |
aoqi@0 | 147 | protected PropertyMap makeMap() { |
aoqi@0 | 148 | assert propertyMap == null : "property map already initialized"; |
hannesw@1330 | 149 | propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), codegen.useDualFields(), fieldCount, paddedFieldCount, evalCode); |
aoqi@0 | 150 | return propertyMap; |
aoqi@0 | 151 | } |
aoqi@0 | 152 | |
aoqi@0 | 153 | /** |
aoqi@0 | 154 | * Store a value in a field of the generated class object. |
aoqi@0 | 155 | * |
aoqi@0 | 156 | * @param method Script method. |
aoqi@0 | 157 | * @param key Property key. |
aoqi@0 | 158 | * @param fieldIndex Field number. |
hannesw@991 | 159 | * @param tuple Tuple to store. |
aoqi@0 | 160 | */ |
attila@963 | 161 | private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) { |
hannesw@1330 | 162 | final Type fieldType = codegen.useDualFields() && tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT; |
attila@963 | 163 | final String fieldClass = getClassName(); |
attila@963 | 164 | final String fieldName = getFieldName(fieldIndex, fieldType); |
attila@963 | 165 | final String fieldDesc = typeDescriptor(fieldType.getTypeClass()); |
attila@963 | 166 | |
attila@963 | 167 | assert fieldName.equals(getFieldName(fieldIndex, PRIMITIVE_FIELD_TYPE)) || fieldType.isObject() : key + " object keys must store to L*-fields"; |
attila@963 | 168 | assert fieldName.equals(getFieldName(fieldIndex, Type.OBJECT)) || fieldType.isPrimitive() : key + " primitive keys must store to J*-fields"; |
attila@963 | 169 | |
hannesw@1720 | 170 | loadTuple(method, tuple, true); |
attila@963 | 171 | method.putField(fieldClass, fieldName, fieldDesc); |
aoqi@0 | 172 | } |
aoqi@0 | 173 | |
aoqi@0 | 174 | /** |
aoqi@0 | 175 | * Store a value in an indexed slot of a generated class object. |
aoqi@0 | 176 | * |
aoqi@0 | 177 | * @param method Script method. |
aoqi@0 | 178 | * @param index Slot index. |
hannesw@991 | 179 | * @param tuple Tuple to store. |
aoqi@0 | 180 | */ |
attila@963 | 181 | private void putSlot(final MethodEmitter method, final long index, final MapTuple<T> tuple) { |
hannesw@1720 | 182 | loadIndex(method, index); |
attila@963 | 183 | loadTuple(method, tuple, false); //we don't pack array like objects |
aoqi@0 | 184 | method.dynamicSetIndex(callSiteFlags); |
aoqi@0 | 185 | } |
aoqi@0 | 186 | |
aoqi@0 | 187 | /** |
aoqi@0 | 188 | * Locate (or indirectly create) the object container class. |
aoqi@0 | 189 | */ |
aoqi@0 | 190 | private void findClass() { |
aoqi@0 | 191 | fieldObjectClassName = isScope() ? |
hannesw@1330 | 192 | ObjectClassGenerator.getClassName(fieldCount, paramCount, codegen.useDualFields()) : |
hannesw@1330 | 193 | ObjectClassGenerator.getClassName(paddedFieldCount, codegen.useDualFields()); |
aoqi@0 | 194 | |
aoqi@0 | 195 | try { |
aoqi@0 | 196 | this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName)); |
aoqi@0 | 197 | } catch (final ClassNotFoundException e) { |
aoqi@0 | 198 | throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created."); |
aoqi@0 | 199 | } |
aoqi@0 | 200 | } |
aoqi@0 | 201 | |
hannesw@1542 | 202 | @Override |
hannesw@1542 | 203 | protected Class<? extends ScriptObject> getAllocatorClass() { |
hannesw@1542 | 204 | return fieldObjectClass; |
hannesw@1542 | 205 | } |
hannesw@1542 | 206 | |
aoqi@0 | 207 | /** |
aoqi@0 | 208 | * Get the class name for the object class, |
aoqi@0 | 209 | * e.g. {@code com.nashorn.oracle.scripts.JO2P0} |
aoqi@0 | 210 | * |
aoqi@0 | 211 | * @return script class name |
aoqi@0 | 212 | */ |
aoqi@0 | 213 | String getClassName() { |
aoqi@0 | 214 | return fieldObjectClassName; |
aoqi@0 | 215 | } |
aoqi@0 | 216 | |
aoqi@0 | 217 | /** |
aoqi@0 | 218 | * Tally the number of fields and parameters. |
aoqi@0 | 219 | */ |
aoqi@0 | 220 | private void countFields() { |
attila@963 | 221 | for (final MapTuple<T> tuple : tuples) { |
attila@963 | 222 | final Symbol symbol = tuple.symbol; |
aoqi@0 | 223 | if (symbol != null) { |
aoqi@0 | 224 | if (hasArguments() && symbol.isParam()) { |
aoqi@0 | 225 | symbol.setFieldIndex(paramCount++); |
hannesw@1014 | 226 | } else if (!isValidArrayIndex(getArrayIndex(tuple.key))) { |
aoqi@0 | 227 | symbol.setFieldIndex(fieldCount++); |
aoqi@0 | 228 | } |
aoqi@0 | 229 | } |
aoqi@0 | 230 | } |
aoqi@0 | 231 | |
aoqi@0 | 232 | paddedFieldCount = getPaddedFieldCount(fieldCount); |
aoqi@0 | 233 | } |
aoqi@0 | 234 | |
aoqi@0 | 235 | } |