JS backend: translate invoke function as intrinsic.

This commit is contained in:
develar
2013-08-06 17:24:48 +04:00
committed by Zalim Bashorov
parent 7b67c47594
commit be4e1b7599
5 changed files with 137 additions and 72 deletions
@@ -20,6 +20,7 @@ import com.google.dart.compiler.backend.js.ast.JsExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.reference.CallTranslator;
import java.util.List;
@@ -45,10 +46,30 @@ public abstract class FunctionIntrinsic {
};
@NotNull
public abstract JsExpression apply(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
@NotNull TranslationContext context);
public JsExpression apply(@NotNull CallTranslator callTranslator,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context) {
return apply(callTranslator.getCallParameters().getThisOrReceiverOrNull(), arguments, context);
}
@NotNull
public abstract JsExpression apply(@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context
);
public boolean exists() {
return true;
}
public abstract static class CallParametersAwareFunctionIntrinsic extends FunctionIntrinsic {
@NotNull
@Override
public JsExpression apply(@Nullable JsExpression receiver,
@NotNull List<JsExpression> arguments,
@NotNull TranslationContext context
) {
throw new IllegalStateException();
}
}
}
@@ -21,16 +21,22 @@ import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.intrinsic.functions.basic.BuiltInFunctionIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.functions.basic.CallStandardMethodIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate;
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder;
import org.jetbrains.k2js.translate.reference.CallTranslator;
import java.util.List;
import static org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic.CallParametersAwareFunctionIntrinsic;
import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern;
import static org.jetbrains.k2js.translate.utils.TranslationUtils.generateCallArgumentList;
public final class TopLevelFIF extends CompositeFIF {
@NotNull
@@ -52,14 +58,44 @@ public final class TopLevelFIF extends CompositeFIF {
public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
private TopLevelFIF() {
add(pattern("toString"), new BuiltInFunctionIntrinsic("toString"));
add(pattern("equals"), EQUALS);
add(pattern("jet", "toString").receiverExists(true), new BuiltInFunctionIntrinsic("toString"));
add(pattern("jet", "equals").receiverExists(true), EQUALS);
add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS);
add(pattern("String|Boolean|Char|Number.equals"), EQUALS);
add(pattern("arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1));
add(pattern("iterator"), RETURN_RECEIVER_INTRINSIC);
add(pattern("jet", "arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1));
add(pattern("jet", "iterator").receiverExists(true), RETURN_RECEIVER_INTRINSIC);
add(new DescriptorPredicate() {
@Override
public boolean apply(@NotNull FunctionDescriptor descriptor) {
if (!descriptor.getName().asString().equals("invoke")) {
return false;
}
int parameterCount = descriptor.getValueParameters().size();
DeclarationDescriptor fun = descriptor.getContainingDeclaration();
return fun == (descriptor.getReceiverParameter() == null
? KotlinBuiltIns.getInstance().getFunction(parameterCount)
: KotlinBuiltIns.getInstance().getExtensionFunction(parameterCount));
}
}, new CallParametersAwareFunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(@NotNull CallTranslator callTranslator, @NotNull List<JsExpression> arguments, @NotNull TranslationContext context) {
JsExpression thisExpression = callTranslator.getCallParameters().getThisObject();
if (thisExpression == null) {
return new JsInvocation(callTranslator.getCallParameters().getFunctionReference(), arguments);
}
else if (callTranslator.getResolvedCall().getReceiverArgument().exists()) {
return callTranslator.extensionFunctionCall(false);
}
else {
return new JsInvocation(new JsNameRef("call", callTranslator.getCallParameters().getFunctionReference()),
generateCallArgumentList(thisExpression, arguments));
}
}
}
);
add(PatternBuilder.create("java", "util", "set").receiverParameterExists(true), new FunctionIntrinsic() {
add(pattern("java", "util", "set").receiverExists(true), new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(
@@ -121,7 +121,7 @@ public final class PatternBuilder {
}
@NotNull
public static DescriptorPredicateImpl create(@NotNull String... names) {
public static DescriptorPredicateImpl pattern(@NotNull String... names) {
return new DescriptorPredicateImpl(names);
}
@@ -138,7 +138,7 @@ public final class PatternBuilder {
this.names = names;
}
public DescriptorPredicateImpl receiverParameterExists(boolean receiverParameterExists) {
public DescriptorPredicateImpl receiverExists(boolean receiverParameterExists) {
this.receiverParameterExists = receiverParameterExists;
return this;
}
@@ -18,7 +18,6 @@ package org.jetbrains.k2js.translate.reference;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.JsNameRef;
import com.google.dart.compiler.backend.js.ast.JsNew;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -35,8 +34,8 @@ import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
@@ -72,46 +71,37 @@ public final class CallTranslator extends AbstractTranslator {
this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context);
}
@NotNull
public ResolvedCall<? extends CallableDescriptor> getResolvedCall() {
return resolvedCall;
}
@NotNull
public CallParameters getCallParameters() {
return callParameters;
}
@NotNull
/*package*/ JsExpression translate() {
if (isIntrinsic()) {
return intrinsicInvocation();
JsExpression result = intrinsicInvocation();
if (result != null) {
return result;
}
if (isConstructor()) {
return constructorCall();
}
if (resolvedCall.getReceiverArgument().exists()) {
if (AnnotationsUtils.isNativeObject(descriptor)) {
return methodCall(callParameters.getReceiver());
return nativeExtensionCall();
}
return extensionFunctionCall(!(descriptor instanceof ExpressionAsFunctionDescriptor) && !descriptor.getName().asString().equals("invoke"));
return extensionFunctionCall(!(descriptor instanceof ExpressionAsFunctionDescriptor));
}
if (isExpressionAsFunction()) {
return expressionAsFunctionCall();
}
if (isInvoke()) {
return invokeCall();
}
return methodCall(getThisObjectOrQualifier());
}
//TODO:
private boolean isInvoke() {
return descriptor.getName().asString().equals("invoke");
}
@NotNull
private JsExpression invokeCall() {
JsExpression thisExpression = callParameters.getThisObject();
if (thisExpression == null) {
return new JsInvocation(callParameters.getFunctionReference(), arguments);
}
JsInvocation call = new JsInvocation(new JsNameRef("call", callParameters.getFunctionReference()));
call.getArguments().add(thisExpression);
call.getArguments().addAll(arguments);
return call;
}
private boolean isExpressionAsFunction() {
return descriptor instanceof ExpressionAsFunctionDescriptor ||
resolvedCall instanceof VariableAsFunctionResolvedCall;
@@ -122,25 +112,20 @@ public final class CallTranslator extends AbstractTranslator {
return methodCall(null);
}
private boolean isIntrinsic() {
if (descriptor instanceof FunctionDescriptor) {
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
return intrinsic.exists();
}
return false;
}
@NotNull
@Nullable
private JsExpression intrinsicInvocation() {
assert descriptor instanceof FunctionDescriptor;
try {
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
assert intrinsic.exists();
return intrinsic.apply(callParameters.getThisOrReceiverOrNull(), arguments, context());
}
catch (RuntimeException e) {
throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext());
if (descriptor instanceof FunctionDescriptor) {
try {
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
if (intrinsic.exists()) {
return intrinsic.apply(this, arguments, context());
}
}
catch (RuntimeException e) {
throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext());
}
}
return null;
}
private boolean isConstructor() {
@@ -189,27 +174,18 @@ public final class CallTranslator extends AbstractTranslator {
}
@NotNull
private JsExpression extensionFunctionCall(final boolean useThis) {
return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() {
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
assert receiver != null : "Could not be null for extensions";
JsExpression functionReference = callParameters.getFunctionReference();
if (useThis) {
setQualifier(functionReference, getThisObjectOrQualifier());
}
return new JsInvocation(functionReference, generateExtensionCallArgumentList(receiver));
}
}, context());
private JsExpression nativeExtensionCall() {
return methodCall(callParameters.getReceiver());
}
@NotNull
private List<JsExpression> generateExtensionCallArgumentList(@NotNull JsExpression receiver) {
List<JsExpression> argumentList = new ArrayList<JsExpression>();
argumentList.add(receiver);
argumentList.addAll(arguments);
return argumentList;
public JsExpression extensionFunctionCall(boolean useThis) {
return callType.constructCall(callParameters.getReceiver(), new ExtensionCallConstructor(useThis), context());
}
@NotNull
private List<JsExpression> generateCallArgumentList(@NotNull JsExpression receiver) {
return TranslationUtils.generateCallArgumentList(receiver, arguments);
}
@NotNull
@@ -268,4 +244,23 @@ public final class CallTranslator extends AbstractTranslator {
}
return context().getQualifierForDescriptor(descriptor);
}
private class ExtensionCallConstructor implements CallType.CallConstructor {
private final boolean useThis;
private ExtensionCallConstructor(boolean useThis) {
this.useThis = useThis;
}
@NotNull
@Override
public JsExpression construct(@Nullable JsExpression receiver) {
assert receiver != null : "Could not be null for extensions";
JsExpression functionReference = callParameters.getFunctionReference();
if (useThis) {
setQualifier(functionReference, getThisObjectOrQualifier());
}
return new JsInvocation(callParameters.getFunctionReference(), generateCallArgumentList(receiver));
}
}
}
@@ -36,6 +36,7 @@ import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
public final class TranslationUtils {
private TranslationUtils() {
@@ -57,7 +58,7 @@ public final class TranslationUtils {
@NotNull
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
@NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
JsObjectLiteral meta = JsAstUtils.createDataDescriptor(function, descriptor.getModality().isOverridable());
JsObjectLiteral meta = createDataDescriptor(function, descriptor.getModality().isOverridable());
return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
}
@@ -193,6 +194,18 @@ public final class TranslationUtils {
return false;
}
@NotNull
public static List<JsExpression> generateCallArgumentList(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) {
if (arguments.isEmpty()) {
return Collections.singletonList(receiver);
}
List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size());
argumentList.add(receiver);
argumentList.addAll(arguments);
return argumentList;
}
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
return !(expression instanceof JsLiteral) &&
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);