JS backend: translate invoke function as intrinsic.
This commit is contained in:
+23
-2
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-6
@@ -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(
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user