Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Painless: Replace Painless Type with Java Class during Casts #27847

Merged
merged 18 commits into from
Jan 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
Expand Down Expand Up @@ -76,6 +77,13 @@ public final class Definition {
public final Type ArrayListType;
public final Type HashMapType;

/** Marker class for def type to be used during type analysis. */
public static final class def {
private def() {

}
}

public static final class Type {
public final String name;
public final int dimensions;
Expand Down Expand Up @@ -365,40 +373,41 @@ public Method getFunctionalMethod() {
}

public static class Cast {

/** Create a standard cast with no boxing/unboxing. */
public static Cast standard(Type from, Type to, boolean explicit) {
public static Cast standard(Class<?> from, Class<?> to, boolean explicit) {
return new Cast(from, to, explicit, null, null, null, null);
}

/** Create a cast where the from type will be unboxed, and then the cast will be performed. */
public static Cast unboxFrom(Type from, Type to, boolean explicit, Type unboxFrom) {
public static Cast unboxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom) {
return new Cast(from, to, explicit, unboxFrom, null, null, null);
}

/** Create a cast where the to type will be unboxed, and then the cast will be performed. */
public static Cast unboxTo(Type from, Type to, boolean explicit, Type unboxTo) {
public static Cast unboxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxTo) {
return new Cast(from, to, explicit, null, unboxTo, null, null);
}

/** Create a cast where the from type will be boxed, and then the cast will be performed. */
public static Cast boxFrom(Type from, Type to, boolean explicit, Type boxFrom) {
public static Cast boxFrom(Class<?> from, Class<?> to, boolean explicit, Class<?> boxFrom) {
return new Cast(from, to, explicit, null, null, boxFrom, null);
}

/** Create a cast where the to type will be boxed, and then the cast will be performed. */
public static Cast boxTo(Type from, Type to, boolean explicit, Type boxTo) {
public static Cast boxTo(Class<?> from, Class<?> to, boolean explicit, Class<?> boxTo) {
return new Cast(from, to, explicit, null, null, null, boxTo);
}

public final Type from;
public final Type to;
public final Class<?> from;
public final Class<?> to;
public final boolean explicit;
public final Type unboxFrom;
public final Type unboxTo;
public final Type boxFrom;
public final Type boxTo;
public final Class<?> unboxFrom;
public final Class<?> unboxTo;
public final Class<?> boxFrom;
public final Class<?> boxTo;

private Cast(Type from, Type to, boolean explicit, Type unboxFrom, Type unboxTo, Type boxFrom, Type boxTo) {
private Cast(Class<?> from, Class<?> to, boolean explicit, Class<?> unboxFrom, Class<?> unboxTo, Class<?> boxFrom, Class<?> boxTo) {
this.from = from;
this.to = to;
this.explicit = explicit;
Expand Down Expand Up @@ -499,6 +508,92 @@ public static boolean isConstantType(Type constant) {
constant.clazz == String.class;
}

public static Class<?> ObjectClassTodefClass(Class<?> clazz) {
if (clazz.isArray()) {
Class<?> component = clazz.getComponentType();
int dimensions = 1;

while (component.isArray()) {
component = component.getComponentType();
++dimensions;
}

if (component == Object.class) {
char[] braces = new char[dimensions];
Arrays.fill(braces, '[');

String descriptor = new String(braces) + org.objectweb.asm.Type.getType(def.class).getDescriptor();
org.objectweb.asm.Type type = org.objectweb.asm.Type.getType(descriptor);

try {
return Class.forName(type.getInternalName().replace('/', '.'));
} catch (ClassNotFoundException exception) {
throw new IllegalStateException("internal error", exception);
}
}
} else if (clazz == Object.class) {
return def.class;
}

return clazz;
}

public static Class<?> defClassToObjectClass(Class<?> clazz) {
if (clazz.isArray()) {
Class<?> component = clazz.getComponentType();
int dimensions = 1;

while (component.isArray()) {
component = component.getComponentType();
++dimensions;
}

if (component == def.class) {
char[] braces = new char[dimensions];
Arrays.fill(braces, '[');

String descriptor = new String(braces) + org.objectweb.asm.Type.getType(Object.class).getDescriptor();
org.objectweb.asm.Type type = org.objectweb.asm.Type.getType(descriptor);

try {
return Class.forName(type.getInternalName().replace('/', '.'));
} catch (ClassNotFoundException exception) {
throw new IllegalStateException("internal error", exception);
}
}
} else if (clazz == def.class) {
return Object.class;
}

return clazz;
}

public static String ClassToName(Class<?> clazz) {
if (clazz.isArray()) {
Class<?> component = clazz.getComponentType();
int dimensions = 1;

while (component.isArray()) {
component = component.getComponentType();
++dimensions;
}

if (component == def.class) {
StringBuilder builder = new StringBuilder("def");

for (int dimension = 0; dimension < dimensions; dimensions++) {
builder.append("[]");
}

return builder.toString();
}
} else if (clazz == def.class) {
return "def";
}

return clazz.getCanonicalName();
}

public RuntimeClass getRuntimeClass(Class<?> clazz) {
return runtimeMap.get(clazz);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,17 @@
package org.elasticsearch.painless;

import org.elasticsearch.painless.Definition.Cast;
import org.elasticsearch.painless.Definition.Type;
import org.elasticsearch.painless.Definition.def;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Deque;
import java.util.List;
Expand Down Expand Up @@ -128,68 +130,68 @@ public void writeLoopCounter(int slot, int count, Location location) {
mark(end);
}

public void writeCast(final Cast cast) {
public void writeCast(Cast cast) {
if (cast != null) {
if (cast.from.clazz == char.class && cast.to.clazz == String.class) {
if (cast.from == char.class && cast.to == String.class) {
invokeStatic(UTILITY_TYPE, CHAR_TO_STRING);
} else if (cast.from.clazz == String.class && cast.to.clazz == char.class) {
} else if (cast.from == String.class && cast.to == char.class) {
invokeStatic(UTILITY_TYPE, STRING_TO_CHAR);
} else if (cast.unboxFrom != null) {
unbox(cast.unboxFrom.type);
unbox(getType(cast.unboxFrom));
writeCast(cast.from, cast.to);
} else if (cast.unboxTo != null) {
if (cast.from.dynamic) {
if (cast.from == def.class) {
if (cast.explicit) {
if (cast.to.clazz == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to.clazz == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
else if (cast.to.clazz == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
else if (cast.to.clazz == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
else if (cast.to.clazz == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
else if (cast.to.clazz == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
else if (cast.to.clazz == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
else if (cast.to.clazz == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_EXPLICIT);
else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_EXPLICIT);
else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_EXPLICIT);
else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_EXPLICIT);
else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_EXPLICIT);
else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_EXPLICIT);
else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_EXPLICIT);
else {
throw new IllegalStateException("Illegal tree structure.");
}
} else {
if (cast.to.clazz == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to.clazz == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
else if (cast.to.clazz == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
else if (cast.to.clazz == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
else if (cast.to.clazz == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
else if (cast.to.clazz == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
else if (cast.to.clazz == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
else if (cast.to.clazz == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
if (cast.to == Boolean.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BOOLEAN);
else if (cast.to == Byte.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_BYTE_IMPLICIT);
else if (cast.to == Short.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_SHORT_IMPLICIT);
else if (cast.to == Character.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_CHAR_IMPLICIT);
else if (cast.to == Integer.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_INT_IMPLICIT);
else if (cast.to == Long.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_LONG_IMPLICIT);
else if (cast.to == Float.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_FLOAT_IMPLICIT);
else if (cast.to == Double.class) invokeStatic(DEF_UTIL_TYPE, DEF_TO_DOUBLE_IMPLICIT);
else {
throw new IllegalStateException("Illegal tree structure.");
}
}
} else {
writeCast(cast.from, cast.to);
unbox(cast.unboxTo.type);
unbox(getType(cast.unboxTo));
}
} else if (cast.boxFrom != null) {
box(cast.boxFrom.type);
box(getType(cast.boxFrom));
writeCast(cast.from, cast.to);
} else if (cast.boxTo != null) {
writeCast(cast.from, cast.to);
box(cast.boxTo.type);
box(getType(cast.boxTo));
} else {
writeCast(cast.from, cast.to);
}
}
}

private void writeCast(final Type from, final Type to) {
private void writeCast(Class<?> from, Class<?> to) {
if (from.equals(to)) {
return;
}

if (from.clazz != boolean.class && from.clazz.isPrimitive() && to.clazz != boolean.class && to.clazz.isPrimitive()) {
cast(from.type, to.type);
if (from != boolean.class && from.isPrimitive() && to != boolean.class && to.isPrimitive()) {
cast(getType(from), getType(to));
} else {
if (!to.clazz.isAssignableFrom(from.clazz)) {
checkCast(to.type);
if (!to.isAssignableFrom(from)) {
checkCast(getType(to));
}
}
}
Expand All @@ -202,6 +204,29 @@ public void box(org.objectweb.asm.Type type) {
valueOf(type);
}

public static Type getType(Class<?> clazz) {
if (clazz.isArray()) {
Class<?> component = clazz.getComponentType();
int dimensions = 1;

while (component.isArray()) {
component = component.getComponentType();
++dimensions;
}

if (component == def.class) {
char[] braces = new char[dimensions];
Arrays.fill(braces, '[');

return Type.getType(new String(braces) + Type.getType(Object.class).getDescriptor());
}
} else if (clazz == def.class) {
return Type.getType(Object.class);
}

return Type.getType(clazz);
}

public void writeBranch(final Label tru, final Label fals) {
if (tru != null) {
visitJumpInsn(Opcodes.IFNE, tru);
Expand All @@ -227,7 +252,7 @@ public int writeNewStrings() {
}
}

public void writeAppendStrings(final Type type) {
public void writeAppendStrings(final Definition.Type type) {
if (INDY_STRING_CONCAT_BOOTSTRAP_HANDLE != null) {
// Java 9+: record type information
stringConcatArgs.peek().add(type.type);
Expand Down Expand Up @@ -267,7 +292,7 @@ public void writeToStrings() {
}

/** Writes a dynamic binary instruction: returnType, lhs, and rhs can be different */
public void writeDynamicBinaryInstruction(Location location, Type returnType, Type lhs, Type rhs,
public void writeDynamicBinaryInstruction(Location location, Definition.Type returnType, Definition.Type lhs, Definition.Type rhs,
Operation operation, int flags) {
org.objectweb.asm.Type methodType = org.objectweb.asm.Type.getMethodType(returnType.type, lhs.type, rhs.type);

Expand Down Expand Up @@ -318,7 +343,7 @@ public void writeDynamicBinaryInstruction(Location location, Type returnType, Ty
}

/** Writes a static binary instruction */
public void writeBinaryInstruction(Location location, Type type, Operation operation) {
public void writeBinaryInstruction(Location location, Definition.Type type, Operation operation) {
if ((type.clazz == float.class || type.clazz == double.class) &&
(operation == Operation.LSH || operation == Operation.USH ||
operation == Operation.RSH || operation == Operation.BWAND ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package org.elasticsearch.painless.node;

import org.elasticsearch.painless.Definition;
import org.elasticsearch.painless.Definition.Cast;

import java.util.Objects;
Expand Down Expand Up @@ -63,6 +64,6 @@ void write(MethodWriter writer, Globals globals) {

@Override
public String toString() {
return singleLineToString(cast.to, child);
return singleLineToString(Definition.ClassToName(cast.to), child);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ private static void assertCast(Type actual, Type expected, boolean mustBeExplici
}

Cast cast = definition.caster.getLegalCast(location, actual, expected, true, false);
assertEquals(actual, cast.from);
assertEquals(expected, cast.to);
assertEquals(actual.clazz, cast.from);
assertEquals(expected.clazz, cast.to);

if (mustBeExplicit) {
ClassCastException error = expectThrows(ClassCastException.class,
() -> definition.caster.getLegalCast(location, actual, expected, false, false));
assertTrue(error.getMessage().startsWith("Cannot cast"));
} else {
cast = definition.caster.getLegalCast(location, actual, expected, false, false);
assertEquals(actual, cast.from);
assertEquals(expected, cast.to);
assertEquals(actual.clazz, cast.from);
assertEquals(expected.clazz, cast.to);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,12 @@ public void testECapturingFunctionRef() {
public void testECast() {
Location l = new Location(getTestName(), 0);
AExpression child = new EConstant(l, "test");
Cast cast = Cast.standard(definition.StringType, definition.IntegerType, true);
Cast cast = Cast.standard(String.class, Integer.class, true);
assertEquals("(ECast java.lang.Integer (EConstant String 'test'))", new ECast(l, child, cast).toString());

l = new Location(getTestName(), 1);
child = new EBinary(l, Operation.ADD, new EConstant(l, "test"), new EConstant(l, 12));
cast = Cast.standard(definition.IntegerType, definition.BooleanType, true);
cast = Cast.standard(Integer.class, Boolean.class, true);
assertEquals("(ECast java.lang.Boolean (EBinary (EConstant String 'test') + (EConstant Integer 12)))",
new ECast(l, child, cast).toString());
}
Expand Down