TypeGroup typeGroup = getTypeGroup();
type = Type.dereference(typeGroup.create(typeName));
if (type == null || type.isUndefined()) {
throw new TypeException("NewExpression.typeCheck : unknown type " + typeName + getPos());
}
if (type.isObject() && arrayDimCount == 0) {
// we need to look for a suitable constructor
Class clazz = type.getTargetClass();
// if we can find a unique method then we can use it to type the parameters
// otherwise we do it the hard way
int arity = arguments.size();
Constructor[] constructors = clazz.getConstructors();
List<Constructor> candidates = new ArrayList<Constructor>();
boolean duplicates = false;
for (Constructor constructor : constructors) {
if (constructor.getParameterTypes().length == arity) {
candidates.add(constructor);
}
}
argumentTypes = new ArrayList<Type>();
// check each argument in turn -- if all candidates have the same argument type then
// use that as the type to check against
for (int i = 0; i < arguments.size() ; i++) {
if (candidates.isEmpty()) {
throw new TypeException("NewExpression.typeCheck : invalid constructor for target class " + typeName + getPos());
}
// TODO get and prune operations do not allow for coercion but type check does!
// e.g. the parameter type may be int and the arg type float
// or the parameter type may be String and the arg type class Foo
// reimplement this using type inter-assignability to do the pruning
Class candidateClass = getCandidateArgClass(candidates, i);
Type candidateType;
if (candidateClass != null) {
candidateType = typeGroup.ensureType(candidateClass);
} else {
candidateType = Type.UNDEFINED;
}
Type argType = arguments.get(i).typeCheck(candidateType);
argumentTypes.add(argType);
if (candidateType == Type.UNDEFINED) {
// we had several constructors to choose from
candidates = pruneCandidates(candidates, i, argType.getTargetClass());
}
}
if (candidates.isEmpty()) {
throw new TypeException("NewExpression.typeCheck : invalid constructor for target class " + typeName + getPos());
}
if (candidates.size() > 1) {
throw new TypeException("NewExpression.typeCheck : ambiguous constructor signature for target class " + typeName + getPos());
}
constructor = candidates.get(0);
// make sure we know the formal parameter types and have included them in the typegroup
paramTypes = new ArrayList<Type>();
Class<?>[] paramClasses = constructor.getParameterTypes();
for (int i = 0; i < arguments.size() ; i++) {
paramTypes.add(typeGroup.ensureType(paramClasses[i]));
}
} else if (arrayDimCount == 0) {
// if we have a primitive type then have to have some array dimensions
throw new TypeException("NewExpression.typeCheck : invalid type for new operation " + getPos());
}
// if this is a new array operation we must have at least one defined dimension and we cannot have
// more dimensions than we can fit into a byte
if (arrayDimCount > 0 && arrayDimDefinedCount == 0) {
throw new TypeException("NewExpression.typeCheck : array dimension missing " + getPos());
}
if (arrayDimCount > Byte.MAX_VALUE) {
throw new TypeException("NewExpression.typeCheck : too many array dimensions " + getPos());
}
// if we have any array dimension sizings then ensure they all type check as integer expressions
for (int i = 0; i < arrayDimCount ; i++) {
if (i < arrayDimDefinedCount) {
Expression expr = arrayDims.get(i);
expr.typeCheck(Type.I);
}
// replace the current type with the corresponding array type
type = typeGroup.createArray(type);
}
// if the expected type is defined then ensure we can assign this type to it
if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
throw new TypeException("NewExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
}
return type;
}