String methods are intrinsics

This commit is contained in:
Dmitry Jemerov
2011-07-14 18:08:57 +02:00
parent fe6eed2913
commit 0518cba342
17 changed files with 139 additions and 72 deletions
@@ -0,0 +1,7 @@
package org.jetbrains.jet.codegen;
/**
* @author yole
*/
public interface Callable {
}
@@ -11,7 +11,7 @@ import java.util.List;
/**
* @author yole
*/
public class CallableMethod {
public class CallableMethod implements Callable {
private String owner;
private final Method signature;
private final int invokeOpcode;
@@ -102,6 +102,10 @@ public class ExpressionCodegen extends JetVisitor {
this.intrinsics = state.getIntrinsics();
}
public JetTypeMapper getTypeMapper() {
return state.getTypeMapper();
}
public void addTypeParameter(TypeParameterDescriptor typeParameter, StackValue expression) {
typeParameterExpressions.put(typeParameter, expression);
}
@@ -618,7 +622,7 @@ public class ExpressionCodegen extends JetVisitor {
Label label = new Label();
v.visitLabel(label);
v.visitLineNumber(lineNumber+1, label); // 1-based
v.visitLineNumber(lineNumber + 1, label); // 1-based
}
}
@@ -653,7 +657,7 @@ public class ExpressionCodegen extends JetVisitor {
final IntrinsicMethod intrinsic = intrinsics.getIntrinsic(descriptor);
if (intrinsic != null) {
final Type expectedType = expressionType(expression);
myStack.push(intrinsic.generate(this, v, expectedType, expression, Collections.<JetExpression>emptyList()));
myStack.push(intrinsic.generate(this, v, expectedType, expression, Collections.<JetExpression>emptyList(), false));
return;
}
@@ -788,33 +792,10 @@ public class ExpressionCodegen extends JetVisitor {
generateConstructorCall(expression, (JetSimpleNameExpression) callee);
}
else if (funDescriptor instanceof FunctionDescriptor) {
final IntrinsicMethod intrinsic = intrinsics.getIntrinsic(funDescriptor);
if (intrinsic != null) {
List<JetExpression> args = new ArrayList<JetExpression>();
for (JetArgument argument : expression.getValueArguments()) {
args.add(argument.getArgumentExpression());
}
myStack.push(intrinsic.generate(this, v, expressionType(expression), expression, args));
return;
}
final FunctionDescriptor fd = (FunctionDescriptor) funDescriptor;
PsiElement declarationPsiElement = resolveCalleeToDeclaration(funDescriptor);
CallableMethod callableMethod;
if (declarationPsiElement instanceof PsiMethod || declarationPsiElement instanceof JetNamedFunction) {
callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declarationPsiElement);
}
else {
callableMethod = ClosureCodegen.asCallableMethod(fd);
}
invokeMethodWithArguments(callableMethod, expression);
final Type callReturnType = callableMethod.getSignature().getReturnType();
if (callReturnType != Type.VOID_TYPE) {
final Type retType = typeMapper.mapType(fd.getReturnType());
StackValue.onStack(callReturnType).upcast(retType, v);
myStack.push(StackValue.onStack(retType));
final StackValue stackValue = invokeFunction(expression, fd, false);
if (stackValue != null) {
myStack.push(stackValue);
}
}
else {
@@ -822,25 +803,50 @@ public class ExpressionCodegen extends JetVisitor {
}
}
private PsiElement resolveCalleeToDeclaration(DeclarationDescriptor funDescriptor) {
PsiElement declarationPsiElement = bindingContext.getDeclarationPsiElement(funDescriptor);
if (declarationPsiElement == null && isClass(funDescriptor.getContainingDeclaration(), "String")) {
final Project project = state.getProject();
PsiClass jlString = JavaPsiFacade.getInstance(project).findClass("java.lang.String",
ProjectScope.getAllScope(project));
// TODO better overload mapping
final PsiMethod[] methods = jlString.findMethodsByName(funDescriptor.getName(), false);
final int arity = ((FunctionDescriptor) funDescriptor).getValueParameters().size();
for (PsiMethod method : methods) {
if (method.getParameterList().getParametersCount() == arity) {
declarationPsiElement = method;
}
private StackValue invokeFunction(JetCallExpression expression, FunctionDescriptor fd, boolean haveReceiver) {
Callable callableMethod = resolveToCallable(fd);
return invokeCallable(fd, callableMethod, expression, haveReceiver);
}
@Nullable
private StackValue invokeCallable(FunctionDescriptor fd, Callable callable, JetCallExpression expression, boolean haveReceiver) {
if (callable instanceof CallableMethod) {
final CallableMethod callableMethod = (CallableMethod) callable;
invokeMethodWithArguments(callableMethod, expression, haveReceiver);
final Type callReturnType = callableMethod.getSignature().getReturnType();
if (callReturnType != Type.VOID_TYPE) {
final Type retType = typeMapper.mapType(fd.getReturnType());
StackValue.onStack(callReturnType).upcast(retType, v);
return StackValue.onStack(retType);
}
return null;
}
if (declarationPsiElement == null) {
throw new UnsupportedOperationException("couldn't find declaration for " + funDescriptor);
else {
IntrinsicMethod intrinsic = (IntrinsicMethod) callable;
List<JetExpression> args = new ArrayList<JetExpression>();
for (JetArgument argument : expression.getValueArguments()) {
args.add(argument.getArgumentExpression());
}
return intrinsic.generate(this, v, expressionType(expression), expression, args, haveReceiver);
}
return declarationPsiElement;
}
private Callable resolveToCallable(FunctionDescriptor fd) {
final IntrinsicMethod intrinsic = intrinsics.getIntrinsic(fd);
if (intrinsic != null) {
return intrinsic;
}
PsiElement declarationPsiElement = bindingContext.getDeclarationPsiElement(fd);
CallableMethod callableMethod;
if (declarationPsiElement instanceof PsiMethod || declarationPsiElement instanceof JetNamedFunction) {
callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declarationPsiElement);
}
else {
callableMethod = ClosureCodegen.asCallableMethod(fd);
}
return callableMethod;
}
private DeclarationDescriptor resolveCalleeDescriptor(JetCallExpression call) {
@@ -859,7 +865,7 @@ public class ExpressionCodegen extends JetVisitor {
invokeMethodWithArguments(callableMethod, expression, false);
}
private void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression, final boolean haveReceiver) {
public void invokeMethodWithArguments(CallableMethod callableMethod, JetCall expression, final boolean haveReceiver) {
final Type calleeType = callableMethod.getGenerateCalleeType();
if (calleeType != null && expression instanceof JetCallExpression) {
gen(((JetCallExpression) expression).getCalleeExpression(), calleeType);
@@ -1091,7 +1097,7 @@ public class ExpressionCodegen extends JetVisitor {
final IntrinsicMethod intrinsic = intrinsics.getIntrinsic(op);
if (intrinsic != null) {
myStack.push(intrinsic.generate(this, v, expressionType(expression), expression,
Arrays.asList(expression.getLeft(), expression.getRight())));
Arrays.asList(expression.getLeft(), expression.getRight()), false));
return;
}
}
@@ -1358,7 +1364,7 @@ public class ExpressionCodegen extends JetVisitor {
final IntrinsicMethod intrinsic = intrinsics.getIntrinsic(op);
final JetExpression operand = unaryExpression.getBaseExpression();
if (intrinsic != null) {
myStack.push(intrinsic.generate(this, v, asmType, unaryExpression, Collections.singletonList(operand)));
myStack.push(intrinsic.generate(this, v, asmType, unaryExpression, Collections.singletonList(operand), false));
return true;
}
else if (isNumberPrimitive(asmType) && (op.getName().equals("inc") || op.getName().equals("dec"))) {
@@ -1863,13 +1869,10 @@ public class ExpressionCodegen extends JetVisitor {
if (call instanceof JetCallExpression) {
v.load(subjectLocal, subjectType);
final DeclarationDescriptor declarationDescriptor = resolveCalleeDescriptor((JetCallExpression) call);
final PsiElement declaration = resolveCalleeToDeclaration(declarationDescriptor);
final CallableMethod callableMethod = typeMapper.mapToCallableMethod((PsiNamedElement) declaration);
if (callableMethod.getSignature().getReturnType() != Type.BOOLEAN_TYPE) {
throw new UnsupportedOperationException("calls in pattern matching must return boolean");
if (!(declarationDescriptor instanceof FunctionDescriptor)) {
throw new UnsupportedOperationException("expected function descriptor in when condition with call, found " + declarationDescriptor);
}
invokeMethodWithArguments(callableMethod, (JetCallExpression) call, true);
conditionValue = StackValue.onStack(Type.BOOLEAN_TYPE);
conditionValue = invokeFunction((JetCallExpression) call, (FunctionDescriptor) declarationDescriptor, true);
}
else if (call instanceof JetSimpleNameExpression) {
final DeclarationDescriptor descriptor = bindingContext.resolveReferenceExpression((JetSimpleNameExpression) call);
@@ -30,7 +30,7 @@ public class GenerationState {
this.project = project;
this.standardLibrary = JetStandardLibrary.getJetStandardLibrary(project);
this.factory = new ClassFileFactory(project, text, this);
this.intrinsics = new IntrinsicMethods(standardLibrary);
this.intrinsics = new IntrinsicMethods(project, standardLibrary);
}
public ClassFileFactory getFactory() {
@@ -404,7 +404,7 @@ public class JetTypeMapper {
return mapToCallableMethod((PsiMethod) declaration);
}
if (!(declaration instanceof JetNamedFunction)) {
throw new UnsupportedOperationException("unknown declaration type");
throw new UnsupportedOperationException("unknown declaration type " + declaration);
}
JetNamedFunction f = (JetNamedFunction) declaration;
final FunctionDescriptor functionDescriptor = bindingContext.getFunctionDescriptor(f);
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class ArraySize implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.ensureReceiverOnStack(element, null);
v.arraylength();
return StackValue.onStack(Type.INT_TYPE);
@@ -20,7 +20,7 @@ public class BinaryOp implements IntrinsicMethod {
}
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.gen(arguments.get(0), expectedType);
codegen.gen(arguments.get(1), expectedType);
v.visitInsn(expectedType.getOpcode(opcode));
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class Concat implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.generateStringBuilderConstructor();
codegen.invokeAppend(arguments.get(0));
codegen.invokeAppend(arguments.get(1));
@@ -1,6 +1,7 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.Callable;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetExpression;
@@ -12,6 +13,6 @@ import java.util.List;
/**
* @author yole
*/
public interface IntrinsicMethod {
StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments);
public interface IntrinsicMethod extends Callable {
StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver);
}
@@ -1,6 +1,11 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.google.common.collect.ImmutableList;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.ProjectScope;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.lang.types.JetStandardClasses;
@@ -8,10 +13,7 @@ import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author yole
@@ -25,10 +27,12 @@ public class IntrinsicMethods {
private static final List<String> PRIMITIVE_NUMBER_TYPES = ImmutableList.of("Boolean", "Byte", "Char", "Short", "Int", "Float", "Long", "Double");
private final Project myProject;
private final JetStandardLibrary myStdLib;
private final Map<DeclarationDescriptor, IntrinsicMethod> myMethods = new HashMap<DeclarationDescriptor, IntrinsicMethod>();
public IntrinsicMethods(JetStandardLibrary stdlib) {
public IntrinsicMethods(Project project, JetStandardLibrary stdlib) {
myProject = project;
myStdLib = stdlib;
List<String> primitiveCastMethods = ImmutableList.of("dbl", "flt", "lng", "int", "chr", "sht", "byt");
for (String method : primitiveCastMethods) {
@@ -60,6 +64,26 @@ public class IntrinsicMethods {
declareIntrinsicFunction("Boolean", "not", 0, new Not());
declareIntrinsicFunction("String", "plus", 1, new Concat());
declareIntrinsicStringMethods();
}
private void declareIntrinsicStringMethods() {
final ClassDescriptor stringClass = myStdLib.getString();
final Collection<DeclarationDescriptor> stringMembers = stringClass.getMemberScope(Collections.<TypeProjection>emptyList()).getAllDescriptors();
final PsiClass stringPsiClass = JavaPsiFacade.getInstance(myProject).findClass("java.lang.String",
ProjectScope.getLibrariesScope(myProject));
for (DeclarationDescriptor stringMember : stringMembers) {
if (stringMember instanceof FunctionDescriptor) {
final FunctionDescriptor stringMethod = (FunctionDescriptor) stringMember;
final PsiMethod[] methods = stringPsiClass.findMethodsByName(stringMember.getName(), false);
for (PsiMethod method : methods) {
if (method.getParameterList().getParametersCount() == stringMethod.getValueParameters().size()) {
myMethods.put(stringMethod, new PsiMethodCall(method));
}
}
}
}
}
private void declareBinaryOp(String methodName, int opcode) {
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class Inv implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.putTopOfStack(expectedType);
v.aconst(-1);
v.xor(expectedType);
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class Not implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
final StackValue stackValue = codegen.generateIntermediateValue(arguments.get(0));
return StackValue.not(stackValue);
}
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class NumberCast implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.putTopOfStack(expectedType);
return StackValue.onStack(expectedType);
}
@@ -0,0 +1,32 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import org.jetbrains.jet.codegen.CallableMethod;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.List;
/**
* @author yole
*/
public class PsiMethodCall implements IntrinsicMethod {
private final PsiMethod myMethod;
public PsiMethodCall(PsiMethod method) {
myMethod = method;
}
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element,
List<JetExpression> arguments, boolean haveReceiver) {
final CallableMethod callableMethod = codegen.getTypeMapper().mapToCallableMethod(myMethod);
codegen.invokeMethodWithArguments(callableMethod, (JetCallExpression) element, haveReceiver);
return StackValue.onStack(callableMethod.getSignature().getReturnType());
}
}
@@ -17,7 +17,7 @@ import java.util.List;
*/
public class TypeInfo implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
final List<JetTypeProjection> typeArguments = ((JetCallExpression) element).getTypeArguments();
if (typeArguments.size() != 1) {
throw new UnsupportedOperationException("one type argument expected");
@@ -14,7 +14,7 @@ import java.util.List;
*/
public class UnaryMinus implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.gen(arguments.get(0), expectedType);
v.neg(expectedType);
return StackValue.onStack(expectedType);
@@ -15,7 +15,7 @@ import java.util.List;
*/
public class ValueTypeInfo implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments) {
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, boolean haveReceiver) {
codegen.gen(arguments.get(0), JetTypeMapper.TYPE_JET_OBJECT);
v.invokeinterface("jet/JetObject", "getTypeInfo", "()Ljet/typeinfo/TypeInfo;");
return StackValue.onStack(JetTypeMapper.TYPE_TYPEINFO);