| /******************************************************************************* |
| * 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; |
| } |
| |
| } |