blob: ee08864afeb4425482c2676b19514e78add5a41a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011,2012 NumberFour AG
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* NumberFour AG - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.javascript.typeinfo;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.dltk.javascript.core.Types;
import org.eclipse.dltk.javascript.typeinfo.model.ParameterKind;
class RFunctionType extends RType implements IRFunctionType {
private final ITypeSystem typeSystem;
private final List<IRParameter> parameters;
private final IRType returnType;
public RFunctionType(ITypeSystem typeSystem, List<IRParameter> parameters,
IRType returnType) {
this.typeSystem = typeSystem;
this.parameters = parameters;
this.returnType = returnType;
}
public ITypeSystem getTypeSystem() {
return typeSystem;
}
@Override
public String toString(Set<RType> processed) {
if (!processed.add(this))
return JSDocTypeParser.FUNCTION + "(_self)";
final StringBuilder sb = new StringBuilder();
sb.append(JSDocTypeParser.FUNCTION);
sb.append('(');
int index = 0;
for (IRParameter parameter : parameters) {
if (++index != 1) {
sb.append(", ");
}
if (parameter.getKind() == ParameterKind.VARARGS) {
sb.append("...");
}
if (parameter.getType() instanceof RType) {
sb.append(((RType) parameter.getType()).toString(processed));
} else {
sb.append(parameter.getType());
}
if (parameter.getKind() == ParameterKind.OPTIONAL) {
sb.append("=");
}
}
sb.append(')');
if (returnType != null) {
sb.append(':');
if (returnType instanceof RType) {
sb.append(((RType) returnType).toString(processed));
} else {
sb.append(returnType);
}
}
return sb.toString();
}
public String getName() {
return toString(new HashSet<RType>());
}
@Override
public TypeCompatibility isAssignableFrom(IRType type) {
if (super.isAssignableFrom(type).ok()) {
return TypeCompatibility.TRUE;
} else if (type instanceof RFunctionType) {
RFunctionType funcType = (RFunctionType) type;
// if return type is null or it is a IRRecordType instance then it
// is always just fine.
// RecordType is hard to check, can be type defs with fluent typing.
TypeCompatibility returnType = (getReturnType() == null || getReturnType() instanceof IRRecordType) ? TypeCompatibility.TRUE
: TypeCompatibility.FALSE;
if (returnType == TypeCompatibility.FALSE
&& funcType.getReturnType() != null
&& getReturnType() != null) {
returnType = getReturnType().isAssignableFrom(
funcType.getReturnType());
}
TypeCompatibility paramsType = TypeCompatibility.TRUE;
if (returnType == TypeCompatibility.TRUE) {
paramsType = ((funcType.getParameters() == null || funcType
.getParameters().size() == 0) && (getParameters() == null || getParameters()
.size() == 0)) ? TypeCompatibility.TRUE
: TypeCompatibility.FALSE;
if ((funcType.getParameters() != null && funcType
.getParameters().size() > 0)
&& (getParameters() != null && getParameters().size() > 0)) {
paramsType = TypeCompatibility.TRUE;
for (int i = 0; i < getParameters().size(); i++) {
IRParameter parameter = getParameters().get(i);
if (parameter.getType() != null) {
if (i < funcType.getParameters().size()) {
IRParameter funcParam = funcType
.getParameters().get(i);
if (funcParam.getType() != null) {
if (funcParam.getType().isAssignableFrom(
parameter.getType()) == TypeCompatibility.FALSE) {
paramsType = TypeCompatibility.FALSE;
break;
}
} else {
paramsType = TypeCompatibility.FALSE;
break;
}
} else if (!parameter.isOptional()) {
paramsType = TypeCompatibility.FALSE;
break;
}
}
}
}
}
return (returnType == TypeCompatibility.TRUE && paramsType == TypeCompatibility.TRUE) ? TypeCompatibility.TRUE
: TypeCompatibility.FALSE;
} else if (type instanceof IRSimpleType) {
// TODO (alex) convert when creating type
return TypeCompatibility
.valueOf(Types.FUNCTION == ((IRSimpleType) type)
.getTarget());
} else {
return TypeCompatibility.FALSE;
}
}
public IRType getReturnType() {
return returnType;
}
public List<IRParameter> getParameters() {
return parameters;
}
@Override
public int hashCode() {
return parameters.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RFunctionType other = (RFunctionType) obj;
if (!parameters.equals(other.parameters))
return false;
if (returnType == null) {
if (other.returnType != null)
return false;
} else if (!returnType.equals(other.returnType))
return false;
return true;
}
}