/*
* Copyright (c) 2010, 2013, 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.codegen;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
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_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;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.JSType.GET_UNDEFINED;
import static jdk.nashorn.internal.runtime.JSType.TYPE_DOUBLE_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_INT_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_OBJECT_INDEX;
import static jdk.nashorn.internal.runtime.JSType.TYPE_UNDEFINED_INDEX;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.AllocationStrategy;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.FunctionScope;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Undefined;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
import jdk.nashorn.internal.runtime.logging.Loggable;
import jdk.nashorn.internal.runtime.logging.Logger;
Generates the ScriptObject subclass structure with fields for a user objects.
/**
* Generates the ScriptObject subclass structure with fields for a user objects.
*/
@Logger(name="fields")
public final class ObjectClassGenerator implements Loggable {
Type guard to make sure we don't unnecessarily explode field storages. Rather unbox e.g.
a java.lang.Number than blow up the field. Gradually, optimistic types should create almost
no boxed types
/**
* Type guard to make sure we don't unnecessarily explode field storages. Rather unbox e.g.
* a java.lang.Number than blow up the field. Gradually, optimistic types should create almost
* no boxed types
*/
private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
Marker for scope parameters
/**
* Marker for scope parameters
*/
private static final String SCOPE_MARKER = "P";
Minimum number of extra fields in an object.
/**
* Minimum number of extra fields in an object.
*/
static final int FIELD_PADDING = 4;
Debug field logger
Should we print debugging information for fields when they are generated and getters/setters are called?
/**
* Debug field logger
* Should we print debugging information for fields when they are generated and getters/setters are called?
*/
private final DebugLogger log;
Field types for object-only fields /** Field types for object-only fields */
private static final Type[] FIELD_TYPES_OBJECT = new Type[] { Type.OBJECT };
Field types for dual primitive/object fields /** 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 /** What type is the primitive type in dual representation */
public static final Type PRIMITIVE_FIELD_TYPE = Type.LONG;
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);
private static boolean initialized = false;
The context /** The context */
private final Context context;
private final boolean dualFields;
Constructor
Params: - context – a context
- dualFields – whether to use dual fields representation
/**
* Constructor
*
* @param context a context
* @param dualFields whether to use dual fields representation
*/
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 (!dualFields) {
log.warning("Running with object fields only - this is a deprecated configuration.");
}
}
}
@Override
public DebugLogger getLogger() {
return log;
}
@Override
public DebugLogger initLogger(final Context ctxt) {
return ctxt.getLogger(this.getClass());
}
Pack a number into a primitive long field
Params: - n – number object
Returns: primitive long value with all the bits in the number
/**
* Pack a number into a primitive long field
* @param n number object
* @return primitive long value with all the bits in the number
*/
public static long pack(final Number n) {
if (n instanceof Integer) {
return n.intValue();
} else if (n instanceof Long) {
return n.longValue();
} else if (n instanceof Double) {
return Double.doubleToRawLongBits(n.doubleValue());
}
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.
Params: - fieldCount – Number of fields to allocate.
- dualFields – whether to use dual fields representation
Returns: The class name.
/**
* 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, final boolean dualFields) {
final String prefix = getPrefixName(dualFields);
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + prefix + fieldCount :
SCRIPTS_PACKAGE + '/' + prefix;
}
Returns the class name for JavaScript scope with fieldCount fields and
paramCount parameters.
Params: - fieldCount – Number of fields to allocate.
- paramCount – Number of parameters to allocate
- dualFields – whether to use dual fields representation
Returns: The class name.
/**
* Returns the class name for JavaScript scope with fieldCount fields and
* paramCount parameters.
*
* @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, 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 getClassName(int, boolean)
or getClassName(int, int, boolean)
. Params: - clazz – the JavaScript scope class.
Returns: the number of fields in the scope class.
/**
* Returns the number of fields in the JavaScript scope class. Its name had to be generated using either
* {@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 = getPrefixName(name);
if (prefix.equals(name)) {
return 0;
}
final int scopeMarker = name.indexOf(SCOPE_MARKER);
return Integer.parseInt(scopeMarker == -1 ? name.substring(prefix.length()) : name.substring(prefix.length(), scopeMarker));
}
Returns the name of a field based on number and type.
Params: - fieldIndex – Ordinal of field.
- type – Type of field.
Returns: The field name.
/**
* Returns the name of a field based on number and type.
*
* @param fieldIndex Ordinal of field.
* @param type Type of field.
*
* @return The field name.
*/
public static String getFieldName(final int fieldIndex, final Type type) {
return type.getDescriptor().substring(0, 1) + fieldIndex;
}
In the world of Object fields, we also have no undefined SwitchPoint, to reduce as much potential
MethodHandle overhead as possible. In that case, we explicitly need to assign undefined to fields
when we initialize them.
Params: - init – constructor to generate code in
- className – name of class
- fieldNames – fields to initialize to undefined, where applicable
/**
* In the world of Object fields, we also have no undefined SwitchPoint, to reduce as much potential
* MethodHandle overhead as possible. In that case, we explicitly need to assign undefined to fields
* when we initialize them.
*
* @param init constructor to generate code in
* @param className name of class
* @param fieldNames fields to initialize to undefined, where applicable
*/
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;
}
if (fieldNames.isEmpty()) {
return;
}
init.load(Type.OBJECT, JAVA_THIS.slot());
init.loadUndefined(Type.OBJECT);
final Iterator<String> iter = fieldNames.iterator();
while (iter.hasNext()) {
final String fieldName = iter.next();
if (iter.hasNext()) {
init.dup2();
}
init.putField(className, fieldName, Type.OBJECT.getDescriptor());
}
}
Generate the byte codes for a JavaScript object class or scope.
Class name is a function of number of fields and number of param
fields
Params: - descriptor – Descriptor pulled from class name.
Returns: Byte codes for generated class.
/**
* Generate the byte codes for a JavaScript object class or scope.
* Class name is a function of number of fields and number of param
* fields
*
* @param descriptor Descriptor pulled from class name.
*
* @return Byte codes for generated class.
*/
public byte[] generate(final String descriptor) {
final String[] counts = descriptor.split(SCOPE_MARKER);
final int fieldCount = Integer.valueOf(counts[0]);
if (counts.length == 1) {
return generate(fieldCount);
}
final int paramCount = Integer.valueOf(counts[1]);
return generate(fieldCount, paramCount);
}
Generate the byte codes for a JavaScript object class with fieldCount fields.
Params: - fieldCount – Number of fields in the JavaScript object.
Returns: Byte codes for generated class.
/**
* Generate the byte codes for a JavaScript object class with fieldCount fields.
*
* @param fieldCount Number of fields in the JavaScript object.
*
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount) {
final String className = getClassName(fieldCount, dualFields);
final String superName = className(ScriptObject.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName);
addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitMethod(classEmitter);
init.returnVoid();
init.end();
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, ScriptObject.class);
initWithSpillArrays.returnVoid();
initWithSpillArrays.end();
newEmptyInit(className, classEmitter);
newAllocate(className, classEmitter);
return toByteArray(className, classEmitter);
}
Generate the byte codes for a JavaScript scope class with fieldCount fields
and paramCount parameters.
Params: - fieldCount – Number of fields in the JavaScript scope.
- paramCount – Number of parameters in the JavaScript scope
.
Returns: Byte codes for generated class.
/**
* Generate the byte codes for a JavaScript scope class with fieldCount fields
* and paramCount parameters.
*
* @param fieldCount Number of fields in the JavaScript scope.
* @param paramCount Number of parameters in the JavaScript scope
* .
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount, final int 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);
final MethodEmitter init = newInitScopeMethod(classEmitter);
initializeToUndefined(init, className, initFields);
init.returnVoid();
init.end();
final MethodEmitter initWithSpillArrays = newInitWithSpillArraysMethod(classEmitter, FunctionScope.class);
initializeToUndefined(initWithSpillArrays, className, initFields);
initWithSpillArrays.returnVoid();
initWithSpillArrays.end();
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
initializeToUndefined(initWithArguments, className, initFields);
initWithArguments.returnVoid();
initWithArguments.end();
return toByteArray(className, classEmitter);
}
Generates the needed fields.
Params: - classEmitter – Open class emitter.
- fieldCount – Number of fields.
Returns: List fields that need to be initialized.
/**
* Generates the needed fields.
*
* @param classEmitter Open class emitter.
* @param fieldCount Number of fields.
*
* @return List fields that need to be initialized.
*/
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 : fieldTypes) {
final String fieldName = getFieldName(i, type);
classEmitter.field(fieldName, type.getTypeClass());
if (type == Type.OBJECT) {
initFields.add(fieldName);
}
}
}
return initFields;
}
Allocate and initialize a new class emitter.
Params: - className – Name of JavaScript class.
Returns: Open class emitter.
/**
* Allocate and initialize a new class emitter.
*
* @param className Name of JavaScript class.
*
* @return Open class emitter.
*/
private ClassEmitter newClassEmitter(final String className, final String superName) {
final ClassEmitter classEmitter = new ClassEmitter(context, className, superName);
classEmitter.begin();
return classEmitter;
}
Allocate and initialize a new method.
Params: - classEmitter – Open class emitter.
Returns: Open method emitter.
/**
* Allocate and initialize a new <init> method.
*
* @param classEmitter Open class emitter.
*
* @return Open method emitter.
*/
private static MethodEmitter newInitMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
init.load(Type.OBJECT, INIT_MAP.slot());
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
return init;
}
private static MethodEmitter newInitWithSpillArraysMethod(final ClassEmitter classEmitter, final Class<?> superClass) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, long[].class, Object[].class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.LONG_ARRAY, 2);
init.load(Type.OBJECT_ARRAY, 3);
init.invoke(constructorNoLookup(superClass, PropertyMap.class, long[].class, Object[].class));
return init;
}
Allocate and initialize a new method for scopes.
Params: - classEmitter – Open class emitter.
Returns: Open method emitter.
/**
* Allocate and initialize a new <init> method for scopes.
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
return init;
}
Allocate and initialize a new method for scopes with arguments.
Params: - classEmitter – Open class emitter.
Returns: Open method emitter.
/**
* Allocate and initialize a new <init> method for scopes with arguments.
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, JAVA_THIS.slot());
init.load(Type.OBJECT, INIT_MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, ScriptObject.class));
return init;
}
Add an empty method to the JavaScript class.
Params: - classEmitter – Open class emitter.
- className – Name of JavaScript class.
/**
* Add an empty <init> method to the JavaScript class.
*
* @param classEmitter Open class emitter.
* @param className Name of JavaScript class.
*/
private static void newEmptyInit(final String className, final ClassEmitter classEmitter) {
final MethodEmitter emptyInit = classEmitter.init();
emptyInit.begin();
emptyInit.load(Type.OBJECT, JAVA_THIS.slot());
emptyInit.loadNull();
emptyInit.invoke(constructorNoLookup(className, PropertyMap.class));
emptyInit.returnVoid();
emptyInit.end();
}
Add an empty method to the JavaScript class.
Params: - classEmitter – Open class emitter.
- className – Name of JavaScript class.
/**
* Add an empty <init> method to the JavaScript class.
*
* @param classEmitter Open class emitter.
* @param className Name of JavaScript class.
*/
private static void newAllocate(final String className, final ClassEmitter classEmitter) {
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.symbolName(), ScriptObject.class, PropertyMap.class);
allocate.begin();
allocate._new(className, Type.typeFor(ScriptObject.class));
allocate.dup();
allocate.load(Type.typeFor(PropertyMap.class), 0);
allocate.invoke(constructorNoLookup(className, PropertyMap.class));
allocate._return();
allocate.end();
}
Collects the byte codes for a generated JavaScript class.
Params: - classEmitter – Open class emitter.
Returns: Byte codes for the class.
/**
* Collects the byte codes for a generated JavaScript class.
*
* @param classEmitter Open class emitter.
* @return Byte codes for the class.
*/
private byte[] toByteArray(final String className, final ClassEmitter classEmitter) {
classEmitter.end();
final byte[] code = classEmitter.toByteArray();
final ScriptEnvironment env = context.getEnv();
DumpBytecode.dumpBytecode(env, log, code, className);
if (env._verify_code) {
context.verify(code);
}
return code;
}
Double to long bits, used with --dual-fields for primitive double values /** Double to long bits, used with --dual-fields for primitive double values */
public static final MethodHandle PACK_DOUBLE =
MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
double bits to long, used with --dual-fields for primitive double values /** double bits to long, used with --dual-fields for primitive double values */
public static final MethodHandle UNPACK_DOUBLE =
MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
//type != forType, so use the correct getter for forType, box it and throw
@SuppressWarnings("unused")
private static Object getDifferent(final Object receiver, final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
//create the sametype getter, and upcast to value. no matter what the store format is,
//
final MethodHandle sameTypeGetter = getterForType(forType, primitiveGetter, objectGetter);
final MethodHandle mh = MH.asType(sameTypeGetter, sameTypeGetter.type().changeReturnType(Object.class));
try {
final Object value = mh.invokeExact(receiver);
throw new UnwarrantedOptimismException(value, programPoint);
} catch (final Error | RuntimeException e) {
throw e;
} catch (final Throwable e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unused")
private static Object getDifferentUndefined(final int programPoint) {
throw new UnwarrantedOptimismException(Undefined.getUndefined(), programPoint);
}
private static MethodHandle getterForType(final Class<?> forType, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
switch (getAccessorTypeIndex(forType)) {
case TYPE_INT_INDEX:
return MH.explicitCastArguments(primitiveGetter, primitiveGetter.type().changeReturnType(int.class));
case TYPE_DOUBLE_INDEX:
return MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
case TYPE_OBJECT_INDEX:
return objectGetter;
default:
throw new AssertionError(forType);
}
}
//no optimism here. we do unconditional conversion to types
private static MethodHandle createGetterInner(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final List<MethodHandle> converters, final int programPoint) {
final int fti = forType == null ? TYPE_UNDEFINED_INDEX : getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type);
//this means fail if forType != type
final boolean isOptimistic = converters == CONVERT_OBJECT_OPTIMISTIC;
final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
//which is the primordial getter
final MethodHandle getter = primitiveGetter == null ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
if (forType == null) {
if (isOptimistic) {
//return undefined if asking for object. otherwise throw UnwarrantedOptimismException
if (ti == TYPE_OBJECT_INDEX) {
return MH.dropArguments(GET_UNDEFINED.get(TYPE_OBJECT_INDEX), 0, Object.class);
}
//throw exception
return MH.asType(
MH.dropArguments(
MH.insertArguments(
GET_DIFFERENT_UNDEFINED,
0,
programPoint),
0,
Object.class),
getter.type().changeReturnType(type));
}
//return an undefined and coerce it to the appropriate type
return MH.dropArguments(GET_UNDEFINED.get(ti), 0, Object.class);
}
assert primitiveGetter != null || forType == Object.class : forType;
if (isOptimistic) {
if (fti < ti) {
//asking for a wider type than currently stored. then it's OK to coerce.
//e.g. stored as int, ask for long or double
//e.g. stored as long, ask for double
assert fti != TYPE_UNDEFINED_INDEX;
final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
return MH.asType(tgetter, tgetter.type().changeReturnType(type));
} else if (fti == ti) {
//Fast path, never throw exception - exact getter, just unpack if needed
return getterForType(forType, primitiveGetter, objectGetter);
} else {
assert fti > ti;
//if asking for a narrower type than the storage - throw exception
//unless FTI is object, in that case we have to go through the converters
//there is no
if (fti == TYPE_OBJECT_INDEX) {
return MH.filterReturnValue(
objectGetter,
MH.insertArguments(
converters.get(ti),
1,
programPoint));
}
//asking for narrower primitive than we have stored, that is an
//UnwarrantedOptimismException
return MH.asType(
MH.filterArguments(
objectGetter,
0,
MH.insertArguments(
GET_DIFFERENT,
1,
forType,
primitiveGetter,
objectGetter,
programPoint)),
objectGetter.type().changeReturnType(type));
}
}
assert !isOptimistic;
// freely coerce the result to whatever you asked for, this is e.g. Object->int for a & b
final MethodHandle tgetter = getterForType(forType, primitiveGetter, objectGetter);
if (fti == TYPE_OBJECT_INDEX) {
if (fti != ti) {
return MH.filterReturnValue(tgetter, CONVERT_OBJECT.get(ti));
}
return tgetter;
}
assert primitiveGetter != null;
final MethodType tgetterType = tgetter.type();
switch (fti) {
case TYPE_INT_INDEX: {
return MH.asType(tgetter, tgetterType.changeReturnType(type));
}
case TYPE_DOUBLE_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
return MH.filterReturnValue(tgetter, JSType.TO_INT32_D.methodHandle);
case TYPE_DOUBLE_INDEX:
assert tgetterType.returnType() == double.class;
return tgetter;
default:
return MH.asType(tgetter, tgetterType.changeReturnType(Object.class));
}
default:
throw new UnsupportedOperationException(forType + "=>" + type);
}
}
Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
the primitive and object version of a field respectively, return one with the correct
method type and the correct filters. For example, if the value is stored as a double
and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
which reads a long, use longBitsToDouble on the result to unpack it, and then change the
return type to Object, boxing it. In the objects only world there are only object fields,
primitives are boxed when asked for them and we don't need to bother with primitive encoding
(or even undefined, which if forType==null) representation, so we just return whatever is
in the object field. The object field is always initiated to Undefined, so here, where we have
the representation for Undefined in all our bits, this is not a problem.
Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
we could limit the width of a representation, and for a double (as long as it is stored as long,
as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
Naturally we could have special undefined values for all types which mean "go look in a wider field",
but the guards needed on every getter took too much time.
To see how this is used, look for example in AccessorProperty.getGetter
Params: - forType – representation of the underlying type in the field, null if undefined
- type – type to retrieve it as
- primitiveGetter – getter to read the primitive version of this field (null if Objects Only)
- objectGetter – getter to read the object version of this field
- programPoint – program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
Returns: getter for the given representation that returns the given type
/**
* Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
* the primitive and object version of a field respectively, return one with the correct
* method type and the correct filters. For example, if the value is stored as a double
* and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
* which reads a long, use longBitsToDouble on the result to unpack it, and then change the
* return type to Object, boxing it. In the objects only world there are only object fields,
* primitives are boxed when asked for them and we don't need to bother with primitive encoding
* (or even undefined, which if forType==null) representation, so we just return whatever is
* in the object field. The object field is always initiated to Undefined, so here, where we have
* the representation for Undefined in all our bits, this is not a problem.
* <p>
* Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
* we could limit the width of a representation, and for a double (as long as it is stored as long,
* as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
* Naturally we could have special undefined values for all types which mean "go look in a wider field",
* but the guards needed on every getter took too much time.
* <p>
* To see how this is used, look for example in {@link AccessorProperty#getGetter}
* <p>
* @param forType representation of the underlying type in the field, null if undefined
* @param type type to retrieve it as
* @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
* @param objectGetter getter to read the object version of this field
* @param programPoint program point for getter, if program point is INVALID_PROGRAM_POINT, then this is not an optimistic getter
*
* @return getter for the given representation that returns the given type
*/
public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter, final int programPoint) {
return createGetterInner(
forType,
type,
primitiveGetter,
objectGetter,
isValid(programPoint) ? CONVERT_OBJECT_OPTIMISTIC : CONVERT_OBJECT,
programPoint);
}
This is similar to the createGetter
function. Performs the necessary operations to massage a setter operand of type type
to fit into the primitive field (if primitive and dual fields is enabled) or into the object field (box if primitive and dual fields is disabled) Params: - forType – representation of the underlying object
- type – representation of field to write, and setter signature
- primitiveSetter – setter that writes to the primitive field (null if Objects Only)
- objectSetter – setter that writes to the object field
Returns: the setter for the given representation that takes a type
/**
* This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs
* the necessary operations to massage a setter operand of type {@code type} to
* fit into the primitive field (if primitive and dual fields is enabled) or into
* the object field (box if primitive and dual fields is disabled)
*
* @param forType representation of the underlying object
* @param type representation of field to write, and setter signature
* @param primitiveSetter setter that writes to the primitive field (null if Objects Only)
* @param objectSetter setter that writes to the object field
*
* @return the setter for the given representation that takes a {@code type}
*/
public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
assert forType != null;
final int fti = getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type);
if (fti == TYPE_OBJECT_INDEX || primitiveSetter == null) {
if (ti == TYPE_OBJECT_INDEX) {
return objectSetter;
}
return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
}
final MethodType pmt = primitiveSetter.type();
switch (fti) {
case TYPE_INT_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class));
case TYPE_DOUBLE_INDEX:
return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE);
default:
return objectSetter;
}
case TYPE_DOUBLE_INDEX:
if (ti == TYPE_OBJECT_INDEX) {
return objectSetter;
}
return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
default:
throw new UnsupportedOperationException(forType + "=>" + type);
}
}
@SuppressWarnings("unused")
private static boolean isType(final Class<?> boxedForType, final Object x) {
return x != null && x.getClass() == boxedForType;
}
private static Class<? extends Number> getBoxedType(final Class<?> forType) {
if (forType == int.class) {
return Integer.class;
}
if (forType == long.class) {
return Long.class;
}
if (forType == double.class) {
return Double.class;
}
assert false;
return null;
}
If we are setting boxed types (because the compiler couldn't determine which they were) to
a primitive field, we can reuse the primitive field getter, as long as we are setting an element
of the same boxed type as the primitive type representation
Params: - forType – the current type
- primitiveSetter – primitive setter for the current type with an element of the current type
- objectSetter – the object setter
Returns: method handle that checks if the element to be set is of the current type, even though it's boxed
and instead of using the generic object setter, that would blow up the type and invalidate the map,
unbox it and call the primitive setter instead
/**
* If we are setting boxed types (because the compiler couldn't determine which they were) to
* a primitive field, we can reuse the primitive field getter, as long as we are setting an element
* of the same boxed type as the primitive type representation
*
* @param forType the current type
* @param primitiveSetter primitive setter for the current type with an element of the current type
* @param objectSetter the object setter
*
* @return method handle that checks if the element to be set is of the current type, even though it's boxed
* and instead of using the generic object setter, that would blow up the type and invalidate the map,
* unbox it and call the primitive setter instead
*/
public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
final Class<? extends Number> boxedForType = getBoxedType(forType);
//object setter that checks for primitive if current type is primitive
return MH.guardWithTest(
MH.insertArguments(
MH.dropArguments(
IS_TYPE_GUARD,
1,
Object.class),
0,
boxedForType),
MH.asType(
primitiveSetter,
objectSetter.type()),
objectSetter);
}
Add padding to field count to avoid creating too many classes and have some spare fields
Params: - count – the field count
Returns: the padded field count
/**
* Add padding to field count to avoid creating too many classes and have some spare fields
* @param count the field count
* @return the padded field count
*/
static int getPaddedFieldCount(final int count) {
return count / FIELD_PADDING * FIELD_PADDING + FIELD_PADDING;
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
}
Creates the allocator class name and property map for a constructor function with the specified
number of "this" properties that it initializes.
Params: - thisProperties – number of properties assigned to "this"
Returns: the allocation strategy
/**
* Creates the allocator class name and property map for a constructor function with the specified
* number of "this" properties that it initializes.
* @param thisProperties number of properties assigned to "this"
* @return the allocation strategy
*/
static AllocationStrategy createAllocationStrategy(final int thisProperties, final boolean dualFields) {
final int paddedFieldCount = getPaddedFieldCount(thisProperties);
return new AllocationStrategy(paddedFieldCount, dualFields);
}
}