| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation and others. |
| * 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 |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.ruby.typeinference; |
| |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.dltk.evaluation.types.AmbiguousType; |
| import org.eclipse.dltk.evaluation.types.IClassType; |
| import org.eclipse.dltk.evaluation.types.UnknownType; |
| import org.eclipse.dltk.ti.types.IEvaluatedType; |
| |
| public class BuiltinMethods { |
| |
| private static final RubyClassType fixnumType = new RubyClassType("Fixnum"); //$NON-NLS-1$ |
| |
| private static final RubyClassType arrayType = new RubyClassType("Array"); //$NON-NLS-1$ |
| |
| private static final RubyClassType strType = new RubyClassType("String"); //$NON-NLS-1$ |
| |
| private static final RubyClassType classType = new RubyClassType("Class"); //$NON-NLS-1$ |
| |
| private static final IEvaluatedType boolType = new AmbiguousType(new IEvaluatedType[] { |
| new RubyClassType("TrueClass"), //$NON-NLS-1$ |
| new RubyClassType("FalseClass")}); //$NON-NLS-1$ |
| |
| private static final class NewMethod implements IntrinsicMethod { |
| @Override |
| public boolean dependsOnArguments() { |
| return false; |
| } |
| |
| @Override |
| public boolean dependsOnReceiver() { |
| return true; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType() { |
| return null; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType(IEvaluatedType receiver, IEvaluatedType[] arguments) { |
| if (receiver instanceof RubyClassType) { |
| RubyClassType rubyClassType = (RubyClassType) receiver; |
| String key = rubyClassType.getModelKey(); |
| if (!key.endsWith("%")) //$NON-NLS-1$ |
| return new RubyClassType(key + "%"); //$NON-NLS-1$ |
| return null; |
| } else if (receiver instanceof AmbiguousType) { |
| Set<IEvaluatedType> possibleReturns = new HashSet<IEvaluatedType>(); |
| AmbiguousType ambiguousType = (AmbiguousType) receiver; |
| IEvaluatedType[] possibleTypes = ambiguousType.getPossibleTypes(); |
| for (int i = 0; i < possibleTypes.length; i++) { |
| IEvaluatedType type = possibleTypes[i]; |
| IEvaluatedType possibleReturn = getReturnType(type, arguments); |
| possibleReturns.add(possibleReturn); |
| } |
| return RubyTypeInferencingUtils.combineTypes(possibleReturns); |
| } |
| return null; |
| } |
| } |
| |
| private static final class ClassMethod implements IntrinsicMethod { |
| |
| @Override |
| public boolean dependsOnArguments() { |
| return false; |
| } |
| |
| @Override |
| public boolean dependsOnReceiver() { |
| return true; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType() { |
| return classType; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType(IEvaluatedType receiver, IEvaluatedType[] arguments) { |
| return RubyTypeInferencingUtils.getAmbiguousMetaType(receiver); |
| } |
| } |
| |
| private static final class ReceiverTypeReturningMethod implements IntrinsicMethod { |
| @Override |
| public boolean dependsOnArguments() { |
| return false; |
| } |
| |
| @Override |
| public boolean dependsOnReceiver() { |
| return true; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType() { |
| return null; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType(IEvaluatedType receiver, IEvaluatedType[] arguments) { |
| return receiver; |
| } |
| } |
| |
| public interface IntrinsicMethod { |
| |
| boolean dependsOnReceiver(); |
| |
| boolean dependsOnArguments(); |
| |
| IEvaluatedType getReturnType(); |
| |
| IEvaluatedType getReturnType(IEvaluatedType receiver, IEvaluatedType[] arguments); |
| |
| } |
| |
| private static class SimpleIntrinsicMethod implements IntrinsicMethod { |
| |
| private final IEvaluatedType returnType; |
| |
| public SimpleIntrinsicMethod(IEvaluatedType returnType) { |
| this.returnType = returnType; |
| } |
| |
| @Override |
| public boolean dependsOnArguments() { |
| return false; |
| } |
| |
| @Override |
| public boolean dependsOnReceiver() { |
| return false; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType() { |
| return returnType; |
| } |
| |
| @Override |
| public IEvaluatedType getReturnType(IEvaluatedType receiver, IEvaluatedType[] arguments) { |
| return getReturnType(); |
| } |
| |
| } |
| |
| public static class BuiltinClass { |
| |
| private final String name; |
| |
| public BuiltinClass(String name) { |
| this.name = name; |
| } |
| |
| private Map<String, IntrinsicMethod> methods = new HashMap<String, IntrinsicMethod>(); |
| |
| public void addMethod(String name, IntrinsicMethod method) { |
| methods.put(name, method); |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| IntrinsicMethod getMethod(String name) { |
| return methods.get(name); |
| } |
| |
| public Collection<IntrinsicMethod> getMethods(){ |
| return methods.values(); |
| } |
| |
| } |
| |
| private static Map<String, BuiltinClass> builtinClasses = new HashMap<String, BuiltinClass>(); |
| |
| private static BuiltinClass addClass(BuiltinClass klass) { |
| builtinClasses.put(klass.getName(), klass); |
| return klass; |
| } |
| |
| static { |
| BuiltinClass klass; |
| |
| klass = addClass(new BuiltinClass("Object")); //$NON-NLS-1$ |
| klass.addMethod("object_id", new SimpleIntrinsicMethod(fixnumType)); //$NON-NLS-1$ |
| klass.addMethod("__id__", new SimpleIntrinsicMethod(fixnumType)); //$NON-NLS-1$ |
| klass.addMethod("send", new SimpleIntrinsicMethod(UnknownType.INSTANCE)); //$NON-NLS-1$ |
| klass.addMethod("class", new ClassMethod()); //$NON-NLS-1$ |
| klass.addMethod("clone", new ReceiverTypeReturningMethod()); //$NON-NLS-1$ |
| klass.addMethod("dup", new ReceiverTypeReturningMethod()); //$NON-NLS-1$ |
| klass.addMethod("extend", new SimpleIntrinsicMethod(UnknownType.INSTANCE)); //$NON-NLS-1$ |
| klass.addMethod("freeze", new ReceiverTypeReturningMethod()); //$NON-NLS-1$ |
| klass.addMethod("frozen?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("hash", new SimpleIntrinsicMethod(fixnumType)); //$NON-NLS-1$ |
| klass.addMethod("inspect", new SimpleIntrinsicMethod(strType)); //$NON-NLS-1$ |
| klass.addMethod("instance_eval", new SimpleIntrinsicMethod(UnknownType.INSTANCE)); //$NON-NLS-1$ |
| klass.addMethod("instance_of?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("instance_variable_get", new SimpleIntrinsicMethod(UnknownType.INSTANCE)); //$NON-NLS-1$ |
| klass.addMethod("instance_variable_set", new SimpleIntrinsicMethod(UnknownType.INSTANCE)); //$NON-NLS-1$ |
| klass.addMethod("instance_variables", new SimpleIntrinsicMethod(arrayType)); //$NON-NLS-1$ |
| klass.addMethod("is_a?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("kind_of?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("nil?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("respond_to?", new SimpleIntrinsicMethod(boolType)); //$NON-NLS-1$ |
| klass.addMethod("to_a", new SimpleIntrinsicMethod(arrayType)); //$NON-NLS-1$ |
| klass.addMethod("to_s", new SimpleIntrinsicMethod(strType)); //$NON-NLS-1$ |
| klass.addMethod("to_i", new SimpleIntrinsicMethod(fixnumType)); //$NON-NLS-1$ |
| |
| klass = addClass(new BuiltinClass("Class")); //$NON-NLS-1$ |
| klass.addMethod("new", new NewMethod()); //$NON-NLS-1$ |
| |
| klass = addClass(new BuiltinClass("Fixnum")); //$NON-NLS-1$ |
| klass.addMethod("abs", new SimpleIntrinsicMethod(fixnumType)); //$NON-NLS-1$ |
| } |
| |
| public static IEvaluatedType getIntrinsicMethodReturnType(IEvaluatedType receiver, |
| String methodName, IEvaluatedType[] arguments) { |
| if (receiver instanceof IClassType) |
| return getIntrinsicMethodReturnType((IClassType) receiver, methodName, arguments); |
| else if (receiver instanceof AmbiguousType) { |
| Set<IEvaluatedType> possibleReturns = new HashSet<IEvaluatedType>(); |
| AmbiguousType ambiguousType = (AmbiguousType) receiver; |
| IEvaluatedType[] possibleTypes = ambiguousType.getPossibleTypes(); |
| for (int i = 0; i < possibleTypes.length; i++) { |
| IEvaluatedType type = possibleTypes[i]; |
| IEvaluatedType possibleReturn = getIntrinsicMethodReturnType(type, methodName, |
| arguments); |
| possibleReturns.add(possibleReturn); |
| } |
| return RubyTypeInferencingUtils.combineTypes(possibleReturns); |
| } |
| return null; |
| } |
| |
| public static IEvaluatedType getIntrinsicMethodReturnType(IClassType receiver, |
| String methodName, IEvaluatedType[] arguments) { |
| String className = getPossibleIntrinsicClassName(receiver); |
| BuiltinClass klass = builtinClasses.get(className); |
| if (klass != null) { |
| IntrinsicMethod method = klass.getMethod(methodName); |
| if (method != null) { |
| return method.getReturnType(receiver, arguments); |
| } |
| } |
| return null; |
| } |
| |
| public static Collection<IntrinsicMethod> getIntrinsicMethods(IEvaluatedType receiver) { |
| if (receiver instanceof IClassType) { |
| String className = getPossibleIntrinsicClassName((IClassType) receiver); |
| BuiltinClass klass = builtinClasses.get(className); |
| if (klass != null) |
| return klass.getMethods(); |
| else |
| return null; |
| } else if (receiver instanceof AmbiguousType) { |
| Set<IntrinsicMethod> methods = new HashSet<IntrinsicMethod>(); |
| AmbiguousType ambiguousType = (AmbiguousType) receiver; |
| IEvaluatedType[] possibleTypes = ambiguousType.getPossibleTypes(); |
| for (int i = 0; i < possibleTypes.length; i++) { |
| IEvaluatedType type = possibleTypes[i]; |
| Collection<IntrinsicMethod> typeMethods = getIntrinsicMethods(type); |
| methods.addAll(typeMethods); |
| } |
| return methods; |
| } |
| return null; |
| } |
| |
| private static String getPossibleIntrinsicClassName(IClassType receiver) { |
| /*if (receiver instanceof RubyMetaClassType) { |
| return "Class"; //$NON-NLS-1$ |
| } else*/ if (receiver instanceof RubyClassType) { |
| // RubyClassType type = (RubyClassType) receiver; |
| // return RubyMixinModel.getInstance().createRubyClass(type).getName(); |
| return "Class"; //$NON-NLS-1$ |
| } /*else if (receiver instanceof SimpleType) { |
| SimpleType type = (SimpleType) receiver; |
| switch (type.getType()) { |
| case SimpleType.TYPE_NUMBER: |
| return "Fixnum"; //$NON-NLS-1$ |
| case SimpleType.TYPE_STRING: |
| return "Str"; //$NON-NLS-1$ |
| } |
| }*/ |
| return null; |
| } |
| |
| |
| |
| } |