blob: 729b84c41ed7abf99f161d636ca5cd37cf76ecba [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.dltk.annotations.Nullable;
import org.eclipse.dltk.internal.javascript.validation.JavaScriptValidations;
import org.eclipse.dltk.javascript.core.JavaScriptPlugin;
import org.eclipse.dltk.javascript.core.Types;
import org.eclipse.dltk.javascript.internal.core.RRecordMember;
import org.eclipse.dltk.javascript.internal.core.TypeSystems;
import org.eclipse.dltk.javascript.typeinference.IValueReference;
import org.eclipse.dltk.javascript.typeinfo.model.JSType;
import org.eclipse.dltk.javascript.typeinfo.model.Member;
import org.eclipse.dltk.javascript.typeinfo.model.Type;
import org.eclipse.emf.common.util.EList;
/**
* Static utility methods pertaining to {@code IRType} instances.
*/
public class RTypes {
private RTypes() {
}
private static IRType initRType(IRType defaultValue) {
final IExtensionRegistry registry = RegistryFactory.getRegistry();
if (registry != null) { // if running under OSGI
final String name = defaultValue.getClass().getSimpleName();
final IConfigurationElement[] elements = registry
.getConfigurationElementsFor(TypeInfoManager.EXT_POINT);
for (IConfigurationElement element : elements) {
if ("runtimeType".equals(element.getName())
&& name.equals(element.getAttribute("name"))) {
try {
return (IRType) element
.createExecutableExtension("class");
} catch (Exception e) {
JavaScriptPlugin.error(e);
}
}
}
}
return defaultValue;
}
private static final IRType UNDEFINED_TYPE = new Undefined();
static class Undefined extends RType {
public String getName() {
return ITypeNames.UNDEFINED;
}
@Override
public TypeCompatibility isAssignableFrom(IRType type) {
return TypeCompatibility.valueOf(type == this);
}
}
/**
* Returns the instance of the special <b>undefined</b> type.
*/
public static IRType undefined() {
return UNDEFINED_TYPE;
}
private static final IRType ANY_TYPE = initRType(new Any());
static class Any extends RType {
public String getName() {
return "Any";
}
@Override
public TypeCompatibility isAssignableFrom(IRType type) {
return TypeCompatibility.TRUE;
}
@Override
public boolean isExtensible() {
return true;
}
}
/**
* Returns the instance of the logical <b>Any</b> type.
*
* @return
*/
public static IRType any() {
return ANY_TYPE;
}
private static final IRType NONE_TYPE = new None();
static class None extends RType {
public String getName() {
return "None";
}
@Override
public TypeCompatibility isAssignableFrom(IRType type) {
return TypeCompatibility.TRUE;
}
@Override
public boolean isExtensible() {
return true;
}
@Override
public boolean isSynthetic() {
return true;
}
}
/**
* Returns the instance of the logical <b>None</b> type (which is used as a
* placeholder if generic type parameters (e.g. array item type) are not
* known/specified).
*/
public static IRType none() {
return NONE_TYPE;
}
/**
* Returns the instance of the <b>empty array literal</b>.
*/
public static IRArrayType arrayOf() {
return ARRAY_OF;
}
static final IRType EMPTY_ARRAY_ITEM_TYPE = new EmptyArrayItem();
private static final IRArrayType ARRAY_OF = arrayOf(TypeSystems.GLOBAL,
EMPTY_ARRAY_ITEM_TYPE);
static class EmptyArrayItem extends RType {
public String getName() {
return "empty";
}
@Override
public TypeCompatibility isAssignableFrom(IRType type) {
return TypeCompatibility.TRUE;
}
@Override
public boolean isExtensible() {
return true;
}
}
public static IRSimpleType simple(ITypeSystem typeSystem, Type type) {
if (Types.ARRAY == type) {
return arrayOf(typeSystem, none());
} else {
return (IRSimpleType) type.toRType(typeSystem);
}
}
public static IRType simple(IRTypeDeclaration declaration) {
return declaration.getSource().getMetaType().toRType(declaration);
}
public static IRClassType classType(ITypeSystem typeSystem, Type type) {
return new RClassType(typeSystem, type);
}
public static IRClassType classType(IRTypeDeclaration declaration) {
return new RClassType(declaration);
}
/**
* Returns the {@link IRClassType} corresponding to the specified (simple)
* type.
* <p>
* NOTICE: At the moment only {@link IRSimpleType} is supported at the
* moment, for all other values empty class type is returned.
* </p>
*/
public static IRClassType classOf(IRType type) {
if (type instanceof IRSimpleType) {
return classType(((IRSimpleType) type).getDeclaration());
} else {
// not supported case - create empty class type.
return classType((IRTypeDeclaration) null);
}
}
public static IRMapType mapOf(final IRType keyType, final IRType valueType) {
return new RMapType(keyType, valueType);
}
/**
* Returns empty record type instance.
*/
public static IRRecordType emptyRecordType() {
return EMPTY_RECORD_TYPE;
}
private static final IRRecordType EMPTY_RECORD_TYPE = new EmptyRecordType();
static class EmptyRecordType extends RType implements IRRecordType {
public String getName() {
return "{}";
}
public IRRecordMember getMember(String name) {
return null;
}
public Collection<IRRecordMember> getMembers() {
return Collections.emptyList();
}
public void init(ITypeSystem context, EList<Member> members) {
}
}
public static IRRecordType recordType(ITypeSystem typeSystem,
Collection<Member> members) {
return new RRecordType(typeSystem, members);
}
public static IRRecordType recordType(Collection<IRRecordMember> members) {
return new RRecordType(members);
}
public static IRRecordType recordType() {
return new RRecordType();
}
/**
* Represents the specified {@link IValueReference} as {@link IRRecordType}.
* Only {@link IRRecordType} value types and direct children are considered,
* otherwise the {@link #emptyRecordType() empty record type} is returned.
*/
public static IRRecordType recordType(@Nullable IValueReference argument) {
if (argument != null) {
final Set<String> directChildren = argument.getDirectChildren();
IRType type = JavaScriptValidations.typeOf(argument);
if (type instanceof IRUnionType) {
for (IRType unionTarget : ((IRUnionType) type).getTargets()) {
if (unionTarget instanceof IRRecordType) {
type = unionTarget;
break;
}
}
}
if (type instanceof IRRecordType) {
if (directChildren.isEmpty()) {
return (IRRecordType) type;
} else {
final List<IRRecordMember> members = new ArrayList<IRRecordMember>(
directChildren.size()
+ ((IRRecordType) type).getMembers().size());
for (String childName : directChildren) {
final IValueReference child = argument
.getChild(childName);
if (child.exists()) {
final IRType memberType = JavaScriptValidations
.typeOf(child);
members.add(new RRecordMember(childName,
memberType != null ? memberType : any(),
child));
}
}
for (IRRecordMember member : ((IRRecordType) type)
.getMembers()) {
if (!directChildren.contains(member.getName())) {
members.add(member);
}
}
return recordType(members);
}
} else if (!directChildren.isEmpty()) {
final List<IRRecordMember> members = new ArrayList<IRRecordMember>(
directChildren.size());
for (String childName : directChildren) {
final IValueReference child = argument.getChild(childName);
if (child.exists()) {
final IRType memberType = JavaScriptValidations
.typeOf(child);
members.add(new RRecordMember(childName,
memberType != null ? memberType : any(), child));
}
}
return recordType(members);
} else {
return emptyRecordType();
}
} else {
return emptyRecordType();
}
}
public static Set<String> memberNames(IRRecordType recordType) {
final Collection<IRRecordMember> members = recordType.getMembers();
if (members.isEmpty()) {
return Collections.emptySet();
}
final Set<String> names = new LinkedHashSet<String>(members.size());
for (IRRecordMember member : members) {
names.add(member.getName());
}
return names;
}
public static IRFunctionType functionType(ITypeSystem typeSystem,
List<IRParameter> parameters, IRType returnType) {
return new RFunctionType(typeSystem, parameters, returnType);
}
public static IRType union(Collection<IRType> targets) {
return new RUnionType(targets);
}
/**
* Creates new instance of the array type with the specified itemType.
*/
public static IRArrayType arrayOf(ITypeSystem typeSystem,
final IRType itemType) {
return new RArrayType(typeSystem, itemType);
}
public static IRLocalType localType(String name, IValueReference value) {
return new RLocalType(name, value);
}
/**
* Converts the specified type expression to the "resolved" type expression.
*/
public static IRType create(ITypeSystem typeSystem, JSType type) {
if (type == null) {
return null;
}
final IRType result = type.toRType(typeSystem);
if (result != null) {
return result;
}
for (IRTypeFactory factory : TypeInfoManager.getRTypeFactories()) {
final IRType runtimeType = factory.create(typeSystem, type);
if (runtimeType != null) {
return runtimeType;
}
}
throw new IllegalArgumentException("Unsupported type "
+ type.getClass().getName());
}
public static final IRSimpleType FUNCTION = simple(TypeSystems.GLOBAL,
Types.FUNCTION);
public static final IRSimpleType STRING = simple(TypeSystems.GLOBAL,
Types.STRING);
public static final IRSimpleType NUMBER = simple(TypeSystems.GLOBAL,
Types.NUMBER);
public static final IRSimpleType BOOLEAN = simple(TypeSystems.GLOBAL,
Types.BOOLEAN);
public static final IRSimpleType OBJECT = simple(TypeSystems.GLOBAL,
Types.OBJECT);
public static final IRSimpleType REGEXP = simple(TypeSystems.GLOBAL,
Types.REGEXP);
public static final IRSimpleType ERROR = simple(TypeSystems.GLOBAL,
Types.ERROR);
public static List<IRType> convert(ITypeSystem typeSystem, List<JSType> args) {
final int size = args.size();
final List<IRType> parameters = new ArrayList<IRType>(size);
for (int i = 0; i < size; ++i) {
parameters.add(create(typeSystem, args.get(i)));
}
return parameters;
}
/**
* Checks if the specified type is {@link #undefined()} or is an union
* containing {@link #undefined()}.
*/
public static boolean isUndefined(IRType type) {
if (type == undefined()) {
return true;
} else if (type instanceof IRUnionType) {
return ((IRUnionType) type).getTargets().contains(undefined());
} else {
return false;
}
}
}