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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
|
import org.jetbrains.k2js.translate.reference.CallTranslator;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -45,10 +46,30 @@ public abstract class FunctionIntrinsic {
|
|||||||
};
|
};
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public abstract JsExpression apply(@Nullable JsExpression receiver, @NotNull List<JsExpression> arguments,
|
public JsExpression apply(@NotNull CallTranslator callTranslator,
|
||||||
@NotNull TranslationContext context);
|
@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() {
|
public boolean exists() {
|
||||||
return true;
|
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 com.google.dart.compiler.backend.js.ast.JsNameRef;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.intrinsic.functions.basic.BuiltInFunctionIntrinsic;
|
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.CallStandardMethodIntrinsic;
|
||||||
import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
|
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.NamePredicate;
|
||||||
import org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder;
|
import org.jetbrains.k2js.translate.reference.CallTranslator;
|
||||||
|
|
||||||
import java.util.List;
|
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.intrinsic.functions.patterns.PatternBuilder.pattern;
|
||||||
|
import static org.jetbrains.k2js.translate.utils.TranslationUtils.generateCallArgumentList;
|
||||||
|
|
||||||
public final class TopLevelFIF extends CompositeFIF {
|
public final class TopLevelFIF extends CompositeFIF {
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -52,14 +58,44 @@ public final class TopLevelFIF extends CompositeFIF {
|
|||||||
public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
|
public static final FunctionIntrinsicFactory INSTANCE = new TopLevelFIF();
|
||||||
|
|
||||||
private TopLevelFIF() {
|
private TopLevelFIF() {
|
||||||
add(pattern("toString"), new BuiltInFunctionIntrinsic("toString"));
|
add(pattern("jet", "toString").receiverExists(true), new BuiltInFunctionIntrinsic("toString"));
|
||||||
add(pattern("equals"), EQUALS);
|
add(pattern("jet", "equals").receiverExists(true), EQUALS);
|
||||||
add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS);
|
add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), EQUALS);
|
||||||
add(pattern("String|Boolean|Char|Number.equals"), EQUALS);
|
add(pattern("String|Boolean|Char|Number.equals"), EQUALS);
|
||||||
add(pattern("arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1));
|
add(pattern("jet", "arrayOfNulls"), new CallStandardMethodIntrinsic(new JsNameRef("nullArray", "Kotlin"), false, 1));
|
||||||
add(pattern("iterator"), RETURN_RECEIVER_INTRINSIC);
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public JsExpression apply(
|
public JsExpression apply(
|
||||||
|
|||||||
+2
-2
@@ -121,7 +121,7 @@ public final class PatternBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static DescriptorPredicateImpl create(@NotNull String... names) {
|
public static DescriptorPredicateImpl pattern(@NotNull String... names) {
|
||||||
return new DescriptorPredicateImpl(names);
|
return new DescriptorPredicateImpl(names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public final class PatternBuilder {
|
|||||||
this.names = names;
|
this.names = names;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DescriptorPredicateImpl receiverParameterExists(boolean receiverParameterExists) {
|
public DescriptorPredicateImpl receiverExists(boolean receiverParameterExists) {
|
||||||
this.receiverParameterExists = receiverParameterExists;
|
this.receiverParameterExists = receiverParameterExists;
|
||||||
return this;
|
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.JsExpression;
|
||||||
import com.google.dart.compiler.backend.js.ast.JsInvocation;
|
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 com.google.dart.compiler.backend.js.ast.JsNew;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.AnnotationsUtils;
|
||||||
import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
|
import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
|
||||||
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
|
import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
|
||||||
|
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
|
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);
|
this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public ResolvedCall<? extends CallableDescriptor> getResolvedCall() {
|
||||||
|
return resolvedCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public CallParameters getCallParameters() {
|
||||||
|
return callParameters;
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
/*package*/ JsExpression translate() {
|
/*package*/ JsExpression translate() {
|
||||||
if (isIntrinsic()) {
|
JsExpression result = intrinsicInvocation();
|
||||||
return intrinsicInvocation();
|
if (result != null) {
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
if (isConstructor()) {
|
if (isConstructor()) {
|
||||||
return constructorCall();
|
return constructorCall();
|
||||||
}
|
}
|
||||||
if (resolvedCall.getReceiverArgument().exists()) {
|
if (resolvedCall.getReceiverArgument().exists()) {
|
||||||
if (AnnotationsUtils.isNativeObject(descriptor)) {
|
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()) {
|
if (isExpressionAsFunction()) {
|
||||||
return expressionAsFunctionCall();
|
return expressionAsFunctionCall();
|
||||||
}
|
}
|
||||||
if (isInvoke()) {
|
|
||||||
return invokeCall();
|
|
||||||
}
|
|
||||||
return methodCall(getThisObjectOrQualifier());
|
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() {
|
private boolean isExpressionAsFunction() {
|
||||||
return descriptor instanceof ExpressionAsFunctionDescriptor ||
|
return descriptor instanceof ExpressionAsFunctionDescriptor ||
|
||||||
resolvedCall instanceof VariableAsFunctionResolvedCall;
|
resolvedCall instanceof VariableAsFunctionResolvedCall;
|
||||||
@@ -122,25 +112,20 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
return methodCall(null);
|
return methodCall(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isIntrinsic() {
|
@Nullable
|
||||||
if (descriptor instanceof FunctionDescriptor) {
|
|
||||||
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
|
|
||||||
return intrinsic.exists();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private JsExpression intrinsicInvocation() {
|
private JsExpression intrinsicInvocation() {
|
||||||
assert descriptor instanceof FunctionDescriptor;
|
if (descriptor instanceof FunctionDescriptor) {
|
||||||
try {
|
try {
|
||||||
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
|
FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
|
||||||
assert intrinsic.exists();
|
if (intrinsic.exists()) {
|
||||||
return intrinsic.apply(callParameters.getThisOrReceiverOrNull(), arguments, context());
|
return intrinsic.apply(this, arguments, context());
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
}
|
||||||
throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext());
|
catch (RuntimeException e) {
|
||||||
|
throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isConstructor() {
|
private boolean isConstructor() {
|
||||||
@@ -189,27 +174,18 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression extensionFunctionCall(final boolean useThis) {
|
private JsExpression nativeExtensionCall() {
|
||||||
return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() {
|
return methodCall(callParameters.getReceiver());
|
||||||
@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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private List<JsExpression> generateExtensionCallArgumentList(@NotNull JsExpression receiver) {
|
public JsExpression extensionFunctionCall(boolean useThis) {
|
||||||
List<JsExpression> argumentList = new ArrayList<JsExpression>();
|
return callType.constructCall(callParameters.getReceiver(), new ExtensionCallConstructor(useThis), context());
|
||||||
argumentList.add(receiver);
|
}
|
||||||
argumentList.addAll(arguments);
|
|
||||||
return argumentList;
|
@NotNull
|
||||||
|
private List<JsExpression> generateCallArgumentList(@NotNull JsExpression receiver) {
|
||||||
|
return TranslationUtils.generateCallArgumentList(receiver, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -268,4 +244,23 @@ public final class CallTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
return context().getQualifierForDescriptor(descriptor);
|
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.BindingUtils.getFunctionDescriptorForOperationExpression;
|
||||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
|
import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
|
||||||
|
import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
|
||||||
|
|
||||||
public final class TranslationUtils {
|
public final class TranslationUtils {
|
||||||
private TranslationUtils() {
|
private TranslationUtils() {
|
||||||
@@ -57,7 +58,7 @@ public final class TranslationUtils {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
|
private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
|
||||||
@NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
|
@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);
|
return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +194,18 @@ public final class TranslationUtils {
|
|||||||
return false;
|
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) {
|
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
|
||||||
return !(expression instanceof JsLiteral) &&
|
return !(expression instanceof JsLiteral) &&
|
||||||
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
|
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
|
||||||
|
|||||||
Reference in New Issue
Block a user