"compiler" folder created

This commit is contained in:
Andrey Breslav
2011-09-09 21:47:21 +04:00
parent 443c342725
commit 116f35c650
375 changed files with 3 additions and 11 deletions
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="IDEA 10.x" jdkType="IDEA JDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="frontend" />
<orderEntry type="module" module-name="frontend.java" />
<orderEntry type="library" name="asm" level="project" />
<orderEntry type="module" module-name="stdlib" />
</component>
</module>
@@ -0,0 +1,7 @@
package org.jetbrains.jet.codegen;
/**
* @author yole
*/
public interface Callable {
}
@@ -0,0 +1,92 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
import java.util.List;
/**
* @author yole
*/
public class CallableMethod implements Callable {
private String owner;
private final Method signature;
private final int invokeOpcode;
private final List<Type> valueParameterTypes;
private boolean acceptsTypeArguments = false;
private boolean needsReceiverOnStack = false;
private boolean ownerFromCall = false;
private ClassDescriptor receiverClass = null;
private Type generateCalleeType = null;
public CallableMethod(String owner, Method signature, int invokeOpcode, List<Type> valueParameterTypes) {
this.owner = owner;
this.signature = signature;
this.invokeOpcode = invokeOpcode;
this.valueParameterTypes = valueParameterTypes;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public Method getSignature() {
return signature;
}
public int getInvokeOpcode() {
return invokeOpcode;
}
public List<Type> getValueParameterTypes() {
return valueParameterTypes;
}
public boolean acceptsTypeArguments() {
return acceptsTypeArguments;
}
public void setAcceptsTypeArguments(boolean acceptsTypeArguments) {
this.acceptsTypeArguments = acceptsTypeArguments;
}
public void setNeedsReceiver(@Nullable ClassDescriptor receiverClass) {
needsReceiverOnStack = true;
this.receiverClass = receiverClass;
}
public boolean needsReceiverOnStack() {
return needsReceiverOnStack;
}
public boolean isOwnerFromCall() {
return ownerFromCall;
}
public void setOwnerFromCall(boolean ownerFromCall) {
this.ownerFromCall = ownerFromCall;
}
public ClassDescriptor getReceiverClass() {
return receiverClass;
}
void invoke(InstructionAdapter v) {
v.visitMethodInsn(getInvokeOpcode(), getOwner(), getSignature().getName(), getSignature().getDescriptor());
}
public void requestGenerateCallee(Type objectType) {
generateCalleeType = objectType;
}
public Type getGenerateCalleeType() {
return generateCalleeType;
}
}
@@ -0,0 +1,125 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author max
* @author yole
*/
public abstract class ClassBodyCodegen {
protected final GenerationState state;
protected final JetClassOrObject myClass;
protected final OwnerKind kind;
protected final ClassDescriptor descriptor;
protected final ClassVisitor v;
protected final ClassContext context;
protected final List<CodeChunk> staticInitializerChunks = new ArrayList<CodeChunk>();
public ClassBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) {
this.state = state;
descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
myClass = aClass;
this.context = context;
this.kind = context.getContextKind();
this.v = v;
}
public void generate() {
generateDeclaration();
generateSyntheticParts();
generateClassBody();
generateStaticInitializer();
v.visitEnd();
}
protected abstract void generateDeclaration();
protected void generateSyntheticParts() {
}
private void generateClassBody() {
final FunctionCodegen functionCodegen = new FunctionCodegen(context, v, state);
final PropertyCodegen propertyCodegen = new PropertyCodegen(context, v, functionCodegen, state);
for (JetDeclaration declaration : myClass.getDeclarations()) {
generateDeclaration(propertyCodegen, declaration, functionCodegen);
}
generatePrimaryConstructorProperties(propertyCodegen);
}
protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration, FunctionCodegen functionCodegen) {
if (declaration instanceof JetProperty) {
propertyCodegen.gen((JetProperty) declaration);
}
else if (declaration instanceof JetNamedFunction) {
try {
functionCodegen.gen((JetNamedFunction) declaration);
} catch (RuntimeException e) {
throw new RuntimeException("Error generating method " + myClass.getName() + "." + declaration.getName() + " in " + context, e);
}
}
}
private void generatePrimaryConstructorProperties(PropertyCodegen propertyCodegen) {
OwnerKind kind = context.getContextKind();
for (JetParameter p : getPrimaryConstructorParameters()) {
if (p.getValOrVarNode() != null) {
PropertyDescriptor propertyDescriptor = state.getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, p);
if (propertyDescriptor != null) {
propertyCodegen.generateDefaultGetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
if (propertyDescriptor.isVar()) {
propertyCodegen.generateDefaultSetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
}
if (!(kind instanceof OwnerKind.DelegateKind) && kind != OwnerKind.INTERFACE && state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
v.visitField(Opcodes.ACC_PRIVATE, p.getName(), state.getTypeMapper().mapType(propertyDescriptor.getOutType()).getDescriptor(), null, null);
}
}
}
}
}
protected List<JetParameter> getPrimaryConstructorParameters() {
if (myClass instanceof JetClass) {
return ((JetClass) myClass).getPrimaryConstructorParameters();
}
return Collections.emptyList();
}
private void generateStaticInitializer() {
if (staticInitializerChunks.size() > 0) {
final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC,
"<clinit>", "()V", null, null);
mv.visitCode();
InstructionAdapter v = new InstructionAdapter(mv);
for (CodeChunk chunk : staticInitializerChunks) {
chunk.generate(v);
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
}
@@ -0,0 +1,60 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
/**
* @author max
*/
public class ClassCodegen {
private final GenerationState state;
public ClassCodegen(GenerationState state) {
this.state = state;
}
public void generate(ClassContext parentContext, JetClassOrObject aClass) {
GenerationState.prepareAnonymousClasses((JetElement) aClass, state.getTypeMapper());
if (aClass instanceof JetObjectDeclaration) {
generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION);
}
else {
generateInterface(parentContext, aClass);
generateImplementation(parentContext, aClass, OwnerKind.IMPLEMENTATION);
}
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
final ClassContext contextForInners = parentContext.intoClass(descriptor, OwnerKind.IMPLEMENTATION);
for (JetDeclaration declaration : aClass.getDeclarations()) {
if (declaration instanceof JetClass && !(declaration instanceof JetEnumEntry)) {
generate(contextForInners, (JetClass) declaration);
}
}
}
private void generateInterface(ClassContext parentContext, JetClassOrObject aClass) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
final ClassVisitor visitor = state.forClassInterface(descriptor);
new InterfaceBodyCodegen(aClass, parentContext.intoClass(descriptor, OwnerKind.INTERFACE), visitor, state).generate();
}
private void generateImplementation(ClassContext parentContext, JetClassOrObject aClass, OwnerKind kind) {
ClassDescriptor descriptor = state.getBindingContext().get(BindingContext.CLASS, aClass);
ClassVisitor v = kind == OwnerKind.IMPLEMENTATION
? state.forClassImplementation(descriptor)
: state.forClassDelegatingImplementation(descriptor);
new ImplementationBodyCodegen(aClass, parentContext.intoClass(descriptor, kind), v, state).generate();
}
public static void newTypeInfo(InstructionAdapter v, boolean isNullable, Type asmType) {
v.aconst(asmType);
v.iconst(isNullable?1:0);
v.invokestatic("jet/typeinfo/TypeInfo", "getTypeInfo", "(Ljava/lang/Class;Z)Ljet/typeinfo/TypeInfo;");
}
}
@@ -0,0 +1,186 @@
/*
* @author max
*/
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.HashMap;
public class ClassContext {
public static final ClassContext STATIC = new ClassContext(null, OwnerKind.NAMESPACE, null, null, null);
private final DeclarationDescriptor contextType;
private final OwnerKind contextKind;
private final StackValue thisExpression;
private final ClassContext parentContext;
private final ClosureCodegen closure;
private boolean thisWasUsed = false;
HashMap<JetType,Integer> typeInfoConstants;
public ClassContext(DeclarationDescriptor contextType, OwnerKind contextKind, StackValue thisExpression, ClassContext parentContext, ClosureCodegen closureCodegen) {
this.contextType = contextType;
this.contextKind = contextKind;
this.thisExpression = thisExpression;
this.parentContext = parentContext;
closure = closureCodegen;
}
public DeclarationDescriptor getContextDescriptor() {
return contextType;
}
public String getNamespaceClassName() {
if(parentContext != STATIC)
return parentContext.getNamespaceClassName();
return NamespaceCodegen.getJVMClassName(contextType.getName());
}
public OwnerKind getContextKind() {
return contextKind;
}
public StackValue getThisExpression() {
if (parentContext == null) return StackValue.none();
thisWasUsed = true;
if (thisExpression != null) return thisExpression;
return parentContext.getThisExpression();
}
public ClassContext intoNamespace(NamespaceDescriptor descriptor) {
return new ClassContext(descriptor, OwnerKind.NAMESPACE, null, this, null);
}
public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind) {
final StackValue thisValue;
thisValue = StackValue.local(0, JetTypeMapper.TYPE_OBJECT);
return new ClassContext(descriptor, kind, thisValue, this, null);
}
public ClassContext intoFunction(FunctionDescriptor descriptor) {
int thisIdx = -1;
if (getContextKind() != OwnerKind.NAMESPACE) {
thisIdx++;
}
final boolean hasReceiver = descriptor.getReceiverType() != null;
if (hasReceiver) {
thisIdx++;
}
return new ClassContext(descriptor, getContextKind(), hasReceiver ? StackValue.local(thisIdx, JetTypeMapper.TYPE_OBJECT) : null, this, null);
}
public ClassContext intoClosure(String internalClassName, ClosureCodegen closureCodegen) {
final Type type = enclosingClassType(closureCodegen.state.getTypeMapper());
StackValue outerClass = type != null
? StackValue.instanceField(type, internalClassName, "this$0")
: StackValue.local(0, JetTypeMapper.TYPE_OBJECT);
return new ClassContext(null, OwnerKind.IMPLEMENTATION, outerClass, this, closureCodegen);
}
public FrameMap prepareFrame() {
FrameMap frameMap = new FrameMap();
if (getContextKind() != OwnerKind.NAMESPACE) {
frameMap.enterTemp(); // 0 slot for this
}
if (hasReceiver()) {
frameMap.enterTemp(); // Next slot for fake this
}
return frameMap;
}
private JetType receiverType() {
return contextType instanceof FunctionDescriptor ? ((FunctionDescriptor) contextType).getReceiverType() : null;
}
private boolean hasReceiver() {
return receiverType() != null;
}
public ClassContext getParentContext() {
return parentContext;
}
public Type jvmType(JetTypeMapper mapper) {
if (contextType instanceof ClassDescriptor) {
return mapper.jvmType((ClassDescriptor) contextType, contextKind);
}
else if (closure != null) {
return Type.getObjectType(closure.name);
}
else {
return parentContext != null ? parentContext.jvmType(mapper) : JetTypeMapper.TYPE_OBJECT;
}
}
public DeclarationDescriptor getContextClass() {
DeclarationDescriptor descriptor = getContextDescriptor();
if (descriptor == null || descriptor instanceof ClassDescriptor || descriptor instanceof NamespaceDescriptor) return descriptor;
final ClassContext parent = getParentContext();
return parent != null ? parent.getContextClass() : null;
}
public boolean isThisWasUsed() {
return thisWasUsed;
}
public StackValue lookupInContext(DeclarationDescriptor d, InstructionAdapter v) {
final ClosureCodegen top = closure;
if (top != null) {
final StackValue answer = top.lookupInContext(d);
if (answer != null) return answer;
final StackValue thisContext = getThisExpression();
thisContext.put(thisContext.type, v);
}
return parentContext != null ? parentContext.lookupInContext(d, v) : null;
}
public Type enclosingClassType(JetTypeMapper mapper) {
DeclarationDescriptor descriptor = getContextDescriptor();
if (descriptor instanceof ClassDescriptor) {
return Type.getObjectType(mapper.jvmName((ClassDescriptor) descriptor, OwnerKind.INTERFACE));
}
if (descriptor instanceof NamespaceDescriptor) {
return null;
}
if (closure != null) {
return Type.getObjectType(closure.name);
}
final ClassContext parent = getParentContext();
return parent != null ? parent.enclosingClassType(mapper) : null;
}
public int getTypeInfoConstantIndex(JetType type) {
if(parentContext != STATIC)
return parentContext.getTypeInfoConstantIndex(type);
if(typeInfoConstants == null)
typeInfoConstants = new HashMap<JetType, Integer>();
Integer index = typeInfoConstants.get(type);
if(index == null) {
index = typeInfoConstants.size();
typeInfoConstants.put(type, index);
}
return index;
}
}
@@ -0,0 +1,95 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import org.jetbrains.jet.lang.psi.JetNamespace;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.TraceClassVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
/**
* @author max
*/
public class ClassFileFactory {
private final Project project;
private final boolean isText;
private final Map<String, NamespaceCodegen> ns2codegen = new HashMap<String, NamespaceCodegen>();
private final Map<String, ClassVisitor> generators = new LinkedHashMap<String, ClassVisitor>();
private boolean isDone = false;
public final GenerationState state;
public ClassFileFactory(Project project, boolean text, GenerationState state) {
this.project = project;
isText = text;
this.state = state;
}
ClassVisitor newVisitor(String filePath) {
ClassVisitor visitor;
if (isText) {
visitor = new TraceClassVisitor(new PrintWriter(new StringWriter()));
}
else {
visitor = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
}
generators.put(filePath, visitor);
return visitor;
}
ClassVisitor forAnonymousSubclass(String className) {
return newVisitor(className + ".class");
}
NamespaceCodegen forNamespace(JetNamespace namespace) {
assert !isDone : "Already done!";
String fqName = namespace.getFQName();
NamespaceCodegen codegen = ns2codegen.get(fqName);
if (codegen == null) {
final ClassVisitor classVisitor = newVisitor(NamespaceCodegen.getJVMClassName(fqName) + ".class");
codegen = new NamespaceCodegen(classVisitor, fqName, state, namespace.getContainingFile());
ns2codegen.put(fqName, codegen);
}
return codegen;
}
private void done() {
if (!isDone) {
isDone = true;
for (NamespaceCodegen codegen : ns2codegen.values()) {
codegen.done();
}
}
}
public String asText(String file) {
assert isText : "Need to create with text=true";
done();
TraceClassVisitor visitor = (TraceClassVisitor) generators.get(file);
StringWriter writer = new StringWriter();
visitor.print(new PrintWriter(writer));
return writer.toString();
}
public byte[] asBytes(String file) {
assert !isText : "This is debug stuff, only produces texts.";
done();
ClassWriter visitor = (ClassWriter) generators.get(file);
return visitor.toByteArray();
}
public List<String> files() {
done();
return new ArrayList<String>(generators.keySet());
}
}
@@ -0,0 +1,258 @@
/*
* @author max
*/
package org.jetbrains.jet.codegen;
import com.intellij.openapi.util.Pair;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.JetFunctionLiteral;
import org.jetbrains.jet.lang.psi.JetFunctionLiteralExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.JetType;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.signature.SignatureWriter;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ClosureCodegen {
public final GenerationState state;
private final ExpressionCodegen exprContext;
private final ClassContext context;
private ClassVisitor cv = null;
public String name = null;
private Map<DeclarationDescriptor, EnclosedValueDescriptor> closure = new LinkedHashMap<DeclarationDescriptor, EnclosedValueDescriptor>();
public ClosureCodegen(GenerationState state, ExpressionCodegen exprContext, ClassContext context) {
this.state = state;
this.exprContext = exprContext;
this.context = context;
}
public static Method erasedInvokeSignature(FunctionDescriptor fd) {
boolean isExtensionFunction = fd.getReceiverType() != null;
int paramCount = fd.getValueParameters().size();
if (isExtensionFunction) {
paramCount++;
}
Type[] args = new Type[paramCount];
Arrays.fill(args, JetTypeMapper.TYPE_OBJECT);
return new Method("invoke", JetTypeMapper.TYPE_OBJECT, args);
}
public Method invokeSignature(FunctionDescriptor fd) {
return state.getTypeMapper().mapSignature("invoke", fd);
}
public StackValue lookupInContext(DeclarationDescriptor d) {
if (d instanceof VariableDescriptor) {
VariableDescriptor vd = (VariableDescriptor) d;
EnclosedValueDescriptor answer = closure.get(vd);
if (answer != null) return answer.getInnerValue();
final int idx = exprContext.lookupLocal(vd);
if (idx < 0) return null;
final Type type = state.getTypeMapper().mapType(vd.getOutType());
StackValue outerValue = StackValue.local(idx, type);
final String fieldName = "$" + (closure.size() + 1);
StackValue innerValue = StackValue.field(type, name, fieldName, false);
cv.visitField(Opcodes.ACC_PUBLIC, fieldName, type.getDescriptor(), null, null);
answer = new EnclosedValueDescriptor(d, innerValue, outerValue);
closure.put(d, answer);
return innerValue;
}
return null;
}
public GeneratedAnonymousClassDescriptor gen(JetFunctionLiteralExpression fun) {
final Pair<String, ClassVisitor> nameAndVisitor = state.forAnonymousSubclass(fun);
final FunctionDescriptor funDescriptor = (FunctionDescriptor) state.getBindingContext().get(BindingContext.DECLARATION_TO_DESCRIPTOR, fun);
cv = nameAndVisitor.getSecond();
name = nameAndVisitor.getFirst();
SignatureWriter signatureWriter = new SignatureWriter();
final List<ValueParameterDescriptor> parameters = funDescriptor.getValueParameters();
final String funClass = getInternalClassName(funDescriptor);
signatureWriter.visitClassType(funClass);
for (ValueParameterDescriptor parameter : parameters) {
appendType(signatureWriter, parameter.getOutType(), '=');
}
appendType(signatureWriter, funDescriptor.getReturnType(), '=');
signatureWriter.visitEnd();
cv.visit(Opcodes.V1_6,
Opcodes.ACC_PUBLIC,
name,
null,
funClass,
new String[0]
);
cv.visitSource(fun.getContainingFile().getName(), null);
generateBridge(name, funDescriptor, cv);
boolean captureThis = generateBody(funDescriptor, cv, fun.getFunctionLiteral());
final Type enclosingType = context.enclosingClassType(state.getTypeMapper());
if (enclosingType == null) captureThis = false;
final Method constructor = generateConstructor(funClass, captureThis);
if (captureThis) {
cv.visitField(Opcodes.ACC_PRIVATE, "this$0", enclosingType.getDescriptor(), null, null);
}
cv.visitEnd();
final GeneratedAnonymousClassDescriptor answer = new GeneratedAnonymousClassDescriptor(name, constructor, captureThis);
for (DeclarationDescriptor descriptor : closure.keySet()) {
final EnclosedValueDescriptor valueDescriptor = closure.get(descriptor);
answer.addArg(valueDescriptor.getOuterValue());
}
return answer;
}
private boolean generateBody(FunctionDescriptor funDescriptor, ClassVisitor cv, JetFunctionLiteral body) {
final ClassContext closureContext = context.intoClosure(name, this);
FunctionCodegen fc = new FunctionCodegen(closureContext, cv, state);
fc.generateMethod(body, invokeSignature(funDescriptor), funDescriptor);
return closureContext.isThisWasUsed();
}
private void generateBridge(String className, FunctionDescriptor funDescriptor, ClassVisitor cv) {
final Method bridge = erasedInvokeSignature(funDescriptor);
final Method delegate = invokeSignature(funDescriptor);
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "invoke", bridge.getDescriptor(), state.getTypeMapper().genericSignature(funDescriptor), new String[0]);
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, Type.getObjectType(className));
final JetType receiverType = funDescriptor.getReceiverType();
int count = 1;
if (receiverType != null) {
StackValue.local(count, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, iv);
StackValue.onStack(JetTypeMapper.TYPE_OBJECT).upcast(state.getTypeMapper().mapType(receiverType), iv);
count++;
}
final List<ValueParameterDescriptor> params = funDescriptor.getValueParameters();
for (ValueParameterDescriptor param : params) {
StackValue.local(count, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, iv);
StackValue.onStack(JetTypeMapper.TYPE_OBJECT).upcast(state.getTypeMapper().mapType(param.getOutType()), iv);
count++;
}
iv.invokespecial(className, "invoke", delegate.getDescriptor());
StackValue.onStack(delegate.getReturnType()).put(JetTypeMapper.TYPE_OBJECT, iv);
iv.areturn(JetTypeMapper.TYPE_OBJECT);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private Method generateConstructor(String funClass, boolean captureThis) {
int argCount = closure.size();
if (captureThis) {
argCount++;
}
Type[] argTypes = new Type[argCount];
int i = 0;
if (captureThis) {
i = 1;
argTypes[0] = context.enclosingClassType(state.getTypeMapper());
}
for (DeclarationDescriptor descriptor : closure.keySet()) {
argTypes[i++] = state.getTypeMapper().mapType(((VariableDescriptor) descriptor).getOutType());
}
final Method constructor = new Method("<init>", Type.VOID_TYPE, argTypes);
final MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "<init>", constructor.getDescriptor(), null, new String[0]);
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, Type.getObjectType(funClass));
iv.invokespecial(funClass, "<init>", "()V");
i = 1;
for (Type type : argTypes) {
StackValue.local(0, JetTypeMapper.TYPE_OBJECT).put(JetTypeMapper.TYPE_OBJECT, iv);
StackValue.local(i, type).put(type, iv);
final String fieldName;
if (captureThis && i == 1) {
fieldName = "this$0";
captureThis = false;
}
else {
fieldName = "$" + (i);
i++;
}
StackValue.field(type, name, fieldName, false).store(iv);
}
iv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
return constructor;
}
public static String getInternalClassName(FunctionDescriptor descriptor) {
final int paramCount = descriptor.getValueParameters().size();
if (descriptor.getReceiverType() != null) {
return "jet/ExtensionFunction" + paramCount;
}
else {
return "jet/Function" + paramCount;
}
}
private void appendType(SignatureWriter signatureWriter, JetType type, char variance) {
signatureWriter.visitTypeArgument(variance);
final JetTypeMapper typeMapper = state.getTypeMapper();
final Type rawRetType = typeMapper.boxType(typeMapper.mapType(type));
signatureWriter.visitClassType(rawRetType.getInternalName());
signatureWriter.visitEnd();
}
public static CallableMethod asCallableMethod(FunctionDescriptor fd) {
Method descriptor = erasedInvokeSignature(fd);
String owner = getInternalClassName(fd);
final CallableMethod result = new CallableMethod(owner, descriptor, Opcodes.INVOKEVIRTUAL, Arrays.asList(descriptor.getArgumentTypes()));
if (fd.getReceiverType() != null) {
result.setNeedsReceiver(null);
}
result.requestGenerateCallee(Type.getObjectType(getInternalClassName(fd)));
return result;
}
}
@@ -0,0 +1,10 @@
package org.jetbrains.jet.codegen;
import org.objectweb.asm.commons.InstructionAdapter;
/**
* @author yole
*/
public interface CodeChunk {
void generate(InstructionAdapter v);
}
@@ -0,0 +1,65 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.objectweb.asm.Type;
import java.util.Collections;
import java.util.List;
/**
* @author yole
*/
public class ConstructorFrameMap extends FrameMap {
private int myOuterThisIndex = -1;
private int myFirstTypeParameter = -1;
private int myTypeParameterCount = 0;
public ConstructorFrameMap(CallableMethod callableMethod, @Nullable ConstructorDescriptor descriptor, OwnerKind kind) {
enterTemp(); // this
ClassDescriptor classDescriptor = null;
if (descriptor != null) {
classDescriptor = descriptor.getContainingDeclaration();
if (classDescriptor.getContainingDeclaration() instanceof ClassDescriptor) {
myOuterThisIndex = enterTemp(); // outer class instance
}
}
List<Type> explicitArgTypes = callableMethod.getValueParameterTypes();
List<ValueParameterDescriptor> paramDescrs = descriptor != null
? descriptor.getValueParameters()
: Collections.<ValueParameterDescriptor>emptyList();
for (int i = 0; i < paramDescrs.size(); i++) {
ValueParameterDescriptor parameter = paramDescrs.get(i);
enter(parameter, explicitArgTypes.get(i).getSize());
}
if (classDescriptor != null) {
myTypeParameterCount = classDescriptor.getTypeConstructor().getParameters().size();
if (kind == OwnerKind.IMPLEMENTATION) {
if (myTypeParameterCount > 0) {
myFirstTypeParameter = enterTemp();
for (int i = 1; i < myTypeParameterCount; i++) {
enterTemp();
}
}
}
}
}
public int getOuterThisIndex() {
return myOuterThisIndex;
}
public int getFirstTypeParameter() {
return myFirstTypeParameter;
}
public int getTypeParameterCount() {
return myTypeParameterCount;
}
}
@@ -0,0 +1,30 @@
/*
* @author max
*/
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
public class EnclosedValueDescriptor {
private final DeclarationDescriptor descriptor;
private final StackValue innerValue;
private final StackValue outerValue;
public EnclosedValueDescriptor(DeclarationDescriptor descriptor, StackValue innerValue, StackValue outerValue) {
this.descriptor = descriptor;
this.innerValue = innerValue;
this.outerValue = outerValue;
}
public DeclarationDescriptor getDescriptor() {
return descriptor;
}
public StackValue getInnerValue() {
return innerValue;
}
public StackValue getOuterValue() {
return outerValue;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
package org.jetbrains.jet.codegen;
import gnu.trove.TObjectIntHashMap;
import gnu.trove.TObjectIntIterator;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import java.util.ArrayList;
import java.util.List;
/**
* @author max
*/
public class FrameMap {
private final TObjectIntHashMap<DeclarationDescriptor> myVarIndex = new TObjectIntHashMap<DeclarationDescriptor>();
private final TObjectIntHashMap<DeclarationDescriptor> myVarSizes = new TObjectIntHashMap<DeclarationDescriptor>();
private int myMaxIndex = 0;
public void enter(DeclarationDescriptor descriptor, int size) {
myVarIndex.put(descriptor, myMaxIndex);
myMaxIndex += size;
myVarSizes.put(descriptor, size);
}
public int leave(DeclarationDescriptor descriptor) {
int size = myVarSizes.get(descriptor);
myMaxIndex -= size;
myVarSizes.remove(descriptor);
return myVarIndex.remove(descriptor);
}
public int enterTemp() {
return myMaxIndex++;
}
public int enterTemp(int size) {
int result = myMaxIndex;
myMaxIndex += size;
return result;
}
public void leaveTemp() {
myMaxIndex--;
}
public void leaveTemp(int size) {
myMaxIndex -= size;
}
public int getIndex(DeclarationDescriptor descriptor) {
return myVarIndex.contains(descriptor) ? myVarIndex.get(descriptor) : -1;
}
public Mark mark() {
return new Mark(myMaxIndex);
}
public class Mark {
private final int myIndex;
public Mark(int index) {
myIndex = index;
}
public void dropTo() {
List<DeclarationDescriptor> descriptorsToDrop = new ArrayList<DeclarationDescriptor>();
TObjectIntIterator<DeclarationDescriptor> iterator = myVarIndex.iterator();
while (iterator.hasNext()) {
iterator.advance();
if (iterator.value() >= myIndex) {
descriptorsToDrop.add(iterator.key());
}
}
for (DeclarationDescriptor declarationDescriptor : descriptorsToDrop) {
myVarIndex.remove(declarationDescriptor);
myVarSizes.remove(declarationDescriptor);
}
myMaxIndex = myIndex;
}
}
}
@@ -0,0 +1,146 @@
package org.jetbrains.jet.codegen;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
import java.util.List;
import java.util.Set;
/**
* @author max
* @author yole
*/
public class FunctionCodegen {
private final ClassContext owner;
private final ClassVisitor v;
private final GenerationState state;
public FunctionCodegen(ClassContext owner, ClassVisitor v, GenerationState state) {
this.owner = owner;
this.v = v;
this.state = state;
}
public void gen(JetNamedFunction f) {
Method method = state.getTypeMapper().mapToCallableMethod(f).getSignature();
final FunctionDescriptor functionDescriptor = state.getBindingContext().get(BindingContext.FUNCTION, f);
generateMethod(f, method, functionDescriptor);
}
public void generateMethod(JetDeclarationWithBody f, Method jvmMethod, FunctionDescriptor functionDescriptor) {
ClassContext funContext = owner.intoFunction(functionDescriptor);
final JetExpression bodyExpression = f.getBodyExpression();
generatedMethod(bodyExpression, jvmMethod, funContext, functionDescriptor);
}
private void generateBridgeMethod(Method function, Method overriden)
{
int flags = Opcodes.ACC_PUBLIC; // TODO.
final MethodVisitor mv = v.visitMethod(flags, function.getName(), overriden.getDescriptor(), null, null);
mv.visitCode();
Type[] argTypes = function.getArgumentTypes();
InstructionAdapterEx iv = new InstructionAdapterEx(mv);
iv.load(0, JetTypeMapper.TYPE_OBJECT);
for (int i = 0, reg = 1; i < argTypes.length; i++) {
Type argType = argTypes[i];
iv.load(reg, argType);
reg += argType.getSize();
}
iv.invokevirtual(state.getTypeMapper().jvmName((ClassDescriptor) owner.getContextDescriptor(), OwnerKind.IMPLEMENTATION), function.getName(), function.getDescriptor());
if(JetTypeMapper.isPrimitive(function.getReturnType()) && !JetTypeMapper.isPrimitive(overriden.getReturnType()))
iv.valueOf(function.getReturnType());
if(function.getReturnType() == Type.VOID_TYPE)
iv.aconst(null);
iv.areturn(overriden.getReturnType());
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void generatedMethod(JetExpression bodyExpressions,
Method jvmSignature,
ClassContext context,
FunctionDescriptor functionDescriptor)
{
List<ValueParameterDescriptor> paramDescrs = functionDescriptor.getValueParameters();
List<TypeParameterDescriptor> typeParameters = functionDescriptor.getTypeParameters();
int flags = Opcodes.ACC_PUBLIC; // TODO.
OwnerKind kind = context.getContextKind();
boolean isStatic = kind == OwnerKind.NAMESPACE;
if (isStatic) flags |= Opcodes.ACC_STATIC;
boolean isAbstract = kind == OwnerKind.INTERFACE || bodyExpressions == null;
if (isAbstract) flags |= Opcodes.ACC_ABSTRACT;
if (isAbstract && (kind == OwnerKind.IMPLEMENTATION )) {
return;
}
final MethodVisitor mv = v.visitMethod(flags, jvmSignature.getName(), jvmSignature.getDescriptor(), null, null);
if (kind != OwnerKind.INTERFACE) {
mv.visitCode();
FrameMap frameMap = context.prepareFrame();
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, jvmSignature.getReturnType(), context, state);
Type[] argTypes = jvmSignature.getArgumentTypes();
for (int i = 0; i < paramDescrs.size(); i++) {
ValueParameterDescriptor parameter = paramDescrs.get(i);
frameMap.enter(parameter, argTypes[i].getSize());
}
for (final TypeParameterDescriptor typeParameterDescriptor : typeParameters) {
int slot = frameMap.enterTemp();
codegen.addTypeParameter(typeParameterDescriptor, StackValue.local(slot, JetTypeMapper.TYPE_TYPEINFO));
}
if (kind instanceof OwnerKind.DelegateKind) {
OwnerKind.DelegateKind dk = (OwnerKind.DelegateKind) kind;
InstructionAdapter iv = new InstructionAdapter(mv);
iv.load(0, JetTypeMapper.TYPE_OBJECT);
dk.getDelegate().put(JetTypeMapper.TYPE_OBJECT, iv);
for (int i = 0; i < argTypes.length; i++) {
Type argType = argTypes[i];
iv.load(i + 1, argType);
}
iv.invokeinterface(dk.getOwnerClass(), jvmSignature.getName(), jvmSignature.getDescriptor());
iv.areturn(jvmSignature.getReturnType());
}
else if (!isAbstract) {
codegen.returnExpression(bodyExpressions);
}
mv.visitMaxs(0, 0);
mv.visitEnd();
Set<? extends FunctionDescriptor> overriddenFunctions = functionDescriptor.getOverriddenDescriptors();
if(overriddenFunctions.size() > 0) {
for (FunctionDescriptor overriddenFunction : overriddenFunctions) {
// TODO should we check params here as well?
if(!TypeUtils.equalClasses(overriddenFunction.getOriginal().getReturnType(), functionDescriptor.getReturnType())) {
generateBridgeMethod(jvmSignature, state.getTypeMapper().mapSignature(overriddenFunction.getName(), overriddenFunction.getOriginal()));
}
}
}
}
}
}
@@ -0,0 +1,42 @@
/*
* @author max
*/
package org.jetbrains.jet.codegen;
import org.objectweb.asm.commons.Method;
import java.util.ArrayList;
import java.util.List;
public class GeneratedAnonymousClassDescriptor {
private final String classname;
private Method constructor;
private final boolean captureThis;
private List<StackValue> args = new ArrayList<StackValue>();
public GeneratedAnonymousClassDescriptor(String classname, Method constructor, boolean captureThis) {
this.classname = classname;
this.constructor = constructor;
this.captureThis = captureThis;
}
public String getClassname() {
return classname;
}
public Method getConstructor() {
return constructor;
}
public void addArg(StackValue local) {
args.add(local);
}
public List<StackValue> getArgs() {
return args;
}
public boolean isCaptureThis() {
return captureThis;
}
}
@@ -0,0 +1,127 @@
/*
* @author max
*/
package org.jetbrains.jet.codegen;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.util.containers.Stack;
import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods;
import org.jetbrains.jet.lang.ErrorHandler;
import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.AnalyzingUtils;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaDefaultImports;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.commons.Method;
public class GenerationState {
private final ClassFileFactory factory;
private final Project project;
private JetTypeMapper typeMapper;
private final Stack<BindingContext> bindingContexts = new Stack<BindingContext>();
private final JetStandardLibrary standardLibrary;
private final IntrinsicMethods intrinsics;
public GenerationState(Project project, boolean text) {
this.project = project;
this.standardLibrary = JetStandardLibrary.getJetStandardLibrary(project);
this.factory = new ClassFileFactory(project, text, this);
this.intrinsics = new IntrinsicMethods(project, standardLibrary);
}
public ClassFileFactory getFactory() {
return factory;
}
public Project getProject() {
return project;
}
public JetTypeMapper getTypeMapper() {
return typeMapper;
}
public BindingContext getBindingContext() {
return bindingContexts.peek();
}
public JetStandardLibrary getStandardLibrary() {
return standardLibrary;
}
public IntrinsicMethods getIntrinsics() {
return intrinsics;
}
public ClassVisitor forClassInterface(ClassDescriptor aClass) {
return factory.newVisitor(JetTypeMapper.jvmNameForInterface(aClass) + ".class");
}
public ClassCodegen forClass() {
return new ClassCodegen(this);
}
public ClassVisitor forClassImplementation(ClassDescriptor aClass) {
return factory.newVisitor(typeMapper.jvmName(aClass, OwnerKind.IMPLEMENTATION) + ".class");
}
public ClassVisitor forClassDelegatingImplementation(ClassDescriptor aClass) {
return factory.newVisitor(JetTypeMapper.jvmNameForDelegatingImplementation(aClass) + ".class");
}
public Pair<String, ClassVisitor> forAnonymousSubclass(JetExpression expression) {
String className = typeMapper.classNameForAnonymousClass(expression);
ClassVisitor visitor = factory.forAnonymousSubclass(className);
return Pair.create(className, visitor);
}
public NamespaceCodegen forNamespace(JetNamespace namespace) {
return factory.forNamespace(namespace);
}
public void compile(JetFile psiFile) {
final JetNamespace namespace = psiFile.getRootNamespace();
NamespaceCodegen codegen = forNamespace(namespace);
final BindingContext bindingContext = AnalyzingUtils.getInstance(JavaDefaultImports.JAVA_DEFAULT_IMPORTS).analyzeNamespace(namespace, JetControlFlowDataTraceFactory.EMPTY);
bindingContexts.push(bindingContext);
typeMapper = new JetTypeMapper(standardLibrary, bindingContext);
try {
ErrorHandler.applyHandler(ErrorHandler.THROW_EXCEPTION, bindingContext);
codegen.generate(namespace);
}
finally {
bindingContexts.pop();
typeMapper = null;
}
}
public GeneratedAnonymousClassDescriptor generateObjectLiteral(JetObjectLiteralExpression literal, ExpressionCodegen context, ClassContext classContext) {
Pair<String, ClassVisitor> nameAndVisitor = forAnonymousSubclass(literal.getObjectDeclaration());
final ClassContext objectContext = classContext.intoClass(getBindingContext().get(BindingContext.CLASS, literal.getObjectDeclaration()), OwnerKind.IMPLEMENTATION);
new ImplementationBodyCodegen(literal.getObjectDeclaration(), objectContext, nameAndVisitor.getSecond(), this).generate();
return new GeneratedAnonymousClassDescriptor(nameAndVisitor.first, new Method("<init>", "()V"), false);
}
public static void prepareAnonymousClasses(JetElement aClass, final JetTypeMapper typeMapper) {
aClass.acceptChildren(new JetVisitorVoid() {
@Override
public void visitJetElement(JetElement element) {
super.visitJetElement(element);
element.acceptChildren(this);
}
@Override
public void visitObjectLiteralExpression(JetObjectLiteralExpression expression) {
typeMapper.classNameForAnonymousClass(expression.getObjectDeclaration());
}
});
}
}
@@ -0,0 +1,498 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.Variance;
import org.jetbrains.jet.lexer.JetTokens;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
import java.util.*;
/**
* @author max
* @author yole
*/
public class ImplementationBodyCodegen extends ClassBodyCodegen {
public ImplementationBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) {
super(aClass, context, v, state);
}
@Override
protected void generateDeclaration() {
String superClass = getSuperClass();
List<String> interfaces = new ArrayList<String>();
interfaces.add("jet/JetObject");
if (!(myClass instanceof JetObjectDeclaration)) {
interfaces.add(JetTypeMapper.jvmNameForInterface(descriptor));
}
else {
interfaces.addAll(InterfaceBodyCodegen.getSuperInterfaces(myClass, state.getBindingContext()));
}
boolean isAbstract = myClass instanceof JetClass && ((JetClass) myClass).hasModifier(JetTokens.ABSTRACT_KEYWORD);
v.visit(Opcodes.V1_6,
Opcodes.ACC_PUBLIC | (isAbstract ? Opcodes.ACC_ABSTRACT : 0),
jvmName(),
null,
superClass,
interfaces.toArray(new String[interfaces.size()])
);
v.visitSource(myClass.getContainingFile().getName(), null);
}
private String jvmName() {
return state.getTypeMapper().jvmName(descriptor, kind);
}
protected String getSuperClass() {
List<JetDelegationSpecifier> delegationSpecifiers = myClass.getDelegationSpecifiers();
if (delegationSpecifiers.isEmpty()) return "java/lang/Object";
JetDelegationSpecifier first = delegationSpecifiers.get(0);
if (first instanceof JetDelegatorToSuperClass || first instanceof JetDelegatorToSuperCall) {
JetType superType = state.getBindingContext().get(BindingContext.TYPE, first.getTypeReference());
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
final PsiElement declaration = state.getBindingContext().get(BindingContext.DESCRIPTOR_TO_DECLARATION, superClassDescriptor);
if (declaration instanceof PsiClass && ((PsiClass) declaration).isInterface()) {
return "java/lang/Object";
}
return state.getTypeMapper().jvmName(superClassDescriptor, kind);
}
return "java/lang/Object";
}
@Override
protected void generateSyntheticParts() {
generateFieldForTypeInfo();
generateFieldForObjectInstance();
generateFieldForClassObject();
try {
generatePrimaryConstructor();
}
catch(RuntimeException e) {
throw new RuntimeException("Error generating primary constructor of class " + myClass.getName() + " with kind " + kind, e);
}
generateGetTypeInfo();
}
private void generateFieldForTypeInfo() {
final boolean typeInfoIsStatic = descriptor.getTypeConstructor().getParameters().size() == 0;
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | (typeInfoIsStatic ? Opcodes.ACC_STATIC : 0), "$typeInfo",
"Ljet/typeinfo/TypeInfo;", null, null);
if (typeInfoIsStatic) {
staticInitializerChunks.add(new CodeChunk() {
@Override
public void generate(InstructionAdapter v) {
JetTypeMapper typeMapper = state.getTypeMapper();
ClassCodegen.newTypeInfo(v, false, typeMapper.jvmType(descriptor, OwnerKind.INTERFACE));
v.putstatic(typeMapper.jvmName(descriptor, kind), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
}
});
}
}
private void generateFieldForObjectInstance() {
if (isNonLiteralObject()) {
Type type = JetTypeMapper.jetImplementationType(descriptor);
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$instance", type.getDescriptor(), null, null);
staticInitializerChunks.add(new CodeChunk() {
@Override
public void generate(InstructionAdapter v) {
String name = jvmName();
v.anew(Type.getObjectType(name));
v.dup();
v.invokespecial(name, "<init>", "()V");
v.putstatic(name, "$instance", JetTypeMapper.jetImplementationType(descriptor).getDescriptor());
}
});
}
}
private void generateFieldForClassObject() {
final JetClassObject classObject = getClassObject();
if (classObject != null) {
Type type = Type.getObjectType(state.getTypeMapper().jvmName(classObject));
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "$classobj", type.getDescriptor(), null, null);
staticInitializerChunks.add(new CodeChunk() {
@Override
public void generate(InstructionAdapter v) {
String name = state.getTypeMapper().jvmName(classObject);
final Type classObjectType = Type.getObjectType(name);
v.anew(classObjectType);
v.dup();
v.invokespecial(name, "<init>", "()V");
v.putstatic(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$classobj",
classObjectType.getDescriptor());
}
});
}
}
protected void generatePrimaryConstructor() {
ConstructorDescriptor constructorDescriptor = state.getBindingContext().get(BindingContext.CONSTRUCTOR, myClass);
if (constructorDescriptor == null && !(myClass instanceof JetObjectDeclaration) && !isEnum(myClass)) return;
Method method;
CallableMethod callableMethod;
if (constructorDescriptor == null) {
method = new Method("<init>", Type.VOID_TYPE, new Type[0]);
callableMethod = new CallableMethod("", method, Opcodes.INVOKESPECIAL, Collections.<Type>emptyList());
}
else {
callableMethod = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind);
method = callableMethod.getSignature();
}
int flags = Opcodes.ACC_PUBLIC; // TODO
final MethodVisitor mv = v.visitMethod(flags, "<init>", method.getDescriptor(), null, null);
mv.visitCode();
Type[] argTypes = method.getArgumentTypes();
List<ValueParameterDescriptor> paramDescrs = constructorDescriptor != null
? constructorDescriptor.getValueParameters()
: Collections.<ValueParameterDescriptor>emptyList();
ConstructorFrameMap frameMap = new ConstructorFrameMap(callableMethod, constructorDescriptor, kind);
final InstructionAdapter iv = new InstructionAdapter(mv);
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, context, state);
String classname = state.getTypeMapper().jvmName(descriptor, kind);
final Type classType = Type.getType("L" + classname + ";");
List<JetDelegationSpecifier> specifiers = myClass.getDelegationSpecifiers();
if (specifiers.isEmpty() || !(specifiers.get(0) instanceof JetDelegatorToSuperCall)) {
// TODO correct calculation of super class
String superClass = "java/lang/Object";
if (!specifiers.isEmpty()) {
final JetType superType = state.getBindingContext().get(BindingContext.TYPE, specifiers.get(0).getTypeReference());
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
if (superClassDescriptor.hasConstructors()) {
superClass = getSuperClass();
}
}
iv.load(0, Type.getType("L" + superClass + ";"));
iv.invokespecial(superClass, "<init>", /* TODO super constructor descriptor */"()V");
}
final ClassDescriptor outerDescriptor = getOuterClassDescriptor();
if (outerDescriptor != null && outerDescriptor.getKind() != ClassKind.OBJECT) {
final Type type = JetTypeMapper.jetImplementationType(outerDescriptor);
String interfaceDesc = type.getDescriptor();
final String fieldName = "this$0";
v.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, fieldName, interfaceDesc, null, null);
iv.load(0, classType);
iv.load(frameMap.getOuterThisIndex(), type);
iv.putfield(classname, fieldName, interfaceDesc);
}
HashSet<FunctionDescriptor> overridden = new HashSet<FunctionDescriptor>();
for (JetDeclaration declaration : myClass.getDeclarations()) {
if (declaration instanceof JetFunction) {
overridden.addAll(state.getBindingContext().get(BindingContext.FUNCTION, (JetNamedFunction) declaration).getOverriddenDescriptors());
}
}
int n = 0;
for (JetDelegationSpecifier specifier : specifiers) {
boolean delegateOnStack = specifier instanceof JetDelegatorToSuperCall && n > 0 ||
specifier instanceof JetDelegatorByExpressionSpecifier;
if (delegateOnStack) {
iv.load(0, classType);
}
if (specifier instanceof JetDelegatorToSuperCall) {
ConstructorDescriptor constructorDescriptor1 = (ConstructorDescriptor) state.getBindingContext().get(BindingContext.REFERENCE_TARGET, ((JetDelegatorToSuperCall) specifier).getCalleeExpression().getConstructorReferenceExpression());
generateDelegatorToConstructorCall(iv, codegen, (JetDelegatorToSuperCall) specifier, constructorDescriptor1, n == 0, frameMap);
}
else if (specifier instanceof JetDelegatorByExpressionSpecifier) {
codegen.genToJVMStack(((JetDelegatorByExpressionSpecifier) specifier).getDelegateExpression());
}
if (delegateOnStack) {
JetType superType = state.getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference());
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
String delegateField = "$delegate_" + n;
Type fieldType = JetTypeMapper.jetInterfaceType(superClassDescriptor);
String fieldDesc = fieldType.getDescriptor();
v.visitField(Opcodes.ACC_PRIVATE, delegateField, fieldDesc, /*TODO*/null, null);
iv.putfield(classname, delegateField, fieldDesc);
JetClass superClass = (JetClass) state.getBindingContext().get(BindingContext.DESCRIPTOR_TO_DECLARATION, superClassDescriptor);
final ClassContext delegateContext = context.intoClass(superClassDescriptor,
new OwnerKind.DelegateKind(StackValue.field(fieldType, classname, delegateField, false),
JetTypeMapper.jvmNameForInterface(superClassDescriptor)));
generateDelegates(superClass, delegateContext, overridden);
}
n++;
}
if (frameMap.getFirstTypeParameter() > 0 && kind == OwnerKind.IMPLEMENTATION) {
generateTypeInfoInitializer(frameMap.getFirstTypeParameter(), frameMap.getTypeParameterCount(), iv);
}
generateInitializers(codegen, iv);
int curParam = 0;
List<JetParameter> constructorParameters = getPrimaryConstructorParameters();
for (JetParameter parameter : constructorParameters) {
if (parameter.getValOrVarNode() != null) {
VariableDescriptor descriptor = paramDescrs.get(curParam);
Type type = state.getTypeMapper().mapType(descriptor.getOutType());
iv.load(0, classType);
iv.load(frameMap.getIndex(descriptor), type);
iv.putfield(classname, descriptor.getName(), type.getDescriptor());
}
curParam++;
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
static boolean isEnum(JetClassOrObject myClass) {
final JetModifierList modifierList = myClass.getModifierList();
return modifierList != null && modifierList.hasModifier(JetTokens.ENUM_KEYWORD);
}
@Nullable
private ClassDescriptor getOuterClassDescriptor() {
if (myClass.getParent() instanceof JetClassObject) {
return null;
}
final DeclarationDescriptor outerDescriptor = descriptor.getContainingDeclaration();
return outerDescriptor instanceof ClassDescriptor ? (ClassDescriptor) outerDescriptor : null;
}
private void generateDelegatorToConstructorCall(InstructionAdapter iv, ExpressionCodegen codegen, JetCallElement constructorCall,
ConstructorDescriptor constructorDescriptor, boolean isJavaSuperclass,
ConstructorFrameMap frameMap) {
ClassDescriptor classDecl = constructorDescriptor.getContainingDeclaration();
PsiElement declaration = state.getBindingContext().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDecl);
Type type;
if (declaration instanceof PsiClass) {
type = JetTypeMapper.psiClassType((PsiClass) declaration);
}
else {
type = JetTypeMapper.jetImplementationType(classDecl);
}
if (isJavaSuperclass) {
iv.load(0, type);
}
else {
iv.anew(type);
iv.dup();
}
if (classDecl.getContainingDeclaration() instanceof ClassDescriptor) {
iv.load(frameMap.getOuterThisIndex(), state.getTypeMapper().jvmType((ClassDescriptor) descriptor.getContainingDeclaration(), OwnerKind.IMPLEMENTATION));
}
CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind);
codegen.invokeMethodWithArguments(method, constructorCall);
}
@Override
protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration, FunctionCodegen functionCodegen) {
if (declaration instanceof JetConstructor) {
generateSecondaryConstructor((JetConstructor) declaration);
}
else if (declaration instanceof JetClassObject) {
generateClassObject((JetClassObject) declaration);
}
else {
super.generateDeclaration(propertyCodegen, declaration, functionCodegen);
}
}
private void generateSecondaryConstructor(JetConstructor constructor) {
ConstructorDescriptor constructorDescriptor = state.getBindingContext().get(BindingContext.CONSTRUCTOR, constructor);
if (constructorDescriptor == null) {
throw new UnsupportedOperationException("failed to get descriptor for secondary constructor");
}
CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, kind);
int flags = Opcodes.ACC_PUBLIC; // TODO
final MethodVisitor mv = v.visitMethod(flags, "<init>", method.getSignature().getDescriptor(), null, null);
mv.visitCode();
ConstructorFrameMap frameMap = new ConstructorFrameMap(method, constructorDescriptor, kind);
final InstructionAdapter iv = new InstructionAdapter(mv);
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, context, state);
for (JetDelegationSpecifier initializer : constructor.getInitializers()) {
if (initializer instanceof JetDelegatorToThisCall) {
JetDelegatorToThisCall thisCall = (JetDelegatorToThisCall) initializer;
DeclarationDescriptor thisDescriptor = state.getBindingContext().get(BindingContext.REFERENCE_TARGET, thisCall.getThisReference());
if (!(thisDescriptor instanceof ConstructorDescriptor)) {
throw new UnsupportedOperationException("expected 'this' delegator to resolve to constructor");
}
generateDelegatorToConstructorCall(iv, codegen, thisCall, (ConstructorDescriptor) thisDescriptor, true, frameMap);
}
else {
throw new UnsupportedOperationException("unknown initializer type");
}
}
JetExpression bodyExpression = constructor.getBodyExpression();
if (bodyExpression != null) {
codegen.gen(bodyExpression, Type.VOID_TYPE);
}
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
protected void generateTypeInfoInitializer(int firstTypeParameter, int typeParamCount, InstructionAdapter iv) {
iv.load(0, JetTypeMapper.TYPE_OBJECT);
iv.aconst(state.getTypeMapper().jvmType(descriptor, OwnerKind.INTERFACE));
iv.iconst(0);
iv.iconst(typeParamCount);
iv.newarray(JetTypeMapper.TYPE_TYPEINFOPROJECTION);
for (int i = 0; i < typeParamCount; i++) {
iv.dup();
iv.iconst(i);
iv.load(firstTypeParameter + i, JetTypeMapper.TYPE_OBJECT);
ExpressionCodegen.genTypeInfoToProjection(iv, Variance.INVARIANT);
iv.astore(JetTypeMapper.TYPE_OBJECT);
}
iv.invokestatic("jet/typeinfo/TypeInfo", "getTypeInfo", "(Ljava/lang/Class;Z[Ljet/typeinfo/TypeInfoProjection;)Ljet/typeinfo/TypeInfo;");
iv.putfield(state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
}
protected void generateInitializers(ExpressionCodegen codegen, InstructionAdapter iv) {
for (JetDeclaration declaration : myClass.getDeclarations()) {
if (declaration instanceof JetProperty) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) state.getBindingContext().get(BindingContext.VARIABLE, declaration);
if (state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
final JetExpression initializer = ((JetProperty) declaration).getInitializer();
if (initializer != null) {
CompileTimeConstant<?> compileTimeValue = state.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, initializer);
if(compileTimeValue != null) {
assert compileTimeValue != null;
Object value = compileTimeValue.getValue();
Type type = state.getTypeMapper().mapType(propertyDescriptor.getOutType());
if(JetTypeMapper.isPrimitive(type)) {
if( !propertyDescriptor.getOutType().isNullable() && value instanceof Number) {
if(type == Type.INT_TYPE && ((Number)value).intValue() == 0)
continue;
if(type == Type.BYTE_TYPE && ((Number)value).byteValue() == 0)
continue;
if(type == Type.LONG_TYPE && ((Number)value).longValue() == 0L)
continue;
if(type == Type.SHORT_TYPE && ((Number)value).shortValue() == 0)
continue;
if(type == Type.DOUBLE_TYPE && ((Number)value).doubleValue() == 0d)
continue;
if(type == Type.FLOAT_TYPE && ((Number)value).byteValue() == 0f)
continue;
}
if(type == Type.BOOLEAN_TYPE && value instanceof Boolean && !((Boolean)value))
continue;
if(type == Type.CHAR_TYPE && value instanceof Character && ((Character)value) == 0)
continue;
}
else {
if(value == null)
continue;
}
}
iv.load(0, JetTypeMapper.TYPE_OBJECT);
Type type = codegen.expressionType(initializer);
if(propertyDescriptor.getOutType().isNullable())
type = state.getTypeMapper().boxType(type);
codegen.gen(initializer, type);
codegen.intermediateValueForProperty(propertyDescriptor, false, false).store(iv);
}
}
}
else if (declaration instanceof JetClassInitializer) {
codegen.gen(((JetClassInitializer) declaration).getBody(), Type.VOID_TYPE);
}
}
}
protected void generateDelegates(JetClass toClass, ClassContext delegateContext, Set<FunctionDescriptor> overriden) {
final FunctionCodegen functionCodegen = new FunctionCodegen(delegateContext, v, state);
final PropertyCodegen propertyCodegen = new PropertyCodegen(delegateContext, v, functionCodegen, state);
for (JetDeclaration declaration : toClass.getDeclarations()) {
if (declaration instanceof JetProperty) {
propertyCodegen.gen((JetProperty) declaration);
}
else if (declaration instanceof JetFunction) {
if (!overriden.contains(state.getBindingContext().get(BindingContext.FUNCTION, declaration))) {
functionCodegen.gen((JetNamedFunction) declaration);
}
}
}
for (JetParameter p : toClass.getPrimaryConstructorParameters()) {
if (p.getValOrVarNode() != null) {
PropertyDescriptor propertyDescriptor = state.getBindingContext().get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, p);
if (propertyDescriptor != null) {
propertyCodegen.generateDefaultGetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
if (propertyDescriptor.isVar()) {
propertyCodegen.generateDefaultSetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
}
}
}
}
}
@Nullable
private JetClassObject getClassObject() {
return myClass instanceof JetClass ? ((JetClass) myClass).getClassObject() : null;
}
private boolean isNonLiteralObject() {
return myClass instanceof JetObjectDeclaration && !((JetObjectDeclaration) myClass).isObjectLiteral() &&
!(myClass.getParent() instanceof JetClassObject);
}
private void generateGetTypeInfo() {
final MethodVisitor mv = v.visitMethod(Opcodes.ACC_PUBLIC,
"getTypeInfo",
"()Ljet/typeinfo/TypeInfo;",
null /* TODO */,
null);
mv.visitCode();
InstructionAdapter v = new InstructionAdapter(mv);
ExpressionCodegen.loadTypeInfo(state.getTypeMapper(), descriptor, v);
v.areturn(JetTypeMapper.TYPE_TYPEINFO);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void generateClassObject(JetClassObject declaration) {
state.forClass().generate(context, declaration.getObjectDeclaration());
}
}
@@ -0,0 +1,92 @@
package org.jetbrains.jet.codegen;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
public class InstructionAdapterEx extends InstructionAdapter {
private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
private static final Method CHAR_VALUE = Method.getMethod("char charValue()");
private static final Method INT_VALUE = Method.getMethod("int intValue()");
private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()");
private static final Method LONG_VALUE = Method.getMethod("long longValue()");
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
public InstructionAdapterEx(MethodVisitor methodVisitor) {
super(methodVisitor);
}
private static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return JetTypeMapper.JL_BYTE_TYPE;
case Type.BOOLEAN:
return JetTypeMapper.JL_BOOLEAN_TYPE;
case Type.SHORT:
return JetTypeMapper.JL_SHORT_TYPE;
case Type.CHAR:
return JetTypeMapper.JL_CHAR_TYPE;
case Type.INT:
return JetTypeMapper.JL_INTEGER_TYPE;
case Type.FLOAT:
return JetTypeMapper.JL_FLOAT_TYPE;
case Type.LONG:
return JetTypeMapper.JL_LONG_TYPE;
case Type.DOUBLE:
return JetTypeMapper.JL_DOUBLE_TYPE;
}
return type;
}
public void valueOf(final Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
aconst(null);
} else {
Type boxed = getBoxedType(type);
invokestatic(boxed.getInternalName(), "valueOf", "(" + type.getDescriptor() + ")" + boxed.getDescriptor());
}
}
public void unbox(final Type type) {
Type t = JetTypeMapper.JL_NUMBER_TYPE;
Method sig = null;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
t = JetTypeMapper.JL_CHAR_TYPE;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = JetTypeMapper.JL_BOOLEAN_TYPE;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
sig = INT_VALUE;
}
checkcast(t);
invokevirtual(t.getInternalName(), sig.getName(), sig.getDescriptor());
}
}
@@ -0,0 +1,137 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.JetType;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/**
* @author yole
*/
public class InterfaceBodyCodegen extends ClassBodyCodegen {
private final List<JetEnumEntry> myEnumConstants = new ArrayList<JetEnumEntry>();
public InterfaceBodyCodegen(JetClassOrObject aClass, ClassContext context, ClassVisitor v, GenerationState state) {
super(aClass, context, v, state);
assert context.getContextKind() == OwnerKind.INTERFACE;
}
protected void generateDeclaration() {
Set<String> superInterfaces = getSuperInterfaces(myClass, state.getBindingContext());
String fqName = JetTypeMapper.jvmNameForInterface(descriptor);
v.visit(Opcodes.V1_6,
Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT,
fqName,
null,
"java/lang/Object",
superInterfaces.toArray(new String[superInterfaces.size()])
);
v.visitSource(myClass.getContainingFile().getName(), null);
}
static Set<String> getSuperInterfaces(JetClassOrObject aClass, final BindingContext bindingContext) {
List<JetDelegationSpecifier> delegationSpecifiers = aClass.getDelegationSpecifiers();
String superClassName = null;
Set<String> superInterfaces = new LinkedHashSet<String>();
for (JetDelegationSpecifier specifier : delegationSpecifiers) {
JetType superType = bindingContext.get(BindingContext.TYPE, specifier.getTypeReference());
ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
PsiElement superPsi = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, superClassDescriptor);
if (superPsi instanceof PsiClass) {
PsiClass psiClass = (PsiClass) superPsi;
String fqn = psiClass.getQualifiedName();
if (psiClass.isInterface()) {
superInterfaces.add(fqn.replace('.', '/'));
}
else {
if (superClassName == null) {
superClassName = fqn.replace('.', '/');
while (psiClass != null) {
for (PsiClass ifs : psiClass.getInterfaces()) {
superInterfaces.add(ifs.getQualifiedName().replace('.', '/'));
}
psiClass = psiClass.getSuperClass();
}
}
else {
throw new RuntimeException("Cannot determine single class to inherit from");
}
}
}
else {
superInterfaces.add(JetTypeMapper.jvmNameForInterface(superClassDescriptor));
}
}
return superInterfaces;
}
@Override
protected void generateDeclaration(PropertyCodegen propertyCodegen, JetDeclaration declaration, FunctionCodegen functionCodegen) {
if (declaration instanceof JetEnumEntry && !((JetEnumEntry) declaration).hasPrimaryConstructor()) {
String name = declaration.getName();
final String desc = "L" + state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE) + ";";
v.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, name, desc, null, null);
if (myEnumConstants.isEmpty()) {
staticInitializerChunks.add(new CodeChunk() {
@Override
public void generate(InstructionAdapter v) {
initializeEnumConstants(v);
}
});
}
myEnumConstants.add((JetEnumEntry) declaration);
}
else {
super.generateDeclaration(propertyCodegen, declaration, functionCodegen);
}
}
private void initializeEnumConstants(InstructionAdapter v) {
ExpressionCodegen codegen = new ExpressionCodegen(v, new FrameMap(), Type.VOID_TYPE, context, state);
for (JetEnumEntry enumConstant : myEnumConstants) {
// TODO type and constructor parameters
String intfClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.INTERFACE);
String implClass = state.getTypeMapper().jvmName(descriptor, OwnerKind.IMPLEMENTATION);
final List<JetDelegationSpecifier> delegationSpecifiers = enumConstant.getDelegationSpecifiers();
if (delegationSpecifiers.size() > 1) {
throw new UnsupportedOperationException("multiple delegation specifiers for enum constant not supported");
}
v.anew(Type.getObjectType(implClass));
v.dup();
if (delegationSpecifiers.size() == 1) {
final JetDelegationSpecifier specifier = delegationSpecifiers.get(0);
if (specifier instanceof JetDelegatorToSuperCall) {
final JetDelegatorToSuperCall superCall = (JetDelegatorToSuperCall) specifier;
ConstructorDescriptor constructorDescriptor = (ConstructorDescriptor) state.getBindingContext().get(BindingContext.REFERENCE_TARGET, superCall.getCalleeExpression().getConstructorReferenceExpression());
CallableMethod method = state.getTypeMapper().mapToCallableMethod(constructorDescriptor, OwnerKind.IMPLEMENTATION);
codegen.invokeMethodWithArguments(method, superCall);
}
else {
throw new UnsupportedOperationException("unsupported type of enum constant initializer: " + specifier);
}
}
else {
v.invokespecial(implClass, "<init>", "()V");
}
v.putstatic(intfClass, enumConstant.getName(), "L" + intfClass + ";");
}
}
}
@@ -0,0 +1,687 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import jet.JetObject;
import jet.typeinfo.TypeInfo;
import jet.typeinfo.TypeInfoProjection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import org.jetbrains.jet.resolve.DescriptorRenderer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import java.util.*;
/**
* @author yole
* @author alex.tkachman
*/
public class JetTypeMapper {
public static final Type TYPE_OBJECT = Type.getObjectType("java/lang/Object");
public static final Type TYPE_TYPEINFO = Type.getType(TypeInfo.class);
public static final Type TYPE_TYPEINFOPROJECTION = Type.getType(TypeInfoProjection.class);
public static final Type TYPE_JET_OBJECT = Type.getType(JetObject.class);
public static final Type TYPE_NOTHING = Type.getObjectType("jet/Nothing");
public static final Type JL_INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
public static final Type JL_LONG_TYPE = Type.getObjectType("java/lang/Long");
public static final Type JL_SHORT_TYPE = Type.getObjectType("java/lang/Short");
public static final Type JL_BYTE_TYPE = Type.getObjectType("java/lang/Byte");
public static final Type JL_CHAR_TYPE = Type.getObjectType("java/lang/Character");
public static final Type JL_FLOAT_TYPE = Type.getObjectType("java/lang/Float");
public static final Type JL_DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
public static final Type JL_BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
public static final Type JL_NUMBER_TYPE = Type.getObjectType("java/lang/Number");
public static final Type JL_STRING_BUILDER = Type.getObjectType("java/lang/StringBuilder");
private final JetStandardLibrary standardLibrary;
private final BindingContext bindingContext;
private final Map<JetExpression, String> classNamesForAnonymousClasses = new HashMap<JetExpression, String>();
private final Map<String, Integer> anonymousSubclassesCount = new HashMap<String, Integer>();
public JetTypeMapper(JetStandardLibrary standardLibrary, BindingContext bindingContext) {
this.standardLibrary = standardLibrary;
this.bindingContext = bindingContext;
}
static String jvmName(PsiClass psiClass) {
final String qName = psiClass.getQualifiedName();
if (qName == null) {
throw new UnsupportedOperationException("can't evaluate JVM name for anonymous class " + psiClass);
}
return qName.replace(".", "/");
}
public static boolean isIntPrimitive(Type type) {
return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE;
}
public static boolean isPrimitive(Type type) {
return type == Type.INT_TYPE
|| type == Type.SHORT_TYPE
|| type == Type.BYTE_TYPE
|| type == Type.CHAR_TYPE
|| type == Type.SHORT_TYPE
|| type == Type.FLOAT_TYPE
|| type == Type.DOUBLE_TYPE
|| type == Type.LONG_TYPE
|| type == Type.BOOLEAN_TYPE
|| type == Type.VOID_TYPE;
}
static Type psiTypeToAsm(PsiType type) {
if(type instanceof PsiArrayType) {
PsiArrayType psiArrayType = (PsiArrayType) type;
return Type.getType("[" + psiTypeToAsm(psiArrayType.getComponentType()).getDescriptor());
}
if (type instanceof PsiPrimitiveType) {
if (type == PsiType.VOID) {
return Type.VOID_TYPE;
}
if (type == PsiType.INT) {
return Type.INT_TYPE;
}
if (type == PsiType.LONG) {
return Type.LONG_TYPE;
}
if (type == PsiType.BOOLEAN) {
return Type.BOOLEAN_TYPE;
}
if (type == PsiType.BYTE) {
return Type.BYTE_TYPE;
}
if (type == PsiType.SHORT) {
return Type.SHORT_TYPE;
}
if (type == PsiType.CHAR) {
return Type.CHAR_TYPE;
}
if (type == PsiType.FLOAT) {
return Type.FLOAT_TYPE;
}
if (type == PsiType.DOUBLE) {
return Type.DOUBLE_TYPE;
}
}
if (type instanceof PsiClassType) {
PsiClass psiClass = ((PsiClassType) type).resolve();
if (psiClass instanceof PsiTypeParameter) {
final PsiClassType[] extendsListTypes = psiClass.getExtendsListTypes();
if (extendsListTypes.length > 0) {
throw new UnsupportedOperationException("should return common supertype");
}
return TYPE_OBJECT;
}
if (psiClass == null) {
throw new UnsupportedOperationException("unresolved PsiClassType: " + type);
}
return psiClassType(psiClass);
}
throw new UnsupportedOperationException("don't know how to map type " + type + " to ASM");
}
static Method getMethodDescriptor(PsiMethod method) {
Type returnType = method.isConstructor() ? Type.VOID_TYPE : psiTypeToAsm(method.getReturnType());
PsiParameter[] parameters = method.getParameterList().getParameters();
Type[] parameterTypes = new Type[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = psiTypeToAsm(parameters[i].getType());
}
return new Method(method.isConstructor() ? "<init>" : method.getName(), Type.getMethodDescriptor(returnType, parameterTypes));
}
static CallableMethod mapToCallableMethod(PsiMethod method) {
final PsiClass containingClass = method.getContainingClass();
String owner = jvmName(containingClass);
Method signature = getMethodDescriptor(method);
List<Type> valueParameterTypes = new ArrayList<Type>();
Collections.addAll(valueParameterTypes, signature.getArgumentTypes());
int opcode;
boolean needsReceiver = false;
boolean ownerFromCall = false;
if (method.isConstructor()) {
opcode = Opcodes.INVOKESPECIAL;
}
else if (method.hasModifierProperty(PsiModifier.STATIC)) {
opcode = Opcodes.INVOKESTATIC;
}
else {
needsReceiver = true;
if (containingClass.isInterface()) {
opcode = Opcodes.INVOKEINTERFACE;
}
else {
opcode = Opcodes.INVOKEVIRTUAL;
}
ownerFromCall = true;
}
final CallableMethod result = new CallableMethod(owner, signature, opcode, valueParameterTypes);
if (needsReceiver) {
result.setNeedsReceiver(null);
}
result.setOwnerFromCall(ownerFromCall);
return result;
}
public String jvmName(ClassDescriptor jetClass, OwnerKind kind) {
PsiElement declaration = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, jetClass);
if (declaration instanceof PsiClass) {
return jvmName((PsiClass) declaration);
}
if (declaration instanceof JetObjectDeclaration && ((JetObjectDeclaration) declaration).isObjectLiteral()) {
final PsiElement parent = declaration.getParent();
if (parent instanceof JetClassObject) {
JetClass containingClass = PsiTreeUtil.getParentOfType(parent, JetClass.class);
final ClassDescriptor containingClassDescriptor = bindingContext.get(BindingContext.CLASS, containingClass);
return jvmName(containingClassDescriptor, OwnerKind.INTERFACE) + "$$ClassObj";
}
String className = classNamesForAnonymousClasses.get(declaration);
if (className == null) {
throw new UnsupportedOperationException("Unexpected forward reference to anonymous class " + declaration);
}
return className;
}
return jetJvmName(jetClass, kind);
}
public String jvmName(JetClassObject classObject) {
final ClassDescriptor descriptor = bindingContext.get(BindingContext.CLASS, classObject.getObjectDeclaration());
return jvmName(descriptor, OwnerKind.IMPLEMENTATION);
}
public boolean isInterface(ClassDescriptor jetClass, OwnerKind kind) {
PsiElement declaration = bindingContext.get(BindingContext.DESCRIPTOR_TO_DECLARATION, jetClass);
if (declaration instanceof JetObjectDeclaration) {
return false;
}
return kind == OwnerKind.INTERFACE;
}
private static String jetJvmName(ClassDescriptor jetClass, OwnerKind kind) {
if (jetClass.getKind() == ClassKind.OBJECT) {
return jvmNameForImplementation(jetClass);
}
if (kind == OwnerKind.INTERFACE) {
return jvmNameForInterface(jetClass);
}
else if (kind == OwnerKind.IMPLEMENTATION) {
return jvmNameForImplementation(jetClass);
}
else {
assert false : "Unsuitable kind";
return "java/lang/Object";
}
}
public Type jvmType(ClassDescriptor jetClass, OwnerKind kind) {
if (jetClass == standardLibrary.getString()) {
return Type.getType(String.class);
}
return Type.getType("L" + jvmName(jetClass, kind) + ";");
}
static Type psiClassType(PsiClass psiClass) {
return Type.getType("L" + jvmName(psiClass) + ";");
}
static Type jetInterfaceType(ClassDescriptor classDescriptor) {
return Type.getType("L" + jvmNameForInterface(classDescriptor) + ";");
}
static Type jetImplementationType(ClassDescriptor classDescriptor) {
return Type.getType("L" + jvmNameForImplementation(classDescriptor) + ";");
}
static Type jetDelegatingImplementationType(ClassDescriptor classDescriptor) {
return Type.getType("L" + jvmNameForDelegatingImplementation(classDescriptor) + ";");
}
public static String jvmName(JetNamespace namespace) {
return NamespaceCodegen.getJVMClassName(namespace.getFQName());
}
static String jvmName(NamespaceDescriptor namespace) {
return NamespaceCodegen.getJVMClassName(DescriptorRenderer.getFQName(namespace));
}
public static String jvmNameForInterface(ClassDescriptor descriptor) {
return DescriptorRenderer.getFQName(descriptor).replace('.', '/');
}
public static String jvmNameForImplementation(ClassDescriptor descriptor) {
return jvmNameForInterface(descriptor) + "$$Impl";
}
public static String jvmNameForDelegatingImplementation(ClassDescriptor descriptor) {
return jvmNameForInterface(descriptor) + "$$DImpl";
}
public String getOwner(DeclarationDescriptor descriptor, OwnerKind kind) {
String owner;
if (descriptor.getContainingDeclaration() instanceof NamespaceDescriptorImpl) {
owner = jvmName((NamespaceDescriptor) descriptor.getContainingDeclaration());
}
else if (descriptor.getContainingDeclaration() instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration();
if (kind instanceof OwnerKind.DelegateKind) {
kind = OwnerKind.INTERFACE;
}
else if (classDescriptor.getKind() == ClassKind.OBJECT) {
kind = OwnerKind.IMPLEMENTATION;
}
owner = jvmName(classDescriptor, kind);
}
else {
throw new UnsupportedOperationException("don't know how to generate owner for parent " + descriptor.getContainingDeclaration());
}
return owner;
}
@NotNull public Type mapReturnType(@NotNull final JetType jetType, OwnerKind kind) {
if (jetType.equals(JetStandardClasses.getUnitType()) || jetType.equals(JetStandardClasses.getNothingType())) {
return Type.VOID_TYPE;
}
return mapType(jetType, kind);
}
@NotNull public Type mapReturnType(final JetType jetType) {
return mapReturnType(jetType, OwnerKind.INTERFACE);
}
@NotNull public Type mapType(final JetType jetType) {
return mapType(jetType, OwnerKind.INTERFACE);
}
@NotNull public Type mapType(@NotNull final JetType jetType, OwnerKind kind) {
if (jetType.equals(JetStandardClasses.getNothingType()) || jetType.equals(JetStandardClasses.getNullableNothingType())) {
return TYPE_NOTHING;
}
if (jetType.equals(standardLibrary.getIntType())) {
return Type.INT_TYPE;
}
if (jetType.equals(standardLibrary.getNullableIntType())) {
return JL_INTEGER_TYPE;
}
if (jetType.equals(standardLibrary.getLongType())) {
return Type.LONG_TYPE;
}
if (jetType.equals(standardLibrary.getNullableLongType())) {
return JL_LONG_TYPE;
}
if (jetType.equals(standardLibrary.getShortType())) {
return Type.SHORT_TYPE;
}
if (jetType.equals(standardLibrary.getNullableShortType())) {
return JL_SHORT_TYPE;
}
if (jetType.equals(standardLibrary.getByteType())) {
return Type.BYTE_TYPE;
}
if (jetType.equals(standardLibrary.getNullableByteType())) {
return JL_BYTE_TYPE;
}
if (jetType.equals(standardLibrary.getCharType())) {
return Type.CHAR_TYPE;
}
if (jetType.equals(standardLibrary.getNullableCharType())) {
return JL_CHAR_TYPE;
}
if (jetType.equals(standardLibrary.getFloatType())) {
return Type.FLOAT_TYPE;
}
if (jetType.equals(standardLibrary.getNullableFloatType())) {
return JL_FLOAT_TYPE;
}
if (jetType.equals(standardLibrary.getDoubleType())) {
return Type.DOUBLE_TYPE;
}
if (jetType.equals(standardLibrary.getNullableDoubleType())) {
return JL_DOUBLE_TYPE;
}
if (jetType.equals(standardLibrary.getBooleanType())) {
return Type.BOOLEAN_TYPE;
}
if (jetType.equals(standardLibrary.getNullableBooleanType())) {
return JL_BOOLEAN_TYPE;
}
if (jetType.equals(standardLibrary.getStringType()) || jetType.equals(standardLibrary.getNullableStringType())) {
return Type.getType(String.class);
}
DeclarationDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
if (standardLibrary.getArray().equals(descriptor)) {
if (jetType.getArguments().size() != 1) {
throw new UnsupportedOperationException("arrays must have one type argument");
}
TypeProjection memberType = jetType.getArguments().get(0);
Type elementType = mapType(memberType.getType());
return Type.getType("[" + elementType.getDescriptor());
}
if (JetStandardClasses.getAny().equals(descriptor)) {
return Type.getType(Object.class);
}
if (descriptor instanceof ClassDescriptor) {
return Type.getObjectType(jvmName((ClassDescriptor) descriptor, kind));
}
if (descriptor instanceof TypeParameterDescriptor) {
return mapType(((TypeParameterDescriptor) descriptor).getBoundsAsType(), kind);
}
throw new UnsupportedOperationException("Unknown type " + jetType);
}
public Type boxType(Type asmType) {
switch (asmType.getSort()) {
case Type.VOID:
return Type.VOID_TYPE;
case Type.BYTE:
return JL_BYTE_TYPE;
case Type.BOOLEAN:
return JL_BOOLEAN_TYPE;
case Type.SHORT:
return JL_SHORT_TYPE;
case Type.CHAR:
return JL_CHAR_TYPE;
case Type.INT:
return JL_INTEGER_TYPE;
case Type.FLOAT:
return JL_FLOAT_TYPE;
case Type.LONG:
return JL_LONG_TYPE;
case Type.DOUBLE:
return JL_DOUBLE_TYPE;
}
return asmType;
}
private static Type getBoxedType(final Type type) {
switch (type.getSort()) {
}
return type;
}
private Method mapSignature(JetNamedFunction f, List<Type> valueParameterTypes) {
final JetTypeReference receiverTypeRef = f.getReceiverTypeRef();
final JetType receiverType = receiverTypeRef == null ? null : bindingContext.get(BindingContext.TYPE, receiverTypeRef);
final List<JetParameter> parameters = f.getValueParameters();
List<Type> parameterTypes = new ArrayList<Type>();
if (receiverType != null) {
parameterTypes.add(mapType(receiverType));
}
for (JetParameter parameter : parameters) {
final Type type = mapType(bindingContext.get(BindingContext.TYPE, parameter.getTypeReference()));
valueParameterTypes.add(type);
parameterTypes.add(type);
}
for (JetTypeParameter p: f.getTypeParameters()) {
parameterTypes.add(TYPE_TYPEINFO);
}
final JetTypeReference returnTypeRef = f.getReturnTypeRef();
Type returnType;
if (returnTypeRef == null) {
final FunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, f);
final JetType type = functionDescriptor.getReturnType();
returnType = mapReturnType(type);
}
else {
returnType = mapReturnType(bindingContext.get(BindingContext.TYPE, returnTypeRef));
}
return new Method(f.getName(), returnType, parameterTypes.toArray(new Type[parameterTypes.size()]));
}
public CallableMethod mapToCallableMethod(PsiNamedElement declaration) {
if (declaration instanceof PsiMethod) {
return mapToCallableMethod((PsiMethod) declaration);
}
if (!(declaration instanceof JetNamedFunction)) {
throw new UnsupportedOperationException("unknown declaration type " + declaration);
}
JetNamedFunction f = (JetNamedFunction) declaration;
final FunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, f);
final DeclarationDescriptor functionParent = functionDescriptor.getContainingDeclaration();
final List<Type> valueParameterTypes = new ArrayList<Type>();
Method descriptor = mapSignature(f, valueParameterTypes);
String owner;
int invokeOpcode;
boolean needsReceiver;
ClassDescriptor receiverClass = null;
if (functionParent instanceof NamespaceDescriptor) {
owner = NamespaceCodegen.getJVMClassName(DescriptorRenderer.getFQName(functionParent));
invokeOpcode = Opcodes.INVOKESTATIC;
needsReceiver = f.getReceiverTypeRef() != null;
}
else if (functionParent instanceof ClassDescriptor) {
ClassDescriptor containingClass = (ClassDescriptor) functionParent;
owner = jvmName(containingClass, OwnerKind.INTERFACE);
invokeOpcode = isInterface(containingClass, OwnerKind.INTERFACE)
? Opcodes.INVOKEINTERFACE
: Opcodes.INVOKEVIRTUAL;
needsReceiver = true;
receiverClass = containingClass;
}
else {
throw new UnsupportedOperationException("unknown function parent");
}
final CallableMethod result = new CallableMethod(owner, descriptor, invokeOpcode, valueParameterTypes);
result.setAcceptsTypeArguments(true);
if (needsReceiver) {
result.setNeedsReceiver(receiverClass);
}
return result;
}
public Method mapSignature(String name, FunctionDescriptor f) {
final JetType receiverType = f.getReceiverType();
final List<ValueParameterDescriptor> parameters = f.getValueParameters();
List<Type> parameterTypes = new ArrayList<Type>();
if (receiverType != null) {
parameterTypes.add(mapType(receiverType));
}
for (ValueParameterDescriptor parameter : parameters) {
parameterTypes.add(mapType(parameter.getOutType()));
}
Type returnType = mapReturnType(f.getReturnType());
return new Method(name, returnType, parameterTypes.toArray(new Type[parameterTypes.size()]));
}
public String genericSignature(FunctionDescriptor f) {
StringBuffer answer = new StringBuffer();
final List<TypeParameterDescriptor> typeParameters = f.getTypeParameters();
if (!typeParameters.isEmpty()) {
answer.append('<');
for (TypeParameterDescriptor p : typeParameters) {
appendTypeParameterSignature(answer, p);
}
answer.append('>');
}
answer.append('(');
for (ValueParameterDescriptor p : f.getValueParameters()) {
appendType(answer, p.getOutType());
}
answer.append(')');
appendType(answer, f.getReturnType());
return answer.toString();
}
private void appendType(StringBuffer answer, JetType type) {
answer.append(mapType(type).getDescriptor()); // TODO: type parameter references!
}
private void appendTypeParameterSignature(StringBuffer answer, TypeParameterDescriptor p) {
answer.append(p.getName()); // TODO: BOUND!
}
public Method mapGetterSignature(PropertyDescriptor descriptor) {
Type returnType = mapType(descriptor.getOutType());
return new Method(PropertyCodegen.getterName(descriptor.getName()), returnType, new Type[0]);
}
public Method mapSetterSignature(PropertyDescriptor descriptor) {
final JetType inType = descriptor.getInType();
if (inType == null) {
return null;
}
Type paramType = mapType(inType);
return new Method(PropertyCodegen.setterName(descriptor.getName()), Type.VOID_TYPE, new Type[] { paramType });
}
private Method mapConstructorSignature(ConstructorDescriptor descriptor, OwnerKind kind, List<Type> valueParameterTypes) {
List<ValueParameterDescriptor> parameters = descriptor.getOriginal().getValueParameters();
List<Type> parameterTypes = new ArrayList<Type>();
ClassDescriptor classDescriptor = descriptor.getContainingDeclaration();
final DeclarationDescriptor outerDescriptor = classDescriptor.getContainingDeclaration();
if (outerDescriptor instanceof ClassDescriptor) {
parameterTypes.add(jvmType((ClassDescriptor) outerDescriptor, OwnerKind.IMPLEMENTATION));
}
for (ValueParameterDescriptor parameter : parameters) {
final Type type = mapType(parameter.getOutType());
parameterTypes.add(type);
valueParameterTypes.add(type);
}
List<TypeParameterDescriptor> typeParameters = classDescriptor.getTypeConstructor().getParameters();
for (TypeParameterDescriptor typeParameter : typeParameters) {
parameterTypes.add(TYPE_TYPEINFO);
}
return new Method("<init>", Type.VOID_TYPE, parameterTypes.toArray(new Type[parameterTypes.size()]));
}
public CallableMethod mapToCallableMethod(ConstructorDescriptor descriptor, OwnerKind kind) {
List<Type> valueParameterTypes = new ArrayList<Type>();
final Method method = mapConstructorSignature(descriptor, kind, valueParameterTypes);
String owner = jvmName(descriptor.getContainingDeclaration(), kind);
final CallableMethod result = new CallableMethod(owner, method, Opcodes.INVOKESPECIAL, valueParameterTypes);
result.setAcceptsTypeArguments(true);
return result;
}
static int getAccessModifiers(JetDeclaration p, int defaultFlags) {
int flags = 0;
if (p.hasModifier(JetTokens.PUBLIC_KEYWORD)) {
flags |= Opcodes.ACC_PUBLIC;
}
else if (p.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
flags |= Opcodes.ACC_PRIVATE;
}
else {
flags |= defaultFlags;
}
return flags;
}
String classNameForAnonymousClass(JetExpression expression) {
String name = classNamesForAnonymousClasses.get(expression);
if (name != null) {
return name;
}
JetNamedDeclaration container = PsiTreeUtil.getParentOfType(expression, JetNamespace.class, JetClass.class, JetObjectDeclaration.class);
String baseName;
if (container instanceof JetNamespace) {
baseName = NamespaceCodegen.getJVMClassName(((JetNamespace) container).getFQName());
}
else {
ClassDescriptor aClass = bindingContext.get(BindingContext.CLASS, (JetClassOrObject) container);
baseName = JetTypeMapper.jvmNameForInterface(aClass);
}
Integer count = anonymousSubclassesCount.get(baseName);
if (count == null) count = 0;
anonymousSubclassesCount.put(baseName, count + 1);
final String className = baseName + "$" + (count + 1);
classNamesForAnonymousClasses.put(expression, className);
return className;
}
public Collection<String> allJvmNames(JetClassOrObject jetClass) {
Set<String> result = new HashSet<String>();
final ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, jetClass);
if (classDescriptor != null) {
result.add(jvmName(classDescriptor, OwnerKind.INTERFACE));
result.add(jvmName(classDescriptor, OwnerKind.IMPLEMENTATION));
}
return result;
}
public String isKnownTypeInfo(JetType jetType) {
if (jetType.equals(standardLibrary.getIntType())) {
return "INT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableIntType())) {
return "NULLABLE_INT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getLongType())) {
return "LONG_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableLongType())) {
return "NULLABLE_LONG_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getShortType())) {
return "SHORT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableShortType())) {
return "NULLABLE_SHORT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getByteType())) {
return "BYTE_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableByteType())) {
return "NULLABLE_BYTE_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getCharType())) {
return "CHAR_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableCharType())) {
return "NULLABLE_CHAR_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getFloatType())) {
return "FLOAT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableFloatType())) {
return "NULLABLE_FLOAT_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getDoubleType())) {
return "DOUBLE_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableDoubleType())) {
return "NULLABLE_DOUBLE_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getBooleanType())) {
return "BOOLEAN_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableBooleanType())) {
return "NULLABLE_BOOLEAN_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getStringType())) {
return "STRING_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableStringType())) {
return "NULLABLE_STRING_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getTuple0Type())) {
return "TUPLE0_TYPE_INFO";
}
if (jetType.equals(standardLibrary.getNullableTuple0Type())) {
return "NULLABLE_TUPLE0_TYPE_INFO";
}
return null;
}
}
@@ -0,0 +1,8 @@
package org.jetbrains.jet.codegen;
/**
* @author max
*/
public class MethodCodeGen {
}
@@ -0,0 +1,198 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.PsiFile;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.*;
import static org.objectweb.asm.Opcodes.*;
/**
* @author max
*/
public class NamespaceCodegen {
private final ClassVisitor v;
private final GenerationState state;
public NamespaceCodegen(ClassVisitor v, String fqName, GenerationState state, PsiFile sourceFile) {
this.v = v;
this.state = state;
v.visit(V1_6,
ACC_PUBLIC,
getJVMClassName(fqName),
null,
//"jet/lang/Namespace",
"java/lang/Object",
new String[0]
);
// TODO figure something out for a namespace that spans multiple files
v.visitSource(sourceFile.getName(), null);
}
public void generate(JetNamespace namespace) {
final ClassContext context = ClassContext.STATIC.intoNamespace(state.getBindingContext().get(BindingContext.NAMESPACE, namespace));
final FunctionCodegen functionCodegen = new FunctionCodegen(context, v, state);
final PropertyCodegen propertyCodegen = new PropertyCodegen(context, v, functionCodegen, state);
final ClassCodegen classCodegen = state.forClass();
GenerationState.prepareAnonymousClasses(namespace, state.getTypeMapper());
for (JetDeclaration declaration : namespace.getDeclarations()) {
if (declaration instanceof JetProperty) {
propertyCodegen.gen((JetProperty) declaration);
}
else if (declaration instanceof JetNamedFunction) {
try {
functionCodegen.gen((JetNamedFunction) declaration);
} catch (Exception e) {
throw new RuntimeException("Failed to generate function " + declaration.getName(), e);
}
}
else if (declaration instanceof JetClassOrObject) {
classCodegen.generate(context, (JetClassOrObject) declaration);
}
else if (declaration instanceof JetNamespace) {
JetNamespace childNamespace = (JetNamespace) declaration;
state.forNamespace(childNamespace).generate(childNamespace);
}
}
if (hasNonConstantPropertyInitializers(namespace, context)) {
generateStaticInitializers(namespace, context);
}
generateTypeInfoFields(namespace, context);
}
private void generateStaticInitializers(JetNamespace namespace, ClassContext context) {
MethodVisitor mv = v.visitMethod(ACC_PUBLIC | ACC_STATIC,
"<clinit>", "()V", null, null);
mv.visitCode();
FrameMap frameMap = new FrameMap();
ExpressionCodegen codegen = new ExpressionCodegen(mv, frameMap, Type.VOID_TYPE, ClassContext.STATIC, state);
for (JetDeclaration declaration : namespace.getDeclarations()) {
if (declaration instanceof JetProperty) {
final JetExpression initializer = ((JetProperty) declaration).getInitializer();
if (initializer != null && !(initializer instanceof JetConstantExpression)) {
final PropertyDescriptor descriptor = (PropertyDescriptor) state.getBindingContext().get(BindingContext.VARIABLE, (JetProperty) declaration);
codegen.genToJVMStack(initializer);
codegen.intermediateValueForProperty(descriptor, true, false).store(new InstructionAdapter(mv));
}
}
}
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
private void generateTypeInfoFields(JetNamespace namespace, ClassContext context) {
if(context.typeInfoConstants != null) {
String jvmClassName = getJVMClassName(namespace.getName());
for(Map.Entry<JetType,Integer> e : (context.typeInfoConstants != null ? context.typeInfoConstants : Collections.<JetType,Integer>emptyMap()).entrySet()) {
String fieldName = "$typeInfoCache$" + e.getValue();
v.visitField(ACC_PRIVATE|ACC_STATIC|ACC_SYNTHETIC, fieldName, "Ljet/typeinfo/TypeInfo;", null, null);
MethodVisitor mmv = v.visitMethod(ACC_PUBLIC|ACC_STATIC|ACC_SYNTHETIC, "$getCachedTypeInfo$" + e.getValue(), "()Ljet/typeinfo/TypeInfo;", null, null);
InstructionAdapterEx v = new InstructionAdapterEx(mmv);
v.visitFieldInsn(GETSTATIC, jvmClassName, fieldName, "Ljet/typeinfo/TypeInfo;");
v.visitInsn(DUP);
Label end = new Label();
v.visitJumpInsn(IFNONNULL, end);
v.pop();
generateTypeInfo(context, v, e.getKey(), state.getTypeMapper(), e.getKey());
v.dup();
v.visitFieldInsn(PUTSTATIC, jvmClassName, fieldName, "Ljet/typeinfo/TypeInfo;");
v.visitLabel(end);
v.visitInsn(ARETURN);
v.visitMaxs(0, 0);
v.visitEnd();
}
}
}
private void generateTypeInfo(ClassContext context, InstructionAdapterEx v, JetType jetType, JetTypeMapper typeMapper, JetType root) {
String knownTypeInfo = typeMapper.isKnownTypeInfo(jetType);
if(knownTypeInfo != null) {
v.getstatic("jet/typeinfo/TypeInfo", knownTypeInfo, "Ljet/typeinfo/TypeInfo;");
return;
}
DeclarationDescriptor declarationDescriptor = jetType.getConstructor().getDeclarationDescriptor();
if(!jetType.equals(root) && jetType.getArguments().size() == 0 && !(declarationDescriptor instanceof JavaClassDescriptor)) {
// TODO: we need some better checks here
v.getstatic(typeMapper.mapType(jetType, OwnerKind.IMPLEMENTATION).getInternalName(), "$typeInfo", "Ljet/typeinfo/TypeInfo;");
return;
}
boolean hasUnsubstituted = TypeUtils.hasUnsubstitutedTypeParameters(jetType);
if(!jetType.equals(root) && !hasUnsubstituted) {
int typeInfoConstantIndex = context.getTypeInfoConstantIndex(jetType);
v.invokestatic(context.getNamespaceClassName(), "$getCachedTypeInfo$" + typeInfoConstantIndex, "()Ljet/typeinfo/TypeInfo;");
return;
}
final Type jvmType = typeMapper.mapType(jetType, OwnerKind.INTERFACE);
v.aconst(jvmType);
v.iconst(jetType.isNullable() ? 1 : 0);
List<TypeProjection> arguments = jetType.getArguments();
if (arguments.size() > 0) {
v.iconst(arguments.size());
v.newarray(JetTypeMapper.TYPE_TYPEINFOPROJECTION);
for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) {
TypeProjection argument = arguments.get(i);
v.dup();
v.iconst(i);
generateTypeInfo(context, v, argument.getType(), typeMapper, root);
ExpressionCodegen.genTypeInfoToProjection(v, argument.getProjectionKind());
v.astore(JetTypeMapper.TYPE_OBJECT);
}
v.invokestatic("jet/typeinfo/TypeInfo", "getTypeInfo", "(Ljava/lang/Class;Z[Ljet/typeinfo/TypeInfoProjection;)Ljet/typeinfo/TypeInfo;");
}
else {
v.invokestatic("jet/typeinfo/TypeInfo", "getTypeInfo", "(Ljava/lang/Class;Z)Ljet/typeinfo/TypeInfo;");
}
}
private static boolean hasNonConstantPropertyInitializers(JetNamespace namespace, ClassContext context) {
for (JetDeclaration declaration : namespace.getDeclarations()) {
if (declaration instanceof JetProperty) {
final JetExpression initializer = ((JetProperty) declaration).getInitializer();
if (initializer != null && !(initializer instanceof JetConstantExpression)) {
return true;
}
}
}
return false;
}
public void done() {
v.visitEnd();
}
public static String getJVMClassName(String fqName) {
if (fqName.length() == 0) {
return "namespace";
}
return fqName.replace('.', '/') + "/namespace";
}
}
@@ -0,0 +1,40 @@
package org.jetbrains.jet.codegen;
/**
* @author max
*/
public class OwnerKind {
private final String name;
public OwnerKind(String name) {
this.name = name;
}
public static final OwnerKind NAMESPACE = new OwnerKind("namespace");
public static final OwnerKind INTERFACE = new OwnerKind("interface");
public static final OwnerKind IMPLEMENTATION = new OwnerKind("implementation");
public static class DelegateKind extends OwnerKind {
private final StackValue delegate;
private final String ownerClass;
public DelegateKind(StackValue delegate, String ownerClass) {
super("delegateKind");
this.delegate = delegate;
this.ownerClass = ownerClass;
}
public StackValue getDelegate() {
return delegate;
}
public String getOwnerClass() {
return ownerClass;
}
}
@Override
public String toString() {
return "OwnerKind(" + name + ")";
}
}
@@ -0,0 +1,223 @@
package org.jetbrains.jet.codegen;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lexer.JetTokens;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
/**
* @author max
*/
public class PropertyCodegen {
private final GenerationState state;
private final FunctionCodegen functionCodegen;
private final ClassVisitor v;
private final OwnerKind kind;
public PropertyCodegen(ClassContext context, ClassVisitor v, FunctionCodegen functionCodegen, GenerationState state) {
this.v = v;
this.functionCodegen = functionCodegen;
this.state = state;
this.kind = context.getContextKind();
}
public void gen(JetProperty p) {
final VariableDescriptor descriptor = state.getBindingContext().get(BindingContext.VARIABLE, p);
if (!(descriptor instanceof PropertyDescriptor)) {
throw new UnsupportedOperationException("expect a property to have a property descriptor");
}
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
if (kind == OwnerKind.NAMESPACE || kind == OwnerKind.IMPLEMENTATION) {
generateBackingField(p, propertyDescriptor);
generateGetter(p, propertyDescriptor);
generateSetter(p, propertyDescriptor);
}
else if (kind == OwnerKind.INTERFACE) {
final JetPropertyAccessor getter = p.getGetter();
if ((getter != null && !getter.hasModifier(JetTokens.PRIVATE_KEYWORD) ||
(getter == null && isExternallyAccessible(p)))) {
v.visitMethod(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC,
getterName(p.getName()),
state.getTypeMapper().mapGetterSignature(propertyDescriptor).getDescriptor(),
null, null);
}
final JetPropertyAccessor setter = p.getSetter();
if ((setter != null && !setter.hasModifier(JetTokens.PRIVATE_KEYWORD) ||
(setter == null && isExternallyAccessible(p) && p.isVar()))) {
v.visitMethod(Opcodes.ACC_ABSTRACT | Opcodes.ACC_PUBLIC,
setterName(p.getName()),
state.getTypeMapper().mapSetterSignature(propertyDescriptor).getDescriptor(),
null, null);
}
}
else if (kind instanceof OwnerKind.DelegateKind) {
generateDefaultGetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
if (propertyDescriptor.isVar()) {
generateDefaultSetter(propertyDescriptor, Opcodes.ACC_PUBLIC);
}
}
}
private void generateBackingField(JetProperty p, PropertyDescriptor propertyDescriptor) {
if (state.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor)) {
Object value = null;
final JetExpression initializer = p.getInitializer();
if (initializer != null) {
if (initializer instanceof JetConstantExpression) {
CompileTimeConstant<?> compileTimeValue = state.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, initializer);
assert compileTimeValue != null;
value = compileTimeValue.getValue();
}
}
final int modifiers;
if (kind == OwnerKind.NAMESPACE) {
int access = isExternallyAccessible(p) ? Opcodes.ACC_PUBLIC : Opcodes.ACC_PRIVATE;
modifiers = access | Opcodes.ACC_STATIC;
}
else {
modifiers = Opcodes.ACC_PRIVATE;
}
v.visitField(modifiers, p.getName(), state.getTypeMapper().mapType(propertyDescriptor.getOutType()).getDescriptor(), null, value);
}
}
private void generateGetter(JetProperty p, PropertyDescriptor propertyDescriptor) {
final JetPropertyAccessor getter = p.getGetter();
if (getter != null) {
if (getter.getBodyExpression() != null) {
functionCodegen.generateMethod(getter, state.getTypeMapper().mapGetterSignature(propertyDescriptor), propertyDescriptor.getGetter());
}
else if (!getter.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
generateDefaultGetter(p, getter);
}
}
else if (isExternallyAccessible(p)) {
generateDefaultGetter(p, p);
}
}
private static boolean isExternallyAccessible(JetProperty p) {
return !p.hasModifier(JetTokens.PRIVATE_KEYWORD);
}
private void generateSetter(JetProperty p, PropertyDescriptor propertyDescriptor) {
final JetPropertyAccessor setter = p.getSetter();
if (setter != null) {
if (setter.getBodyExpression() != null) {
final PropertySetterDescriptor setterDescriptor = propertyDescriptor.getSetter();
assert setterDescriptor != null;
functionCodegen.generateMethod(setter, state.getTypeMapper().mapSetterSignature(propertyDescriptor), setterDescriptor);
}
else if (!p.hasModifier(JetTokens.PRIVATE_KEYWORD)) {
generateDefaultSetter(p, setter);
}
}
else if (isExternallyAccessible(p) && p.isVar()) {
generateDefaultSetter(p, p);
}
}
private void generateDefaultGetter(JetProperty p, JetDeclaration declaration) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) state.getBindingContext().get(BindingContext.VARIABLE, p);
int flags = JetTypeMapper.getAccessModifiers(declaration, Opcodes.ACC_PUBLIC);
generateDefaultGetter(propertyDescriptor, flags);
}
public void generateDefaultGetter(PropertyDescriptor propertyDescriptor, int flags) {
if (kind == OwnerKind.NAMESPACE) {
flags |= Opcodes.ACC_STATIC;
}
else if (kind == OwnerKind.INTERFACE) {
flags |= Opcodes.ACC_ABSTRACT;
}
final String signature = state.getTypeMapper().mapGetterSignature(propertyDescriptor).getDescriptor();
String getterName = getterName(propertyDescriptor.getName());
MethodVisitor mv = v.visitMethod(flags, getterName, signature, null, null);
if (kind != OwnerKind.INTERFACE) {
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
if (kind != OwnerKind.NAMESPACE) {
iv.load(0, JetTypeMapper.TYPE_OBJECT);
}
final Type type = state.getTypeMapper().mapType(propertyDescriptor.getOutType());
if (kind instanceof OwnerKind.DelegateKind) {
OwnerKind.DelegateKind dk = (OwnerKind.DelegateKind) kind;
dk.getDelegate().put(JetTypeMapper.TYPE_OBJECT, iv);
iv.invokeinterface(dk.getOwnerClass(), getterName, signature);
}
else {
iv.visitFieldInsn(kind == OwnerKind.NAMESPACE ? Opcodes.GETSTATIC : Opcodes.GETFIELD,
state.getTypeMapper().getOwner(propertyDescriptor, kind), propertyDescriptor.getName(),
type.getDescriptor());
}
iv.areturn(type);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
private void generateDefaultSetter(JetProperty p, JetDeclaration declaration) {
final PropertyDescriptor propertyDescriptor = (PropertyDescriptor) state.getBindingContext().get(BindingContext.VARIABLE, p);
int flags = JetTypeMapper.getAccessModifiers(declaration, Opcodes.ACC_PUBLIC);
generateDefaultSetter(propertyDescriptor, flags);
}
public void generateDefaultSetter(PropertyDescriptor propertyDescriptor, int flags) {
if (kind == OwnerKind.NAMESPACE) {
flags |= Opcodes.ACC_STATIC;
}
else if (kind == OwnerKind.INTERFACE) {
flags |= Opcodes.ACC_ABSTRACT;
}
final String signature = state.getTypeMapper().mapSetterSignature(propertyDescriptor).getDescriptor();
MethodVisitor mv = v.visitMethod(flags, setterName(propertyDescriptor.getName()), signature, null, null);
if (kind != OwnerKind.INTERFACE) {
mv.visitCode();
InstructionAdapter iv = new InstructionAdapter(mv);
final Type type = state.getTypeMapper().mapType(propertyDescriptor.getOutType());
int paramCode = 0;
if (kind != OwnerKind.NAMESPACE) {
iv.load(0, JetTypeMapper.TYPE_OBJECT);
paramCode = 1;
}
if (kind instanceof OwnerKind.DelegateKind) {
OwnerKind.DelegateKind dk = (OwnerKind.DelegateKind) kind;
iv.load(0, JetTypeMapper.TYPE_OBJECT);
dk.getDelegate().put(JetTypeMapper.TYPE_OBJECT, iv);
iv.load(paramCode, type);
iv.invokeinterface(dk.getOwnerClass(), setterName(propertyDescriptor.getName()), signature);
}
else {
iv.load(paramCode, type);
iv.visitFieldInsn(kind == OwnerKind.NAMESPACE ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD,
state.getTypeMapper().getOwner(propertyDescriptor, kind), propertyDescriptor.getName(),
type.getDescriptor());
}
iv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
public static String getterName(String propertyName) {
return "get" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
}
public static String setterName(String propertyName) {
return "set" + StringUtil.capitalizeWithJavaBeanConvention(propertyName);
}
}
@@ -0,0 +1,608 @@
package org.jetbrains.jet.codegen;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lexer.JetTokens;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.commons.Method;
/**
* @author yole
*/
public abstract class StackValue {
protected final Type type;
public StackValue(Type type) {
this.type = type;
}
public abstract void put(Type type, InstructionAdapter v);
public void store(InstructionAdapter v) {
throw new UnsupportedOperationException("cannot store to value " + this);
}
public void dupReceiver(InstructionAdapter v, int below) {
}
public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
if (this.type == Type.BOOLEAN_TYPE) {
put(Type.BOOLEAN_TYPE, v);
if (jumpIfFalse) {
v.ifeq(label);
}
else {
v.ifne(label);
}
}
else {
throw new UnsupportedOperationException("can't generate a cond jump for a non-boolean value");
}
}
public static StackValue local(int index, Type type) {
return new Local(index, type);
}
public static StackValue onStack(Type type) {
return type == Type.VOID_TYPE ? none() : new OnStack(type);
}
public static StackValue constant(Object value, Type type) {
return new Constant(value, type);
}
public static StackValue cmp(IElementType opToken, Type type) {
return type.getSort() == Type.OBJECT ? new ObjectCompare(opToken, type) : new NumberCompare(opToken, type);
}
public static StackValue not(StackValue stackValue) {
return new Invert(stackValue);
}
public static StackValue arrayElement(Type type) {
return new ArrayElement(type);
}
public static StackValue collectionElement(Type type, CallableMethod getter, CallableMethod setter) {
return new CollectionElement(type, getter, setter);
}
public static StackValue field(Type type, String owner, String name, boolean isStatic) {
return new Field(type, owner, name, isStatic);
}
public static StackValue instanceField(Type type, String owner, String name) {
return new InstanceField(type, owner, name);
}
public static StackValue property(String name, String owner, Type type, boolean isStatic, boolean isInterface, Method getter, Method setter) {
return new Property(name, owner, getter, setter, isStatic, isInterface, type);
}
public static StackValue expression(Type type, JetExpression expression, ExpressionCodegen generator) {
return new Expression(type, expression, generator);
}
private static void box(final Type type, final Type toType, InstructionAdapter v) {
// TODO handle toType correctly
if (type == Type.INT_TYPE || (JetTypeMapper.isIntPrimitive(type) && toType.getInternalName().equals("java/lang/Integer"))) {
v.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
}
else if (type == Type.BOOLEAN_TYPE) {
v.invokestatic("java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
}
else if (type == Type.CHAR_TYPE) {
v.invokestatic("java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
}
else if (type == Type.SHORT_TYPE) {
v.invokestatic("java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
}
else if (type == Type.LONG_TYPE) {
v.invokestatic("java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
}
else if (type == Type.BYTE_TYPE) {
v.invokestatic("java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
}
else if (type == Type.FLOAT_TYPE) {
v.invokestatic("java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
}
else if (type == Type.DOUBLE_TYPE) {
v.invokestatic("java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
}
}
private static void unbox(final Type type, InstructionAdapter v) {
if (type == Type.INT_TYPE) {
v.invokevirtual("java/lang/Number", "intValue", "()I");
}
else if (type == Type.BOOLEAN_TYPE) {
v.invokevirtual("java/lang/Boolean", "booleanValue", "()Z");
}
else if (type == Type.CHAR_TYPE) {
v.invokevirtual("java/lang/Character", "charValue", "()C");
}
else if (type == Type.SHORT_TYPE) {
v.invokevirtual("java/lang/Number", "shortValue", "()S");
}
else if (type == Type.LONG_TYPE) {
v.invokevirtual("java/lang/Number", "longValue", "()J");
}
else if (type == Type.BYTE_TYPE) {
v.invokevirtual("java/lang/Number", "byteValue", "()B");
}
else if (type == Type.FLOAT_TYPE) {
v.invokevirtual("java/lang/Number", "floatValue", "()F");
}
else if (type == Type.DOUBLE_TYPE) {
v.invokevirtual("java/lang/Number", "doubleValue", "()D");
}
}
public void upcast(Type type, InstructionAdapter v) {
if (type.equals(this.type)) return;
if (type.getSort() == Type.OBJECT && this.type.getSort() == Type.OBJECT) {
v.checkcast(type);
}
else {
coerce(type, v);
}
}
protected void coerce(Type type, InstructionAdapter v) {
if (type.equals(this.type)) return;
if (type.getSort() == Type.VOID && this.type.getSort() != Type.VOID) {
if(this.type.getSize() == 1)
v.pop();
else
v.pop2();
}
else if (type.getSort() != Type.VOID && this.type.getSort() == Type.VOID) {
if(type.getSort() == Type.OBJECT)
v.visitFieldInsn(Opcodes.GETSTATIC, "jet/Tuple0", "INSTANCE", "Ljet/Tuple0;");
else if(type == Type.LONG_TYPE)
v.lconst(0);
else if(type == Type.FLOAT_TYPE)
v.fconst(0);
else if(type == Type.DOUBLE_TYPE)
v.dconst(0);
else
v.iconst(0);
}
else if (type.getSort() == Type.OBJECT && this.type.getSort() == Type.OBJECT) {
// v.checkcast(type);
}
else if (type.getSort() == Type.OBJECT) {
box(this.type, type, v);
}
else if (this.type.getSort() == Type.OBJECT && type.getSort() <= Type.DOUBLE) {
if (this.type.equals(JetTypeMapper.TYPE_OBJECT)) {
if (type.getSort() == Type.BOOLEAN) {
v.checkcast(JetTypeMapper.JL_BOOLEAN_TYPE);
}
else {
v.checkcast(JetTypeMapper.JL_NUMBER_TYPE);
}
}
unbox(type, v);
}
else {
v.cast(this.type, type);
}
}
protected void putAsBoolean(InstructionAdapter v) {
Label ifTrue = new Label();
Label end = new Label();
condJump(ifTrue, false, v);
v.iconst(0);
v.goTo(end);
v.mark(ifTrue);
v.iconst(1);
v.mark(end);
}
public static StackValue none() {
return None.INSTANCE;
}
private static class None extends StackValue {
public static None INSTANCE = new None();
private None() {
super(Type.VOID_TYPE);
}
@Override
public void put(Type type, InstructionAdapter v) {
coerce(type, v);
}
}
public static class Local extends StackValue {
private final int index;
public Local(int index, Type type) {
super(type);
this.index = index;
}
@Override
public void put(Type type, InstructionAdapter v) {
v.load(index, this.type);
coerce(type, v);
// TODO unbox
}
@Override
public void store(InstructionAdapter v) {
v.store(index, this.type);
}
}
public static class OnStack extends StackValue {
public OnStack(Type type) {
super(type);
}
@Override
public void put(Type type, InstructionAdapter v) {
if (type == Type.VOID_TYPE && this.type != Type.VOID_TYPE) {
if (this.type.getSize() == 2) {
v.pop2();
}
else {
v.pop();
}
}
else {
coerce(type, v);
}
}
}
public static class Constant extends StackValue {
private final Object value;
public Constant(Object value, Type type) {
super(type);
this.value = value;
}
@Override
public void put(Type type, InstructionAdapter v) {
if(value instanceof Integer)
v.iconst((Integer) value);
else
if(value instanceof Long)
v.lconst((Long) value);
else
if(value instanceof Float)
v.fconst((Float) value);
else
if(value instanceof Double)
v.dconst((Double) value);
else
v.aconst(value);
coerce(type, v);
}
@Override
public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
if (value instanceof Boolean) {
boolean boolValue = (Boolean) value;
if (boolValue ^ jumpIfFalse) {
v.goTo(label);
}
}
else {
throw new UnsupportedOperationException("don't know how to generate this condjump");
}
}
}
private static class NumberCompare extends StackValue {
protected final IElementType opToken;
private final Type operandType;
public NumberCompare(IElementType opToken, Type operandType) {
super(Type.BOOLEAN_TYPE);
this.opToken = opToken;
this.operandType = operandType;
}
@Override
public void put(Type type, InstructionAdapter v) {
if (type == Type.VOID_TYPE) {
return;
}
if (type != Type.BOOLEAN_TYPE) {
throw new UnsupportedOperationException("don't know how to put a compare as a non-boolean type " + type);
}
putAsBoolean(v);
}
@Override
public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
int opcode;
if (opToken == JetTokens.EQEQ) {
opcode = jumpIfFalse ? Opcodes.IFNE : Opcodes.IFEQ;
}
else if (opToken == JetTokens.EXCLEQ) {
opcode = jumpIfFalse ? Opcodes.IFEQ : Opcodes.IFNE;
}
else if (opToken == JetTokens.GT) {
opcode = jumpIfFalse ? Opcodes.IFLE : Opcodes.IFGT;
}
else if (opToken == JetTokens.GTEQ) {
opcode = jumpIfFalse ? Opcodes.IFLT : Opcodes.IFGE;
}
else if (opToken == JetTokens.LT) {
opcode = jumpIfFalse ? Opcodes.IFGE : Opcodes.IFLT;
}
else if (opToken == JetTokens.LTEQ) {
opcode = jumpIfFalse ? Opcodes.IFGT : Opcodes.IFLE;
}
else {
throw new UnsupportedOperationException("don't know how to generate this condjump");
}
if (operandType == Type.FLOAT_TYPE || operandType == Type.DOUBLE_TYPE) {
if (opToken == JetTokens.GT || opToken == JetTokens.GTEQ) {
v.cmpg(operandType);
}
else {
v.cmpl(operandType);
}
}
else if (operandType == Type.LONG_TYPE) {
v.lcmp();
}
else {
opcode += (Opcodes.IF_ICMPEQ - Opcodes.IFEQ);
}
v.visitJumpInsn(opcode, label);
}
}
private static class ObjectCompare extends NumberCompare {
public ObjectCompare(IElementType opToken, Type operandType) {
super(opToken, operandType);
}
@Override
public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
int opcode;
if (opToken == JetTokens.EQEQEQ) {
opcode = jumpIfFalse ? Opcodes.IF_ACMPNE : Opcodes.IF_ACMPEQ;
}
else if (opToken == JetTokens.EXCLEQEQEQ) {
opcode = jumpIfFalse ? Opcodes.IF_ACMPEQ : Opcodes.IF_ACMPNE;
}
else {
throw new UnsupportedOperationException("don't know how to generate this condjump");
}
v.visitJumpInsn(opcode, label);
}
}
private static class Invert extends StackValue {
private StackValue myOperand;
private Invert(StackValue operand) {
super(operand.type);
myOperand = operand;
}
@Override
public void put(Type type, InstructionAdapter v) {
if (type != Type.BOOLEAN_TYPE) {
throw new UnsupportedOperationException("don't know how to put a compare as a non-boolean type");
}
putAsBoolean(v);
}
@Override
public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) {
myOperand.condJump(label, !jumpIfFalse, v);
}
}
private static class ArrayElement extends StackValue {
public ArrayElement(Type type) {
super(type);
}
@Override
public void put(Type type, InstructionAdapter v) {
v.aload(type); // assumes array and index are on the stack
}
@Override
public void store(InstructionAdapter v) {
v.astore(type); // assumes array and index are on the stack
}
@Override
public void dupReceiver(InstructionAdapter v, int below) {
if (below == 1) {
v.dup2X1();
}
else {
v.dup2(); // array and index
}
}
}
private static class CollectionElement extends StackValue {
private final CallableMethod getter;
private final CallableMethod setter;
public CollectionElement(Type type, CallableMethod getter, CallableMethod setter) {
super(type);
this.getter = getter;
this.setter = setter;
}
@Override
public void put(Type type, InstructionAdapter v) {
if (getter == null) {
throw new UnsupportedOperationException("no getter specified");
}
getter.invoke(v);
coerce(type, v);
}
@Override
public void store(InstructionAdapter v) {
if (setter == null) {
throw new UnsupportedOperationException("no setter specified");
}
setter.invoke(v);
}
@Override
public void dupReceiver(InstructionAdapter v, int below) {
if (below == 1) {
v.dup2X1();
}
else {
v.dup2(); // collection and index
}
}
}
private static class Field extends StackValue {
private final String owner;
private final String name;
private final boolean isStatic;
public Field(Type type, String owner, String name, boolean isStatic) {
super(type);
this.owner = owner;
this.name = name;
this.isStatic = isStatic;
}
@Override
public void put(Type type, InstructionAdapter v) {
v.visitFieldInsn(isStatic ? Opcodes.GETSTATIC : Opcodes.GETFIELD, owner, name, this.type.getDescriptor());
}
@Override
public void dupReceiver(InstructionAdapter v, int below) {
if (!isStatic) {
if (below == 1) {
v.dupX1();
}
else {
v.dup();
}
}
}
@Override
public void store(InstructionAdapter v) {
v.visitFieldInsn(isStatic ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD, owner, name, this.type.getDescriptor());
}
}
private static class InstanceField extends StackValue {
private final String owner;
private final String name;
public InstanceField(Type type, String owner, String name) {
super(type);
this.owner = owner;
this.name = name;
}
@Override
public void put(Type type, InstructionAdapter v) {
v.load(0, JetTypeMapper.TYPE_OBJECT);
v.getfield(owner, name, this.type.getDescriptor());
}
@Override
public void dupReceiver(InstructionAdapter v, int below) {
}
@Override
public void store(InstructionAdapter v) {
v.load(0, JetTypeMapper.TYPE_OBJECT);
v.putfield(owner, name, this.type.getDescriptor());
}
}
private static class Property extends StackValue {
private final String name;
private final Method getter;
private final Method setter;
private final String owner;
private final boolean isStatic;
private final boolean isInterface;
public Property(String name, String owner, Method getter, Method setter, boolean aStatic, boolean isInterface, Type type) {
super(type);
this.name = name;
this.owner = owner;
this.getter = getter;
this.setter = setter;
isStatic = aStatic;
this.isInterface = isInterface;
}
@Override
public void put(Type type, InstructionAdapter v) {
if (getter == null) {
v.visitFieldInsn(isStatic ? Opcodes.GETSTATIC : Opcodes.GETFIELD, owner, name, this.type.getDescriptor());
}
else {
v.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : isInterface ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL, owner, getter.getName(), getter.getDescriptor());
}
coerce(type, v);
}
@Override
public void store(InstructionAdapter v) {
if (setter == null) {
v.visitFieldInsn(isStatic ? Opcodes.PUTSTATIC : Opcodes.PUTFIELD, owner, name, this.type.getDescriptor());
}
else {
v.visitMethodInsn(isStatic ? Opcodes.INVOKESTATIC : isInterface ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL, owner, setter.getName(), setter.getDescriptor());
}
}
@Override
public void dupReceiver(InstructionAdapter v, int below) {
if (!isStatic) {
if (below == 1) {
v.dupX1();
}
else {
v.dup();
}
}
}
}
private static class Expression extends StackValue {
private final JetExpression expression;
private final ExpressionCodegen generator;
public Expression(Type type, JetExpression expression, ExpressionCodegen generator) {
super(type);
this.expression = expression;
this.generator = generator;
}
@Override
public void put(Type type, InstructionAdapter v) {
generator.gen(expression, type);
}
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
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 ArraySize implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
receiver.put(JetTypeMapper.TYPE_OBJECT, v);
v.arraylength();
return StackValue.onStack(Type.INT_TYPE);
}
}
@@ -0,0 +1,38 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 BinaryOp implements IntrinsicMethod {
private final int opcode;
public BinaryOp(int opcode) {
this.opcode = opcode;
}
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
if (arguments.size() == 1) {
// intrinsic is called as an ordinary function
if (receiver != null) {
receiver.put(expectedType, v);
}
codegen.gen(arguments.get(0), expectedType);
}
else {
codegen.gen(arguments.get(0), expectedType);
codegen.gen(arguments.get(1), expectedType);
}
v.visitInsn(expectedType.getOpcode(opcode));
return StackValue.onStack(expectedType);
}
}
@@ -0,0 +1,32 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 Concat implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
codegen.generateStringBuilderConstructor();
if (receiver == null) { // LHS.plus(RHS)
v.swap(); // StringBuilder LHS
codegen.invokeAppendMethod(codegen.expressionType(arguments.get(0))); // StringBuilder(LHS)
codegen.invokeAppend(arguments.get(0));
}
else { // LHS + RHS
codegen.invokeAppend(arguments.get(0)); // StringBuilder(LHS)
codegen.invokeAppend(arguments.get(1));
}
v.invokevirtual(ExpressionCodegen.CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;");
return StackValue.onStack(Type.getObjectType("java/lang/String"));
}
}
@@ -0,0 +1,53 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetReferenceExpression;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.List;
/**
* @author yole
*/
public class Increment implements IntrinsicMethod {
private final int myDelta;
public Increment(int delta) {
myDelta = delta;
}
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
final JetExpression operand = arguments.get(0);
if (operand instanceof JetReferenceExpression) {
final int index = codegen.indexOfLocal((JetReferenceExpression) operand);
if (index >= 0 && JetTypeMapper.isIntPrimitive(expectedType)) {
v.iinc(index, myDelta);
return StackValue.local(index, expectedType);
}
}
StackValue value = codegen.genQualified(receiver, operand);
value.dupReceiver(v, 0);
value.put(expectedType, v);
if (expectedType == Type.LONG_TYPE) {
v.lconst(myDelta);
}
else if (expectedType == Type.FLOAT_TYPE) {
v.fconst(myDelta);
}
else if (expectedType == Type.DOUBLE_TYPE) {
v.dconst(myDelta);
}
else {
v.aconst(myDelta);
}
v.add(expectedType);
value.store(v);
return value;
}
}
@@ -0,0 +1,18 @@
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;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.List;
/**
* @author yole
*/
public interface IntrinsicMethod extends Callable {
StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver);
}
@@ -0,0 +1,137 @@
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;
import org.jetbrains.jet.lang.types.JetStandardLibrary;
import org.jetbrains.jet.lang.types.TypeProjection;
import org.objectweb.asm.Opcodes;
import java.util.*;
/**
* @author yole
*/
public class IntrinsicMethods {
private static final IntrinsicMethod UNARY_MINUS = new UnaryMinus();
private static final IntrinsicMethod NUMBER_CAST = new NumberCast();
private static final IntrinsicMethod INV = new Inv();
private static final IntrinsicMethod TYPEINFO = new TypeInfo();
private static final IntrinsicMethod VALUE_TYPEINFO = new ValueTypeInfo();
private static final IntrinsicMethod RANGE_TO = new RangeTo();
private static final IntrinsicMethod INC = new Increment(1);
private static final IntrinsicMethod DEC = new Increment(-1);
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(Project project, JetStandardLibrary stdlib) {
myProject = project;
myStdLib = stdlib;
List<String> primitiveCastMethods = ImmutableList.of("dbl", "flt", "lng", "int", "chr", "sht", "byt");
for (String method : primitiveCastMethods) {
declareIntrinsicProperty("Number", method, NUMBER_CAST);
}
declareIntrinsicProperty("Array", "size", new ArraySize());
for (String type : PRIMITIVE_NUMBER_TYPES) {
declareIntrinsicFunction(type, "minus", 0, UNARY_MINUS);
declareIntrinsicFunction(type, "inv", 0, INV);
declareIntrinsicFunction(type, "rangeTo", 1, RANGE_TO);
declareIntrinsicFunction(type, "inc", 0, INC);
declareIntrinsicFunction(type, "dec", 0, DEC);
}
final FunctionGroup typeInfoFunctionGroup = stdlib.getTypeInfoFunctionGroup();
declareOverload(typeInfoFunctionGroup, 0, TYPEINFO);
declareOverload(typeInfoFunctionGroup, 1, VALUE_TYPEINFO);
declareBinaryOp("plus", Opcodes.IADD);
declareBinaryOp("minus", Opcodes.ISUB);
declareBinaryOp("times", Opcodes.IMUL);
declareBinaryOp("div", Opcodes.IDIV);
declareBinaryOp("mod", Opcodes.IREM);
declareBinaryOp("shl", Opcodes.ISHL);
declareBinaryOp("shr", Opcodes.ISHR);
declareBinaryOp("ushr", Opcodes.IUSHR);
declareBinaryOp("and", Opcodes.IAND);
declareBinaryOp("or", Opcodes.IOR);
declareBinaryOp("xor", Opcodes.IXOR);
declareIntrinsicFunction("Boolean", "not", 0, new Not());
declareIntrinsicFunction("String", "plus", 1, new Concat());
declareIntrinsicStringMethods();
declareIntrinsicProperty("String", "length", new StringLength());
}
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) {
BinaryOp op = new BinaryOp(opcode);
for (String type : PRIMITIVE_NUMBER_TYPES) {
declareIntrinsicFunction(type, methodName, 1, op);
}
}
private void declareIntrinsicProperty(String className, String methodName, IntrinsicMethod implementation) {
final JetScope numberScope = getClassMemberScope(className);
final VariableDescriptor variable = numberScope.getVariable(methodName);
myMethods.put(variable.getOriginal(), implementation);
}
private void declareIntrinsicFunction(String className, String functionName, int arity, IntrinsicMethod implementation) {
JetScope memberScope = getClassMemberScope(className);
final FunctionGroup group = memberScope.getFunctionGroup(functionName);
declareOverload(group, arity, implementation);
}
private void declareOverload(FunctionGroup group, int arity, IntrinsicMethod implementation) {
for (FunctionDescriptor descriptor : group.getFunctionDescriptors()) {
if (descriptor.getValueParameters().size() == arity) {
myMethods.put(descriptor.getOriginal(), implementation);
}
}
}
private JetScope getClassMemberScope(String className) {
final ClassDescriptor descriptor = (ClassDescriptor) myStdLib.getLibraryScope().getClassifier(className);
final List<TypeParameterDescriptor> typeParameterDescriptors = descriptor.getTypeConstructor().getParameters();
List<TypeProjection> typeParameters = new ArrayList<TypeProjection>();
for (TypeParameterDescriptor typeParameterDescriptor : typeParameterDescriptors) {
typeParameters.add(new TypeProjection(JetStandardClasses.getAnyType()));
}
return descriptor.getMemberScope(typeParameters);
}
public IntrinsicMethod getIntrinsic(DeclarationDescriptor descriptor) {
return myMethods.get(descriptor.getOriginal());
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 Inv implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
receiver.put(expectedType, v);
v.iconst(-1);
v.xor(expectedType);
return StackValue.onStack(expectedType);
}
}
@@ -0,0 +1,27 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 Not implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
final StackValue stackValue;
if (arguments.size() == 1) {
stackValue = codegen.gen(arguments.get(0));
}
else {
stackValue = receiver;
}
return StackValue.not(stackValue);
}
}
@@ -0,0 +1,21 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 NumberCast implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
receiver.put(expectedType, v);
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, StackValue receiver) {
final CallableMethod callableMethod = codegen.getTypeMapper().mapToCallableMethod(myMethod);
codegen.invokeMethodWithArguments(callableMethod, (JetCallExpression) element, receiver);
return StackValue.onStack(callableMethod.getSignature().getReturnType());
}
}
@@ -0,0 +1,39 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import jet.IntRange;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
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 RangeTo implements IntrinsicMethod {
private static final String INT_RANGE_CONSTRUCTOR_DESCRIPTOR = "(II)V";
private static final Type INT_RANGE_TYPE = Type.getType(IntRange.class);
private static final String CLASS_INT_RANGE = "jet/IntRange";
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
JetBinaryExpression expression = (JetBinaryExpression) element;
final Type leftType = codegen.expressionType(expression.getLeft());
if (JetTypeMapper.isIntPrimitive(leftType)) {
v.anew(INT_RANGE_TYPE);
v.dup();
codegen.gen(expression.getLeft(), Type.INT_TYPE);
codegen.gen(expression.getRight(), Type.INT_TYPE);
v.invokespecial(CLASS_INT_RANGE, "<init>", INT_RANGE_CONSTRUCTOR_DESCRIPTOR);
return StackValue.onStack(INT_RANGE_TYPE);
}
else {
throw new UnsupportedOperationException("ranges are only supported for int objects");
}
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.List;
/**
* @author alex.tkachman
*/
public class StringLength implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
receiver.put(JetTypeMapper.TYPE_OBJECT, v);
v.invokevirtual("java/lang/String", "length", "()I");
return StackValue.onStack(Type.INT_TYPE);
}
}
@@ -0,0 +1,28 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetTypeProjection;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import java.util.List;
/**
* @author yole
*/
public class TypeInfo implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
final List<JetTypeProjection> typeArguments = ((JetCallExpression) element).getTypeArguments();
if (typeArguments.size() != 1) {
throw new UnsupportedOperationException("one type argument expected");
}
codegen.pushTypeArgument(typeArguments.get(0));
return StackValue.onStack(JetTypeMapper.TYPE_TYPEINFO);
}
}
@@ -0,0 +1,27 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.StackValue;
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 UnaryMinus implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
if (arguments.size() == 1) {
codegen.gen(arguments.get(0), expectedType);
}
else {
receiver.put(expectedType, v);
}
v.neg(expectedType);
return StackValue.onStack(expectedType);
}
}
@@ -0,0 +1,23 @@
package org.jetbrains.jet.codegen.intrinsics;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.codegen.ExpressionCodegen;
import org.jetbrains.jet.codegen.JetTypeMapper;
import org.jetbrains.jet.codegen.StackValue;
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 ValueTypeInfo implements IntrinsicMethod {
@Override
public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List<JetExpression> arguments, StackValue receiver) {
codegen.gen(arguments.get(0), JetTypeMapper.TYPE_JET_OBJECT);
v.invokeinterface("jet/JetObject", "getTypeInfo", "()Ljet/typeinfo/TypeInfo;");
return StackValue.onStack(JetTypeMapper.TYPE_TYPEINFO);
}
}