提交 86e077f0 编写于 作者: H hannesw

8067215: Disable dual fields when not using optimistic types

Reviewed-by: attila, lagergren
上级 ffaedbb3
......@@ -124,8 +124,6 @@ public class PrototypeGenerator extends ClassGenerator {
if (memberCount > 0) {
// call "super(map$)"
mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
// make sure we use duplicated PropertyMap so that original map
// stays intact and so can be used for many global.
mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
// initialize Function type fields
initFunctionFields(mi);
......
......@@ -63,16 +63,19 @@ SYSTEM PROPERTY: -Dnashorn.codegen.debug.trace=<x>
See the description of the codegen logger below.
SYSTEM PROPERTY: -Dnashorn.fields.objects
When this property is true, Nashorn will only use object fields for
AccessorProperties. This means that primitive values must be boxed
when stored in a field, which is significantly slower than using
primitive fields.
By default, Nashorn uses dual object and long fields. Ints are
represented as the 32 low bits of the long fields. Doubles are
represented as the doubleToLongBits of their value. This way a
SYSTEM PROPERTY: -Dnashorn.fields.objects, -Dnashorn.fields.dual
When the nashorn.fields.objects property is true, Nashorn will always
use object fields for AccessorProperties, requiring boxing for all
primitive property values. When nashorn.fields.dual is set, Nashorn
will always use dual long/object fields, which allows primitives to be
stored without boxing. When neither system property is set, Nashorn
chooses a setting depending on the optimistic types setting (dual
fields when optimistic types are enabled, object-only fields otherwise).
With dual fields, Nashorn uses long fields to store primitive values.
Ints are represented as the 32 low bits of the long fields. Doubles
are represented as the doubleToLongBits of their value. This way a
single field can be used for all primitive types. Packing and
unpacking doubles to their bit representation is intrinsified by
the JVM and extremely fast.
......
......@@ -43,7 +43,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.ir.Symbol.HAS_SLOT;
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
......@@ -305,6 +304,14 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
return evalCode;
}
/**
* Are we using dual primitive/object field representation?
* @return true if using dual field representation, false for object-only fields
*/
boolean useDualFields() {
return compiler.getContext().useDualFields();
}
/**
* Load an identity node
*
......@@ -1896,10 +1903,10 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//this symbol will be put fielded, we can't initialize it as undefined with a known type
@Override
public Class<?> getValueType() {
if (OBJECT_FIELDS_ONLY || value == null || paramType == null) {
if (!useDualFields() || value == null || paramType == null || paramType.isBoolean()) {
return Object.class;
}
return paramType.isBoolean() ? Object.class : paramType.getTypeClass();
return paramType.getTypeClass();
}
});
}
......@@ -2555,7 +2562,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
//for literals, a value of null means object type, i.e. the value null or getter setter function
//(I think)
final Class<?> valueType = (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
final Class<?> valueType = (!useDualFields() || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
tuples.add(new MapTuple<Expression>(key, symbol, Type.typeFor(valueType), value) {
@Override
public Class<?> getValueType() {
......
......@@ -149,8 +149,11 @@ public enum CompilerConstants {
/** Arguments parameter in scope object constructors; in slot 3 when present */
INIT_ARGUMENTS(null, 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
JS_OBJECT_PREFIX("JO"),
/** prefix for all ScriptObject subclasses with dual object/primitive fields, see {@link ObjectClassGenerator} */
JS_OBJECT_DUAL_FIELD_PREFIX("JD"),
/** prefix for all ScriptObject subclasses with object fields only, see {@link ObjectClassGenerator} */
JS_OBJECT_SINGLE_FIELD_PREFIX("JO"),
/** name for allocate method in JO objects */
ALLOCATE("allocate"),
......
......@@ -151,7 +151,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode);
propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), codegen.useDualFields(), fieldCount, paddedFieldCount, evalCode);
return propertyMap;
}
......@@ -166,7 +166,7 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final MapTuple<T> tuple) {
method.dup();
final Type fieldType = tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT;
final Type fieldType = codegen.useDualFields() && tuple.isPrimitive() ? PRIMITIVE_FIELD_TYPE : Type.OBJECT;
final String fieldClass = getClassName();
final String fieldName = getFieldName(fieldIndex, fieldType);
final String fieldDesc = typeDescriptor(fieldType.getTypeClass());
......@@ -202,8 +202,8 @@ public abstract class FieldObjectCreator<T> extends ObjectCreator<T> {
*/
private void findClass() {
fieldObjectClassName = isScope() ?
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
ObjectClassGenerator.getClassName(paddedFieldCount);
ObjectClassGenerator.getClassName(fieldCount, paramCount, codegen.useDualFields()) :
ObjectClassGenerator.getClassName(paddedFieldCount, codegen.useDualFields());
try {
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
......
......@@ -207,7 +207,7 @@ final class FindScopeDepths extends NodeVisitor<LexicalContext> implements Logga
final RecompilableScriptFunctionData data = new RecompilableScriptFunctionData(
newFunctionNode,
compiler.getCodeInstaller(),
ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties()),
ObjectClassGenerator.createAllocationStrategy(newFunctionNode.getThisProperties(), compiler.getContext().useDualFields()),
nestedFunctions,
externalSymbolDepths.get(fnId),
internalSymbols.get(fnId),
......
......@@ -68,17 +68,17 @@ public class MapCreator<T> {
* @param evalCode is this property map created for 'eval' code?
* @return New map populated with accessor properties.
*/
PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
PropertyMap makeFieldMap(final boolean hasArguments, final boolean dualFields, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
final List<Property> properties = new ArrayList<>();
assert tuples != null;
for (final MapTuple<T> tuple : tuples) {
final String key = tuple.key;
final Symbol symbol = tuple.symbol;
final Class<?> initialType = tuple.getValueType();
final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class;
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
final int flags = getPropertyFlags(symbol, hasArguments, evalCode);
final int flags = getPropertyFlags(symbol, hasArguments, evalCode, dualFields);
final Property property = new AccessorProperty(
key,
flags,
......@@ -92,7 +92,7 @@ public class MapCreator<T> {
return PropertyMap.newMap(properties, structure.getName(), fieldCount, fieldMaximum, 0);
}
PropertyMap makeSpillMap(final boolean hasArguments) {
PropertyMap makeSpillMap(final boolean hasArguments, final boolean dualFields) {
final List<Property> properties = new ArrayList<>();
int spillIndex = 0;
assert tuples != null;
......@@ -100,10 +100,10 @@ public class MapCreator<T> {
for (final MapTuple<T> tuple : tuples) {
final String key = tuple.key;
final Symbol symbol = tuple.symbol;
final Class<?> initialType = tuple.getValueType();
final Class<?> initialType = dualFields ? tuple.getValueType() : Object.class;
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
final int flags = getPropertyFlags(symbol, hasArguments, false);
final int flags = getPropertyFlags(symbol, hasArguments, false, dualFields);
properties.add(
new SpillProperty(
key,
......@@ -124,7 +124,7 @@ public class MapCreator<T> {
*
* @return flags to use for fields
*/
static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) {
static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode, final boolean dualFields) {
int flags = 0;
if (symbol.isParam()) {
......@@ -162,6 +162,10 @@ public class MapCreator<T> {
flags |= Property.NEEDS_DECLARATION;
}
if (dualFields) {
flags |= Property.DUAL_FIELDS;
}
return flags;
}
}
......@@ -25,8 +25,6 @@
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
......@@ -52,11 +50,11 @@ class MapTuple<T> {
}
public Class<?> getValueType() {
return OBJECT_FIELDS_ONLY ? Object.class : null; //until proven otherwise we are undefined, see NASHORN-592 int.class;
return null; //until proven otherwise we are undefined, see NASHORN-592 int.class;
}
boolean isPrimitive() {
return !OBJECT_FIELDS_ONLY && getValueType().isPrimitive() && getValueType() != boolean.class;
return getValueType() != null && getValueType().isPrimitive() && getValueType() != boolean.class;
}
@Override
......
......@@ -31,7 +31,8 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JAVA_THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.lookup.Lookup.MH;
......@@ -99,18 +100,10 @@ public final class ObjectClassGenerator implements Loggable {
*/
private final DebugLogger log;
/**
* Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
* will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
* This introduces a larger number of method handles in the system, as we need to have different getters
* and setters for the different fields.
*
* This is engineered to plug into the TaggedArray implementation, when it's done.
*/
public static final boolean OBJECT_FIELDS_ONLY = Options.getBooleanProperty("nashorn.fields.objects");
/** The field types in the system */
private static final List<Type> FIELD_TYPES = new LinkedList<>();
/** Field types for object-only fields */
private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT };
/** Field types for dual primitive/object fields */
private static final Type[] FIELD_TYPES_DUAL = new Type[] { Type.LONG, Type.OBJECT };
/** What type is the primitive type in dual representation */
public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
......@@ -118,33 +111,27 @@ public final class ObjectClassGenerator implements Loggable {
private static final MethodHandle GET_DIFFERENT = findOwnMH("getDifferent", Object.class, Object.class, Class.class, MethodHandle.class, MethodHandle.class, int.class);
private static final MethodHandle GET_DIFFERENT_UNDEFINED = findOwnMH("getDifferentUndefined", Object.class, int.class);
/**
* The list of field types that we support - one type creates one field. This is currently either
* LONG + OBJECT or just OBJECT for classic mode.
*/
static {
if (!OBJECT_FIELDS_ONLY) {
FIELD_TYPES.add(PRIMITIVE_FIELD_TYPE);
}
FIELD_TYPES.add(Type.OBJECT);
}
private static boolean initialized = false;
/** The context */
private final Context context;
private final boolean dualFields;
/**
* Constructor
*
* @param context a context
* @param dualFields whether to use dual fields representation
*/
public ObjectClassGenerator(final Context context) {
public ObjectClassGenerator(final Context context, final boolean dualFields) {
this.context = context;
this.dualFields = dualFields;
assert context != null;
this.log = initLogger(context);
if (!initialized) {
initialized = true;
if (OBJECT_FIELDS_ONLY) {
if (!dualFields) {
log.warning("Running with object fields only - this is a deprecated configuration.");
}
}
......@@ -176,16 +163,30 @@ public final class ObjectClassGenerator implements Loggable {
throw new AssertionError("cannot pack" + n);
}
private static String getPrefixName(final boolean dualFields) {
return dualFields ? JS_OBJECT_DUAL_FIELD_PREFIX.symbolName() : JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
}
private static String getPrefixName(final String className) {
if (className.startsWith(JS_OBJECT_DUAL_FIELD_PREFIX.symbolName())) {
return getPrefixName(true);
} else if (className.startsWith(JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName())) {
return getPrefixName(false);
}
throw new AssertionError("Not a structure class: " + className);
}
/**
* Returns the class name for JavaScript objects with fieldCount fields.
*
* @param fieldCount Number of fields to allocate.
*
* @param dualFields whether to use dual fields representation
* @return The class name.
*/
public static String getClassName(final int fieldCount) {
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount :
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName();
public static String getClassName(final int fieldCount, final boolean dualFields) {
final String prefix = getPrefixName(dualFields);
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount :
SCRIPTS_PACKAGE + '/' + prefix;
}
/**
......@@ -194,22 +195,23 @@ public final class ObjectClassGenerator implements Loggable {
*
* @param fieldCount Number of fields to allocate.
* @param paramCount Number of parameters to allocate
*
* @param dualFields whether to use dual fields representation
* @return The class name.
*/
public static String getClassName(final int fieldCount, final int paramCount) {
return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.symbolName() + fieldCount + SCOPE_MARKER + paramCount;
public static String getClassName(final int fieldCount, final int paramCount, final boolean dualFields) {
return SCRIPTS_PACKAGE + '/' + getPrefixName(dualFields) + fieldCount + SCOPE_MARKER + paramCount;
}
/**
* Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
* {@link #getClassName(int)} or {@link #getClassName(int, int)}.
* {@link #getClassName(int, boolean)} or {@link #getClassName(int, int, boolean)}.
* @param clazz the JavaScript scope class.
* @return the number of fields in the scope class.
*/
public static int getFieldCount(final Class<?> clazz) {
final String name = clazz.getSimpleName();
final String prefix = JS_OBJECT_PREFIX.symbolName();
final String prefix = getPrefixName(name);
if (prefix.equals(name)) {
return 0;
}
......@@ -238,8 +240,8 @@ public final class ObjectClassGenerator implements Loggable {
* @param className name of class
* @param fieldNames fields to initialize to undefined, where applicable
*/
private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
if (!OBJECT_FIELDS_ONLY) {
private void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
if (dualFields) {
// no need to initialize anything to undefined in the dual field world
// - then we have a constant getter for undefined for any unknown type
return;
......@@ -292,7 +294,7 @@ public final class ObjectClassGenerator implements Loggable {
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount) {
final String className = getClassName(fieldCount);
final String className = getClassName(fieldCount, dualFields);
final String superName = className(ScriptObject.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName);
......@@ -322,7 +324,7 @@ public final class ObjectClassGenerator implements Loggable {
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount, final int paramCount) {
final String className = getClassName(fieldCount, paramCount);
final String className = getClassName(fieldCount, paramCount, dualFields);
final String superName = className(FunctionScope.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName);
final List<String> initFields = addFields(classEmitter, fieldCount);
......@@ -353,11 +355,11 @@ public final class ObjectClassGenerator implements Loggable {
*
* @return List fields that need to be initialized.
*/
private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
private List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
final List<String> initFields = new LinkedList<>();
final Type[] fieldTypes = dualFields ? FIELD_TYPES_DUAL : FIELD_TYPES_OBJECT;
for (int i = 0; i < fieldCount; i++) {
for (final Type type : FIELD_TYPES) {
for (final Type type : fieldTypes) {
final String fieldName = getFieldName(i, type);
classEmitter.field(fieldName, type.getTypeClass());
......@@ -533,13 +535,10 @@ public final class ObjectClassGenerator implements Loggable {
private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
switch (getAccessorTypeIndex(forType)) {
case TYPE_INT_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
case TYPE_LONG_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return primitiveGetter;
case TYPE_DOUBLE_INDEX:
assert !OBJECT_FIELDS_ONLY : "this can only happen with dual fields";
return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
case TYPE_OBJECT_INDEX:
return objectGetter;
......@@ -557,7 +556,7 @@ public final class ObjectClassGenerator implements Loggable {
final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
//which is the primordial getter
final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
if (forType == null) {
if (isOptimistic) {
......@@ -580,8 +579,7 @@ public final class ObjectClassGenerator implements Loggable {
return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class);
}
assert forType != null;
assert !OBJECT_FIELDS_ONLY || forType == Object.class : forType;
assert primitiveGetter != null || forType == Object.class : forType;
if (isOptimistic) {
if (fti < ti) {
......@@ -635,8 +633,6 @@ public final class ObjectClassGenerator implements Loggable {
return tgetter;
}
assert !OBJECT_FIELDS_ONLY;
//final MethodType pmt = primitiveGetter.type();
assert primitiveGetter != null;
final MethodType tgetterType = tgetter.type();
switch (fti) {
......@@ -727,7 +723,7 @@ public final class ObjectClassGenerator implements Loggable {
final int fti = getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type);
if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) {
if (ti == TYPE_OBJECT_INDEX) {
return objectSetter;
}
......@@ -735,8 +731,6 @@ public final class ObjectClassGenerator implements Loggable {
return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
}
assert !OBJECT_FIELDS_ONLY;
final MethodType pmt = primitiveSetter.type();
switch (fti) {
......@@ -832,8 +826,8 @@ public final class ObjectClassGenerator implements Loggable {
* @param thisProperties number of properties assigned to "this"
* @return the allocation strategy
*/
static AllocationStrategy createAllocationStrategy(final int thisProperties) {
static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) {
final int paddedFieldCount = getPaddedFieldCount(thisProperties);
return new AllocationStrategy(paddedFieldCount);
return new AllocationStrategy(paddedFieldCount, dualFields);
}
}
......@@ -134,7 +134,7 @@ public abstract class ObjectCreator<T> {
MethodEmitter loadTuple(final MethodEmitter method, final MapTuple<T> tuple, final boolean pack) {
loadValue(tuple.value, tuple.type);
if (pack && tuple.isPrimitive()) {
if (pack && codegen.useDualFields() && tuple.isPrimitive()) {
method.pack();
} else {
method.convert(Type.OBJECT);
......
......@@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import java.util.LinkedHashSet;
import java.util.List;
......@@ -42,6 +41,7 @@ import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO;
/**
......@@ -65,10 +65,13 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
assert !isScope() : "spill scope objects are not currently supported";
final int length = tuples.size();
final long[] jpresetValues = new long[ScriptObject.spillAllocationLength(length)];
final Object[] opresetValues = new Object[ScriptObject.spillAllocationLength(length)];
final boolean dualFields = codegen.useDualFields();
final int spillLength = ScriptObject.spillAllocationLength(length);
final long[] jpresetValues = dualFields ? new long[spillLength] : null;
final Object[] opresetValues = new Object[spillLength];
final Set<Integer> postsetValues = new LinkedHashSet<>();
final int callSiteFlags = codegen.getCallSiteFlags();
final Class<?> objectClass = dualFields ? JD.class : JO.class;
ArrayData arrayData = ArrayData.allocate(ScriptRuntime.EMPTY_ARRAY);
// Compute constant property values
......@@ -88,9 +91,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
final Property property = propertyMap.findProperty(key);
if (property != null) {
// normal property key
property.setType(JSType.unboxedFieldType(constantValue));
property.setType(dualFields ? JSType.unboxedFieldType(constantValue) : Object.class);
final int slot = property.getSlot();
if (!OBJECT_FIELDS_ONLY && constantValue instanceof Number) {
if (dualFields && constantValue instanceof Number) {
jpresetValues[slot] = ObjectClassGenerator.pack((Number)constantValue);
} else {
opresetValues[slot] = constantValue;
......@@ -130,28 +133,32 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
//assert postsetValues.isEmpty() : "test me " + postsetValues;
// create object and invoke constructor
method._new(JO.class).dup();
method._new(objectClass).dup();
codegen.loadConstant(propertyMap);
//load primitive values to j spill array
codegen.loadConstant(jpresetValues);
for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
if (property != null && tuple.isPrimitive()) {
method.dup();
method.load(property.getSlot());
loadTuple(method, tuple);
method.arraystore();
if (dualFields) {
codegen.loadConstant(jpresetValues);
for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
if (property != null && tuple.isPrimitive()) {
method.dup();
method.load(property.getSlot());
loadTuple(method, tuple);
method.arraystore();
}
}
} else {
method.loadNull();
}
//load object values to o spill array
codegen.loadConstant(opresetValues);
for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
if (property != null && !tuple.isPrimitive()) {
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
if (property != null && (!dualFields || !tuple.isPrimitive())) {
method.dup();
method.load(property.getSlot());
loadTuple(method, tuple);
......@@ -160,7 +167,7 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
}
//instantiate the script object with spill objects
method.invoke(constructorNoLookup(JO.class, PropertyMap.class, long[].class, Object[].class));
method.invoke(constructorNoLookup(objectClass, PropertyMap.class, long[].class, Object[].class));
// Set prefix array data if any
if (arrayData.length() > 0) {
......@@ -171,8 +178,8 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
// set postfix
for (final int i : postsetValues) {
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
final MapTuple<Expression> tuple = tuples.get(i);
final Property property = propertyMap.findProperty(tuple.key);
if (property == null) {
final int index = ArrayIndex.getArrayIndex(tuple.key);
assert ArrayIndex.isValidArrayIndex(index);
......@@ -188,7 +195,9 @@ public final class SpillObjectCreator extends ObjectCreator<Expression> {
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
propertyMap = new MapCreator<>(JO.class, tuples).makeSpillMap(false);
final boolean dualFields = codegen.useDualFields();
final Class<? extends ScriptObject> clazz = dualFields ? JD.class : JO.class;
propertyMap = new MapCreator<>(clazz, tuples).makeSpillMap(false, codegen.useDualFields());
return propertyMap;
}
......
......@@ -79,6 +79,7 @@ import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.regexp.RegExpResult;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO;
import jdk.nashorn.tools.ShellFunctions;
......@@ -718,7 +719,7 @@ public final class Global extends ScriptObject implements Scope {
private static final MethodHandle LOAD = findOwnMH_S("load", Object.class, Object.class, Object.class);
private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class);
private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class);
// initialized by nasgen
private static PropertyMap $nasgenmap$;
......@@ -750,6 +751,11 @@ public final class Global extends ScriptObject implements Scope {
return context;
}
@Override
protected boolean useDualFields() {
return context.useDualFields();
}
// performs initialization checks for Global constructor and returns the
// PropertyMap, if everything is fine.
private static PropertyMap checkAndGetMap(final Context context) {
......@@ -933,7 +939,7 @@ public final class Global extends ScriptObject implements Scope {
* @return the new ScriptObject
*/
public ScriptObject newObject() {
return new JO(getObjectPrototype(), JO.getInitialMap());
return useDualFields() ? new JD(getObjectPrototype()) : new JO(getObjectPrototype());
}
/**
......@@ -2744,8 +2750,8 @@ public final class Global extends ScriptObject implements Scope {
*/
private static class LexicalScope extends ScriptObject {
LexicalScope(final ScriptObject proto) {
super(proto, PropertyMap.newMap());
LexicalScope(final Global global) {
super(global, PropertyMap.newMap());
}
@Override
......
......@@ -160,7 +160,7 @@ public final class NativeJSAdapter extends ScriptObject {
}
private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
return new JO(adaptee, JO.getInitialMap());
return new JO(adaptee);
}
@Override
......
......@@ -41,6 +41,7 @@ import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.SpillProperty;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
import jdk.nashorn.internal.scripts.JD;
import jdk.nashorn.internal.scripts.JO;
import static jdk.nashorn.internal.parser.TokenType.STRING;
......@@ -54,11 +55,10 @@ public class JSONParser {
final private String source;
final private Global global;
final private boolean dualFields;
final int length;
int pos = 0;
private static PropertyMap EMPTY_MAP = PropertyMap.newMap();
private static final int EOF = -1;
private static final String TRUE = "true";
......@@ -74,10 +74,11 @@ public class JSONParser {
* @param source the source
* @param global the global object
*/
public JSONParser(final String source, final Global global ) {
public JSONParser(final String source, final Global global, final boolean dualFields) {
this.source = source;
this.global = global;
this.length = source.length();
this.dualFields = dualFields;
}
/**
......@@ -180,7 +181,7 @@ public class JSONParser {
}
private Object parseObject() {
PropertyMap propertyMap = EMPTY_MAP;
PropertyMap propertyMap = dualFields ? JD.getInitialMap() : JO.getInitialMap();
ArrayData arrayData = ArrayData.EMPTY_ARRAY;
final ArrayList<Object> values = new ArrayList<>();
int state = STATE_EMPTY;
......@@ -241,36 +242,45 @@ public class JSONParser {
return newArrayData.set(index, value, false);
}
private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
private PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
final String id, final Object value) {
final Property oldProperty = propertyMap.findProperty(id);
final PropertyMap newMap;
final Class<?> type = ObjectClassGenerator.OBJECT_FIELDS_ONLY ? Object.class : getType(value);
final Class<?> type;
final int flags;
if (dualFields) {
type = getType(value);
flags = Property.DUAL_FIELDS;
} else {
type = Object.class;
flags = 0;
}
if (oldProperty != null) {
values.set(oldProperty.getSlot(), value);
newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, 0, oldProperty.getSlot(), type));;
newMap = propertyMap.replaceProperty(oldProperty, new SpillProperty(id, flags, oldProperty.getSlot(), type));;
} else {
values.add(value);
newMap = propertyMap.addProperty(new SpillProperty(id, 0, propertyMap.size(), type));
newMap = propertyMap.addProperty(new SpillProperty(id, flags, propertyMap.size(), type));
}
return newMap;
}
private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) {
final long[] primitiveSpill = new long[values.size()];
final long[] primitiveSpill = dualFields ? new long[values.size()] : null;
final Object[] objectSpill = new Object[values.size()];
for (final Property property : propertyMap.getProperties()) {
if (property.getType() == Object.class) {
if (!dualFields || property.getType() == Object.class) {
objectSpill[property.getSlot()] = values.get(property.getSlot());
} else {
primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot()));
}
}
final ScriptObject object = new JO(propertyMap, primitiveSpill, objectSpill);
final ScriptObject object = dualFields ?
new JD(propertyMap, primitiveSpill, objectSpill) : new JO(propertyMap, null, objectSpill);
object.setInitialProto(global.getObjectPrototype());
object.setArray(arrayData);
return object;
......
......@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createGetter;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.createSetter;
......@@ -98,7 +97,7 @@ public class AccessorProperty extends Property {
objectSetters[i] = MH.asType(MH.setter(LOOKUP, structure, fieldName, typeClass), Lookup.SET_OBJECT_TYPE);
}
if (!OBJECT_FIELDS_ONLY) {
if (!StructureLoader.isSingleFieldStructure(structure.getName())) {
for (int i = 0; i < fieldCount; i++) {
final String fieldNamePrimitive = getFieldName(i, PRIMITIVE_FIELD_TYPE);
final Class<?> typeClass = PRIMITIVE_FIELD_TYPE.getTypeClass();
......@@ -211,7 +210,7 @@ public class AccessorProperty extends Property {
* @param setter the property setter or null if non writable, non configurable
*/
private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
super(key, flags | IS_BUILTIN | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
super(key, flags | IS_BUILTIN | DUAL_FIELDS | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
assert !isSpill();
// we don't need to prep the setters these will never be invalidated as this is a nasgen
......@@ -221,18 +220,15 @@ public class AccessorProperty extends Property {
final Class<?> setterType = setter == null ? null : setter.type().parameterType(1);
assert setterType == null || setterType == getterType;
if (OBJECT_FIELDS_ONLY) {
primitiveGetter = primitiveSetter = null;
if (getterType == int.class || getterType == long.class) {
primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
} else if (getterType == double.class) {
primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE);
primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE);
} else {
if (getterType == int.class || getterType == long.class) {
primitiveGetter = MH.asType(getter, Lookup.GET_PRIMITIVE_TYPE);
primitiveSetter = setter == null ? null : MH.asType(setter, Lookup.SET_PRIMITIVE_TYPE);
} else if (getterType == double.class) {
primitiveGetter = MH.asType(MH.filterReturnValue(getter, ObjectClassGenerator.PACK_DOUBLE), Lookup.GET_PRIMITIVE_TYPE);
primitiveSetter = setter == null ? null : MH.asType(MH.filterArguments(setter, 1, ObjectClassGenerator.UNPACK_DOUBLE), Lookup.SET_PRIMITIVE_TYPE);
} else {
primitiveGetter = primitiveSetter = null;
}
primitiveGetter = primitiveSetter = null;
}
assert primitiveGetter == null || primitiveGetter.type() == Lookup.GET_PRIMITIVE_TYPE : primitiveGetter + "!=" + Lookup.GET_PRIMITIVE_TYPE;
......@@ -241,7 +237,7 @@ public class AccessorProperty extends Property {
objectGetter = getter.type() != Lookup.GET_OBJECT_TYPE ? MH.asType(getter, Lookup.GET_OBJECT_TYPE) : getter;
objectSetter = setter != null && setter.type() != Lookup.SET_OBJECT_TYPE ? MH.asType(setter, Lookup.SET_OBJECT_TYPE) : setter;
setType(OBJECT_FIELDS_ONLY ? Object.class : getterType);
setType(getterType);
}
/**
......@@ -282,6 +278,9 @@ public class AccessorProperty extends Property {
objectSetter = gs.objectSetters[slot];
primitiveSetter = gs.primitiveSetters[slot];
}
// Always use dual fields except for single field structures
assert hasDualFields() != StructureLoader.isSingleFieldStructure(structure.getName());
}
/**
......@@ -310,7 +309,7 @@ public class AccessorProperty extends Property {
*/
public AccessorProperty(final String key, final int flags, final Class<?> structure, final int slot, final Class<?> initialType) {
this(key, flags, structure, slot);
setType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
setType(hasDualFields() ? initialType : Object.class);
}
/**
......@@ -347,7 +346,7 @@ public class AccessorProperty extends Property {
* @param initialValue initial value
*/
protected final void setInitialValue(final ScriptObject owner, final Object initialValue) {
setType(JSType.unboxedFieldType(initialValue));
setType(hasDualFields() ? JSType.unboxedFieldType(initialValue) : Object.class);
if (initialValue instanceof Integer) {
invokeSetter(owner, ((Integer)initialValue).intValue());
} else if (initialValue instanceof Long) {
......@@ -363,7 +362,7 @@ public class AccessorProperty extends Property {
* Initialize the type of a property
*/
protected final void initializeType() {
setType(OBJECT_FIELDS_ONLY ? Object.class : null);
setType(!hasDualFields() ? Object.class : null);
}
private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
......@@ -670,7 +669,7 @@ public class AccessorProperty extends Property {
@Override
public final boolean canChangeType() {
if (OBJECT_FIELDS_ONLY) {
if (!hasDualFields()) {
return false;
}
// Return true for currently undefined even if non-writable/configurable to allow initialization of ES6 CONST.
......
......@@ -44,6 +44,9 @@ final public class AllocationStrategy implements Serializable {
/** Number of fields in the allocated object */
private final int fieldCount;
/** Whether to use dual field representation */
private final boolean dualFields;
/** Name of class where allocator function resides */
private transient String allocatorClassName;
......@@ -53,15 +56,17 @@ final public class AllocationStrategy implements Serializable {
/**
* Construct an allocation strategy with the given map and class name.
* @param fieldCount number of fields in the allocated object
* @param dualFields whether to use dual field representation
*/
public AllocationStrategy(final int fieldCount) {
public AllocationStrategy(final int fieldCount, final boolean dualFields) {
this.fieldCount = fieldCount;
this.dualFields = dualFields;
}
private String getAllocatorClassName() {
if (allocatorClassName == null) {
// These classes get loaded, so an interned variant of their name is most likely around anyway.
allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount)).intern();
allocatorClassName = Compiler.binaryName(ObjectClassGenerator.getClassName(fieldCount, dualFields)).intern();
}
return allocatorClassName;
}
......
......@@ -130,6 +130,23 @@ public final class Context {
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
/**
* Should scripts use only object slots for fields, or dual long/object slots? The default
* behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
* and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
* or "nashorn.fields.dual" system property.
*/
private final FieldMode fieldMode;
private static enum FieldMode {
/** Value for automatic field representation depending on optimistic types setting */
AUTO,
/** Value for object field representation regardless of optimistic types setting */
OBJECTS,
/** Value for dual primitive/object field representation regardless of optimistic types setting */
DUAL
}
/**
* Keeps track of which builtin prototypes and properties have been relinked
* Currently we are conservative and associate the name of a builtin class with all
......@@ -434,7 +451,7 @@ public final class Context {
* @param appLoader application class loader
*/
public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
this(options, errors, appLoader, (ClassFilter)null);
this(options, errors, appLoader, null);
}
/**
......@@ -522,6 +539,14 @@ public final class Context {
getErr().println("nashorn full version " + Version.fullVersion());
}
if (Options.getBooleanProperty("nashorn.fields.dual")) {
fieldMode = FieldMode.DUAL;
} else if (Options.getBooleanProperty("nashorn.fields.objects")) {
fieldMode = FieldMode.OBJECTS;
} else {
fieldMode = FieldMode.AUTO;
}
initLoggers();
}
......@@ -575,6 +600,14 @@ public final class Context {
return env.getErr();
}
/**
* Should scripts compiled by this context use dual field representation?
* @return true if using dual fields, false for object-only fields
*/
public boolean useDualFields() {
return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types);
}
/**
* Get the PropertyMap of the current global scope
* @return the property map of the current global scope
......
......@@ -72,7 +72,8 @@ public final class JSONFunctions {
public static Object parse(final Object text, final Object reviver) {
final String str = JSType.toString(text);
final Global global = Context.getGlobal();
final JSONParser parser = new JSONParser(str, global);
final boolean dualFields = ((ScriptObject) global).useDualFields();
final JSONParser parser = new JSONParser(str, global, dualFields);
final Object value;
try {
......
......@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
......@@ -1972,10 +1971,6 @@ public enum JSType {
* @return primive type or Object.class if not primitive
*/
public static Class<?> unboxedFieldType(final Object o) {
if (OBJECT_FIELDS_ONLY) {
return Object.class;
}
if (o == null) {
return Object.class;
} else if (o.getClass() == Integer.class) {
......
......@@ -96,6 +96,9 @@ public abstract class Property implements Serializable {
/** Is this property an ES6 lexical binding? */
public static final int IS_LEXICAL_BINDING = 1 << 10;
/** Does this property support dual field representation? */
public static final int DUAL_FIELDS = 1 << 11;
/** Property key. */
private final String key;
......@@ -286,7 +289,7 @@ public abstract class Property implements Serializable {
* @return true if parameter
*/
public boolean isParameter() {
return (flags & IS_PARAMETER) == IS_PARAMETER;
return (flags & IS_PARAMETER) != 0;
}
/**
......@@ -294,7 +297,7 @@ public abstract class Property implements Serializable {
* @return true if has arguments
*/
public boolean hasArguments() {
return (flags & HAS_ARGUMENTS) == HAS_ARGUMENTS;
return (flags & HAS_ARGUMENTS) != 0;
}
/**
......@@ -316,7 +319,7 @@ public abstract class Property implements Serializable {
* @return true if this is a bound property
*/
public boolean isBound() {
return (flags & IS_BOUND) == IS_BOUND;
return (flags & IS_BOUND) != 0;
}
/**
......@@ -325,7 +328,7 @@ public abstract class Property implements Serializable {
* @return true if this is a block-scoped variable
*/
public boolean needsDeclaration() {
return (flags & NEEDS_DECLARATION) == NEEDS_DECLARATION;
return (flags & NEEDS_DECLARATION) != 0;
}
/**
......@@ -345,16 +348,6 @@ public abstract class Property implements Serializable {
return this;
}
/**
* Check if a flag is set for a property
* @param property property
* @param flag flag to check
* @return true if flag is set
*/
public static boolean checkFlag(final Property property, final int flag) {
return (property.getFlags() & flag) == flag;
}
/**
* Get the flags for this property
* @return property flags
......@@ -363,16 +356,6 @@ public abstract class Property implements Serializable {
return flags;
}
/**
* Get the modify flags for this property. The modify flags are the ECMA 8.6.1
* flags that decide if the Property is writable, configurable and/or enumerable.
*
* @return modify flags for property
*/
public int getModifyFlags() {
return flags & MODIFY_MASK;
}
/**
* Remove property flags from the property. Properties are immutable here,
* so any property change that results in a smaller flag set results in the
......@@ -715,7 +698,7 @@ public abstract class Property implements Serializable {
* @return whether this property is a function declaration or not.
*/
public boolean isFunctionDeclaration() {
return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
return (flags & IS_FUNCTION_DECLARATION) != 0;
}
/**
......@@ -723,6 +706,14 @@ public abstract class Property implements Serializable {
* @return true if this property represents a lexical binding.
*/
public boolean isLexicalBinding() {
return (flags & IS_LEXICAL_BINDING) == IS_LEXICAL_BINDING;
return (flags & IS_LEXICAL_BINDING) != 0;
}
/**
* Does this property support dual fields for both primitive and object values?
* @return true if supports dual fields
*/
public boolean hasDualFields() {
return (flags & DUAL_FIELDS) != 0;
}
}
......@@ -198,13 +198,22 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
return properties == null || properties.isEmpty()? newMap() : newMap(properties, JO.class.getName(), 0, 0, 0);
}
/**
* Return a sharable empty map for the given object class.
* @param clazz the base object class
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newMap(final Class<? extends ScriptObject> clazz) {
return new PropertyMap(EMPTY_HASHMAP, clazz.getName(), 0, 0, 0, false);
}
/**
* Return a sharable empty map.
*
* @return New empty {@link PropertyMap}.
*/
public static PropertyMap newMap() {
return new PropertyMap(EMPTY_HASHMAP, JO.class.getName(), 0, 0, 0, false);
return newMap(JO.class);
}
/**
......
......@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
......@@ -146,12 +145,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** Area for reference properties added to object after instantiation, see {@link AccessorProperty} */
protected Object[] objectSpill;
/**
* Number of elements in the spill. This may be less than the spill array lengths, if not all of
* the allocated memory is in use
*/
private int spillLength;
/** Indexed array data. */
private ArrayData arrayData;
......@@ -171,12 +164,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/** Method handle for getting the array data */
public static final Call GET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArray", ArrayData.class);
/** Method handle for getting the property map - debugging purposes */
public static final Call GET_MAP = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getMap", PropertyMap.class);
/** Method handle for setting the array data */
public static final Call SET_ARRAY = virtualCall(MethodHandles.lookup(), ScriptObject.class, "setArray", void.class, ArrayData.class);
/** Method handle for getting a function argument at a given index. Used from MapCreator */
public static final Call GET_ARGUMENT = virtualCall(MethodHandles.lookup(), ScriptObject.class, "getArgument", Object.class, int.class);
......@@ -259,8 +246,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
this(map);
this.primitiveSpill = primitiveSpill;
this.objectSpill = objectSpill;
assert primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
this.spillLength = spillAllocationLength(primitiveSpill.length);
assert primitiveSpill == null || primitiveSpill.length == objectSpill.length : " primitive spill pool size is not the same length as object spill pool size";
}
/**
......@@ -727,7 +713,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
setArray(getArray().ensure(longIndex));
doesNotHaveEnsureDelete(longIndex, oldLength, false);
}
setArray(getArray().set(index,value, false));
setArray(getArray().set(index, value, false));
}
private void checkIntegerKey(final String key) {
......@@ -976,10 +962,10 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
*/
protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
final int slot = spillLength;
ensureSpillSize(spillLength); //arguments=slot0, caller=slot0
objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
final PropertyMap oldMap = getMap();
final int slot = oldMap.getFreeSpillSlot();
ensureSpillSize(slot);
objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
Property newProperty;
PropertyMap newMap;
do {
......@@ -1006,19 +992,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final int slot = uc.getSlot();
assert uc.getLocalType() == Object.class;
if (slot >= spillLength) {
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else {
final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
if (gs == null) {
uc.setAccessors(this, getMap(), new UserAccessorProperty.Accessors(getter, setter));
} else {
//reuse existing getter setter for speed
gs.set(getter, setter);
if (uc.getFlags() == propertyFlags) {
return oldProperty;
}
}
final UserAccessorProperty.Accessors gs = uc.getAccessors(this); //this crashes
assert gs != null;
//reuse existing getter setter for speed
gs.set(getter, setter);
if (uc.getFlags() == propertyFlags) {
return oldProperty;
}
newProperty = new UserAccessorProperty(uc.getKey(), propertyFlags, slot);
} else {
......@@ -2053,8 +2032,6 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
protoSwitchPoint = null;
}
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
}
......@@ -2531,13 +2508,14 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
/**
* Add a spill property for the given key.
* @param key Property key.
* @param propertyFlags Property flags.
* @param key Property key.
* @param flags Property flags.
* @return Added property.
*/
private Property addSpillProperty(final String key, final int propertyFlags, final Object value, final boolean hasInitialValue) {
private Property addSpillProperty(final String key, final int flags, final Object value, final boolean hasInitialValue) {
final PropertyMap propertyMap = getMap();
final int fieldSlot = propertyMap.getFreeFieldSlot();
final int propertyFlags = flags | (useDualFields() ? Property.DUAL_FIELDS : 0);
Property property;
if (fieldSlot > -1) {
......@@ -2562,7 +2540,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @return Setter method handle.
*/
MethodHandle addSpill(final Class<?> type, final String key) {
return addSpillProperty(key, 0, null, false).getSetter(OBJECT_FIELDS_ONLY ? Object.class : type, getMap());
return addSpillProperty(key, 0, null, false).getSetter(type, getMap());
}
/**
......@@ -2649,9 +2627,9 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final int spreadArgs = mh.type().parameterCount() - callSiteParamCount + 1;
return MH.filterArguments(
MH.asSpreader(
mh,
Object[].class,
spreadArgs),
mh,
Object[].class,
spreadArgs),
callSiteParamCount - 1,
MH.insertArguments(
TRUNCATINGFILTER,
......@@ -3739,24 +3717,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return uc;
}
/**
* Returns {@code true} if properties for this object should use dual field mode, {@code false} otherwise.
* @return {@code true} if dual fields should be used.
*/
protected boolean useDualFields() {
return !StructureLoader.isSingleFieldStructure(getClass().getName());
}
Object ensureSpillSize(final int slot) {
if (slot < spillLength) {
final int oldLength = objectSpill == null ? 0 : objectSpill.length;
if (slot < oldLength) {
return this;
}
final int newLength = alignUp(slot + 1, SPILL_RATE);
final Object[] newObjectSpill = new Object[newLength];
final long[] newPrimitiveSpill = OBJECT_FIELDS_ONLY ? null : new long[newLength];
final long[] newPrimitiveSpill = useDualFields() ? new long[newLength] : null;
if (objectSpill != null) {
System.arraycopy(objectSpill, 0, newObjectSpill, 0, spillLength);
if (!OBJECT_FIELDS_ONLY) {
System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, spillLength);
System.arraycopy(objectSpill, 0, newObjectSpill, 0, oldLength);
if (primitiveSpill != null && newPrimitiveSpill != null) {
System.arraycopy(primitiveSpill, 0, newPrimitiveSpill, 0, oldLength);
}
}
this.primitiveSpill = newPrimitiveSpill;
this.objectSpill = newObjectSpill;
this.spillLength = newLength;
return this;
}
......
......@@ -232,14 +232,18 @@ final class SetMethodCreator {
}
private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) {
return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
return createNewSetter(new AccessorProperty(getName(), getFlags(sobj), sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
}
private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) {
return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
return createNewSetter(new SpillProperty(getName(), getFlags(sobj), getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
}
private PropertyMap getNewMap(final Property property) {
return getMap().addProperty(property);
}
private static int getFlags(final ScriptObject scriptObject) {
return scriptObject.useDualFields() ? Property.DUAL_FIELDS : 0;
}
}
......@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.lang.invoke.MethodHandle;
......@@ -139,11 +138,11 @@ public class SpillProperty extends AccessorProperty {
}
}
private static MethodHandle primitiveGetter(final int slot) {
return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, true);
private static MethodHandle primitiveGetter(final int slot, final int flags) {
return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, true) : null;
}
private static MethodHandle primitiveSetter(final int slot) {
return OBJECT_FIELDS_ONLY ? null : Accessors.getCached(slot, true, false);
private static MethodHandle primitiveSetter(final int slot, final int flags) {
return (flags & DUAL_FIELDS) == DUAL_FIELDS ? Accessors.getCached(slot, true, false) : null;
}
private static MethodHandle objectGetter(final int slot) {
return Accessors.getCached(slot, false, true);
......@@ -160,8 +159,7 @@ public class SpillProperty extends AccessorProperty {
* @param slot spill slot
*/
public SpillProperty(final String key, final int flags, final int slot) {
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot));
assert !OBJECT_FIELDS_ONLY || getLocalType() == Object.class;
super(key, flags, slot, primitiveGetter(slot, flags), primitiveSetter(slot, flags), objectGetter(slot), objectSetter(slot));
}
/**
......@@ -173,7 +171,7 @@ public class SpillProperty extends AccessorProperty {
*/
public SpillProperty(final String key, final int flags, final int slot, final Class<?> initialType) {
this(key, flags, slot);
setType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
setType(hasDualFields() ? initialType : Object.class);
}
SpillProperty(final String key, final int flags, final int slot, final ScriptObject owner, final Object initialValue) {
......@@ -216,8 +214,8 @@ public class SpillProperty extends AccessorProperty {
@Override
void initMethodHandles(final Class<?> structure) {
final int slot = getSlot();
primitiveGetter = primitiveGetter(slot);
primitiveSetter = primitiveSetter(slot);
primitiveGetter = primitiveGetter(slot, getFlags());
primitiveSetter = primitiveSetter(slot, getFlags());
objectGetter = objectGetter(slot);
objectSetter = objectSetter(slot);
}
......
......@@ -27,7 +27,8 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.Compiler.binaryName;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_DUAL_FIELD_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_SINGLE_FIELD_PREFIX;
import java.security.ProtectionDomain;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
......@@ -36,7 +37,8 @@ import jdk.nashorn.internal.codegen.ObjectClassGenerator;
* Responsible for on the fly construction of structure classes.
*/
final class StructureLoader extends NashornLoader {
private static final String JS_OBJECT_PREFIX_EXTERNAL = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_PREFIX.symbolName();
private static final String SINGLE_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_SINGLE_FIELD_PREFIX.symbolName();
private static final String DUAL_FIELD_PREFIX = binaryName(SCRIPTS_PACKAGE) + '.' + JS_OBJECT_DUAL_FIELD_PREFIX.symbolName();
/**
* Constructor.
......@@ -45,14 +47,39 @@ final class StructureLoader extends NashornLoader {
super(parent);
}
/**
* Returns true if the class name represents a structure object with dual primitive/object fields.
* @param name a class name
* @return true if a dual field structure class
*/
private static boolean isDualFieldStructure(final String name) {
return name.startsWith(DUAL_FIELD_PREFIX);
}
/**
* Returns true if the class name represents a structure object with single object-only fields.
* @param name a class name
* @return true if a single field structure class
*/
static boolean isSingleFieldStructure(final String name) {
return name.startsWith(SINGLE_FIELD_PREFIX);
}
/**
* Returns true if the class name represents a Nashorn structure object.
* @param name a class name
* @return true if a structure class
*/
static boolean isStructureClass(final String name) {
return name.startsWith(JS_OBJECT_PREFIX_EXTERNAL);
return isDualFieldStructure(name) || isSingleFieldStructure(name);
}
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
if (isStructureClass(name)) {
return generateClass(name, name.substring(JS_OBJECT_PREFIX_EXTERNAL.length()));
if (isDualFieldStructure(name)) {
return generateClass(name, name.substring(DUAL_FIELD_PREFIX.length()), true);
} else if (isSingleFieldStructure(name)) {
return generateClass(name, name.substring(SINGLE_FIELD_PREFIX.length()), false);
}
return super.findClass(name);
}
......@@ -63,10 +90,10 @@ final class StructureLoader extends NashornLoader {
* @param descriptor Layout descriptor.
* @return Generated class.
*/
private Class<?> generateClass(final String name, final String descriptor) {
private Class<?> generateClass(final String name, final String descriptor, final boolean dualFields) {
final Context context = Context.getContextTrusted();
final byte[] code = new ObjectClassGenerator(context).generate(descriptor);
final byte[] code = new ObjectClassGenerator(context, dualFields).generate(descriptor);
return defineClass(name, code, 0, code.length, new ProtectionDomain(null, getPermissions(null)));
}
}
......@@ -74,17 +74,16 @@ public final class Bootstrap {
* of object fields only, it is fine. However, with dual fields, in order to get
* performance on benchmarks with a lot of object instantiation and then field
* reassignment, it can take slightly more relinks to become stable with type
* changes swapping out an entire proprety map and making a map guard fail.
* Therefore the relink threshold is set to 16 for dual fields (now the default).
* This doesn't seem to have any other negative performance implication.
* changes swapping out an entire property map and making a map guard fail.
* Since we need to set this value statically it must work with possibly changing
* optimistic types and dual fields settings. A higher value does not seem to have
* any other negative performance implication when running with object-only fields,
* so we choose a higher value here.
*
* See for example octane.gbemu, run with --log=fields:warning to study
* megamorphic behavior
*/
private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD =
ObjectClassGenerator.OBJECT_FIELDS_ONLY ?
8 :
16;
private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16;
// do not create me!!
private Bootstrap() {
......
......@@ -33,7 +33,6 @@ import java.lang.ref.WeakReference;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
......@@ -123,7 +122,7 @@ public final class NashornGuards {
*/
static boolean needsGuard(final Property property, final CallSiteDescriptor desc) {
return property == null || property.isConfigurable()
|| property.isBound() || !ObjectClassGenerator.OBJECT_FIELDS_ONLY
|| property.isBound() || property.hasDualFields()
|| !NashornCallSiteDescriptor.isFastScope(desc) || property.canChangeType();
}
......
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.scripts;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Empty object class for dual primitive-object fields.
*/
public class JD extends ScriptObject {
private static final PropertyMap map$ = PropertyMap.newMap(JD.class);
/**
* Returns the initial property map to be used.
* @return the initial property map.
*/
public static PropertyMap getInitialMap() {
return map$;
}
/**
* Constructor given an initial property map
*
* @param map the property map
*/
public JD(final PropertyMap map) {
super(map);
}
/**
* Constructor given an initial prototype and the default initial property map.
*
* @param proto the prototype object
*/
public JD(final ScriptObject proto) {
super(proto, getInitialMap());
}
/**
* Constructor that takes a pre-initialized spill pool. Used by
* {@link jdk.nashorn.internal.codegen.SpillObjectCreator} and
* {@link jdk.nashorn.internal.parser.JSONParser} for initializing object literals
*
* @param map property map
* @param primitiveSpill primitive spill pool
* @param objectSpill reference spill pool
*/
public JD(final PropertyMap map, final long[] primitiveSpill, final Object[] objectSpill) {
super(map, primitiveSpill, objectSpill);
}
/**
* A method handle of this method is passed to the ScriptFunction constructor.
*
* @param map the property map to use for allocatorMap
*
* @return newly allocated ScriptObject
*/
public static ScriptObject allocate(final PropertyMap map) {
return new JD(map);
}
}
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -29,11 +29,11 @@ import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* Empty object class.
* Empty object class for object-only fields.
*/
public class JO extends ScriptObject {
private static final PropertyMap map$ = PropertyMap.newMap();
private static final PropertyMap map$ = PropertyMap.newMap(JO.class);
/**
* Returns the initial property map to be used.
......@@ -53,13 +53,12 @@ public class JO extends ScriptObject {
}
/**
* Constructor given an initial prototype and an initial property map.
* Constructor given an initial prototype and the default initial property map.
*
* @param proto the prototype object
* @param map the property map
*/
public JO(final ScriptObject proto, final PropertyMap map) {
super(proto, map);
public JO(final ScriptObject proto) {
super(proto, getInitialMap());
}
/**
......@@ -86,3 +85,4 @@ public class JO extends ScriptObject {
return new JO(map);
}
}
/*
* Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* JDK-8067215: Disable dual fields when not using optimistic types
*
* @test
* @run
* @option -Dnashorn.debug=true
* @fork
*/
var intType = Java.type("int");
var doubleType = Java.type("double");
var longType = Java.type("long");
var objectType = Java.type("java.lang.Object");
var Context = Java.type("jdk.nashorn.internal.runtime.Context");
var JSType = Java.type("jdk.nashorn.internal.runtime.JSType");
var context = Context.getContext();
var dualFields = context.useDualFields();
var optimisticTypes = context.getEnv()._optimistic_types;
if (dualFields != optimisticTypes) {
throw new Error("Wrong dual fields setting");
}
function testMap(obj) {
obj.x = "foo";
obj["y"] = 0;
Object.defineProperty(obj, "z", {value: 0.5});
var map = Debug.map(obj);
for (var key in obj) {
var prop = map.findProperty(key);
if (prop.hasDualFields() !== dualFields) {
throw new Error("Wrong property flags: " + prop);
}
if (prop.getType() != getExpectedType(obj[key])) {
throw new Error("Wrong property type: " + prop.getType() + " // " + getExpectedType(obj[key]));
}
}
}
function getExpectedType(value) {
if (!dualFields) {
return objectType.class;
}
if (JSType.isRepresentableAsInt(value)) {
return intType.class;
}
if (JSType.isRepresentableAsLong(value)) {
return longType.class;
}
if (JSType.isNumber(value)) {
return doubleType.class;
}
return objectType.class;
}
var o = {
a: 1,
b: 2.5,
c: 0x10000000000,
d: true
};
function C() {
this.a = 1;
this.b = 2.5;
this.c = 0x10000000000;
this.d = true;
}
var a = 1;
var b = 2.5;
var c = 0x10000000000;
var d = true;
testMap(o);
testMap(new C());
testMap(JSON.parse('{ "a": 1, "b": 2.5, "c": 1099511627776, "d": true }'));
testMap(this);
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册