package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import java.lang.invoke.MethodHandle;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Undefined;
class ObjectType extends Type {
private static final long serialVersionUID = 1L;
protected ObjectType() {
this(Object.class);
}
protected ObjectType(final Class<?> clazz) {
super("object",
clazz,
clazz == Object.class ? Type.MAX_WEIGHT : 10,
1);
}
@Override
public String toString() {
return "object" + (getTypeClass() != Object.class ? "<type=" + getTypeClass().getSimpleName() + '>' : "");
}
@Override
public String getShortDescriptor() {
return getTypeClass() == Object.class ? "Object" : getTypeClass().getSimpleName();
}
@Override
public Type add(final MethodVisitor method, final int programPoint) {
invokestatic(method, ScriptRuntime.ADD);
return Type.OBJECT;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ALOAD, slot);
return this;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ASTORE, slot);
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
return UNDEFINED;
}
@Override
public Type loadForcedInitializer(final MethodVisitor method) {
method.visitInsn(ACONST_NULL);
return OBJECT;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
return UNDEFINED;
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
if (c == null) {
method.visitInsn(ACONST_NULL);
} else if (c instanceof Undefined) {
return loadUndefined(method);
} else if (c instanceof String) {
method.visitLdcInsn(c);
return STRING;
} else if (c instanceof Handle) {
method.visitLdcInsn(c);
return Type.typeFor(MethodHandle.class);
} else {
throw new UnsupportedOperationException("implementation missing for class " + c.getClass() + " value=" + c);
}
return Type.OBJECT;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
final boolean toString = to.isString();
if (!toString) {
if (to.isArray()) {
final Type elemType = ((ArrayType)to).getElementType();
if (elemType.isString()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(String[].class));
} else if (elemType.isNumber()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(double[].class));
} else if (elemType.isLong()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(long[].class));
} else if (elemType.isInteger()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(int[].class));
} else {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(Object[].class));
}
return to;
} else if (to.isObject()) {
final Class<?> toClass = to.getTypeClass();
if(!toClass.isAssignableFrom(getTypeClass())) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(toClass));
}
return to;
}
} else if (isString()) {
return to;
}
if (to.isInteger()) {
invokestatic(method, JSType.TO_INT32);
} else if (to.isNumber()) {
invokestatic(method, JSType.TO_NUMBER);
} else if (to.isLong()) {
invokestatic(method, JSType.TO_LONG);
} else if (to.isBoolean()) {
invokestatic(method, JSType.TO_BOOLEAN);
} else if (to.isString()) {
invokestatic(method, JSType.TO_PRIMITIVE_TO_STRING);
} else if (to.isCharSequence()) {
invokestatic(method, JSType.TO_PRIMITIVE_TO_CHARSEQUENCE);
} else {
throw new UnsupportedOperationException("Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString);
}
return to;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(ARETURN);
}
@Override
public char getBytecodeStackType() {
return 'A';
}
}