diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ErrorUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ErrorUtils.java index 0d3a563afb5d..613af41f7dc7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ErrorUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/ErrorUtils.java @@ -30,6 +30,7 @@ import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.MappingInitialValueEntry; +import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_LANG_ERROR_PKG_ID; import static io.ballerina.runtime.api.constants.RuntimeConstants.FLOAT_LANG_LIB; import static io.ballerina.runtime.api.creators.ErrorCreator.createError; import static io.ballerina.runtime.internal.errors.ErrorCodes.INCOMPATIBLE_CONVERT_OPERATION; @@ -187,4 +188,11 @@ public static BError createInvalidFractionDigitsError() { ErrorReasons.INVALID_FRACTION_DIGITS_ERROR), ErrorHelper.getErrorDetails(ErrorCodes.INVALID_FRACTION_DIGITS)); } + + public static BError createNoMessageError(String chnlName) { + String[] splitWorkers = chnlName.split(":")[0].split("->"); + return createError(BALLERINA_LANG_ERROR_PKG_ID, "NoMessage", ErrorReasons.NO_MESSAGE_ERROR, + null, ErrorHelper.getErrorDetails(ErrorCodes.NO_MESSAGE_ERROR, + StringUtils.fromString(splitWorkers[0]), StringUtils.fromString(splitWorkers[1]))); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorCodes.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorCodes.java index a969630c6a83..574b79c7c680 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorCodes.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorCodes.java @@ -149,7 +149,8 @@ public enum ErrorCodes implements DiagnosticCode { REGEXP_INVALID_HEX_DIGIT("regexp.invalid.hex.digit", "RUNTIME_0120"), CONFIG_TOML_INVALID_MODULE_STRUCTURE_WITH_VARIABLE("config.toml.invalid.module.structure.with.variable", "RUNTIME_0121"), - EMPTY_XML_SEQUENCE_HAS_NO_ATTRIBUTES("empty.xml.sequence.no.attributes", "RUNTIME_0122"); + EMPTY_XML_SEQUENCE_HAS_NO_ATTRIBUTES("empty.xml.sequence.no.attributes", "RUNTIME_0122"), + NO_MESSAGE_ERROR("no.worker.message.received", "RUNTIME_0123"); private final String errorMsgKey; private final String errorCode; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorReasons.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorReasons.java index 9daa5d928554..ed8e8f0540f5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorReasons.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/errors/ErrorReasons.java @@ -133,6 +133,8 @@ private ErrorReasons() {} public static final BString REGEXP_OPERATION_ERROR = getModulePrefixedReason(REGEXP_LANG_LIB, "RegularExpressionOperationError"); + public static final BString NO_MESSAGE_ERROR = StringUtils.fromString("NoMessage"); + public static BString getModulePrefixedReason(String moduleName, String identifier) { return StringUtils.fromString(BALLERINA_ORG_PREFIX.concat(moduleName) .concat(CLOSING_CURLY_BRACE).concat(identifier)); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java index 652795c5b68c..e421bbf9d0b2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/Strand.java @@ -23,6 +23,7 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.ChannelDetails; @@ -90,6 +91,8 @@ public class Strand { public Stack trxContexts; private State state; private final ReentrantLock strandLock; + public BMap workerReceiveMap = null; + public int channelCount = 0; public Strand(String name, StrandMetadata metadata, Scheduler scheduler, Strand parent, Map properties) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WDChannels.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WDChannels.java index d0e7dca48de1..f00a5c9a40b3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WDChannels.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WDChannels.java @@ -17,9 +17,22 @@ */ package io.ballerina.runtime.internal.scheduling; +import io.ballerina.runtime.api.creators.ValueCreator; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.values.BMap; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.ErrorUtils; +import io.ballerina.runtime.internal.values.ChannelDetails; +import io.ballerina.runtime.internal.values.ErrorValue; + +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import static io.ballerina.runtime.internal.scheduling.State.BLOCK_AND_YIELD; + /** * This represents a worker data channel holder that is created for each strand to hold channels required. * @@ -28,11 +41,13 @@ public class WDChannels { private Map wDChannels; + private final List errors = new ArrayList<>(); - //TODO try to generalize this to a normal data channel, in that case we won't need these classes. - public WDChannels() { + // A worker receive field for multiple receive action. + public record ReceiveField(String fieldName, String channelName) { } + //TODO try to generalize this to a normal data channel, in that case we won't need these classes. public synchronized WorkerDataChannel getWorkerDataChannel(String name) { if (this.wDChannels == null) { this.wDChannels = new HashMap<>(); @@ -44,4 +59,122 @@ public synchronized WorkerDataChannel getWorkerDataChannel(String name) { } return channel; } + + public Object receiveDataMultipleChannels(Strand strand, ReceiveField[] receiveFields, Type targetType) + throws Throwable { + if (strand.workerReceiveMap == null) { + strand.workerReceiveMap = ValueCreator.createMapValue(targetType); + } + for (ReceiveField field : receiveFields) { + WorkerDataChannel channel = getWorkerDataChannel(field.channelName()); + WorkerDataChannel.State state = channel.getState(); + Object result = null; + switch (state) { + case OPEN: + result = channel.tryTakeData(strand, true); + break; + case AUTO_CLOSED: + result = ErrorUtils.createNoMessageError(field.channelName()); + break; + case CLOSED: + continue; + } + checkAndPopulateResult(strand, field, result, channel); + } + return clearResultCache(strand, receiveFields); + } + + private void checkAndPopulateResult(Strand strand, ReceiveField field, Object result, WorkerDataChannel channel) { + if (result == null) { + strand.setState(BLOCK_AND_YIELD); + return; + } + result = getResultValue(result); + strand.workerReceiveMap.populateInitialValue(StringUtils.fromString(field.fieldName()), result); + channel.close(); + ++strand.channelCount; + } + + private Object clearResultCache(Strand strand, ReceiveField[] receiveFields) { + if (strand.channelCount != receiveFields.length) { + return null; + } + BMap map = strand.workerReceiveMap; + strand.workerReceiveMap = null; + strand.channelCount = 0; + strand.setState(State.RUNNABLE); + return map; + } + + public Object receiveDataAlternateChannels(Strand strand, String[] channels) throws Throwable { + Object result = null; + boolean allChannelsClosed = true; + for (String channelName : channels) { + WorkerDataChannel channel = getWorkerDataChannel(channelName); + WorkerDataChannel.State state = channel.getState(); + if (state == WorkerDataChannel.State.OPEN) { + allChannelsClosed = false; + result = handleResultForOpenChannel(strand, channels, channel); + } else if (state == WorkerDataChannel.State.AUTO_CLOSED) { + errors.add((ErrorValue) ErrorUtils.createNoMessageError(channelName)); + } + } + return processResulAndError(strand, channels, result, allChannelsClosed); + } + + private Object handleResultForOpenChannel(Strand strand, String[] channels, WorkerDataChannel channel) + throws Throwable { + Object result = channel.tryTakeData(strand, true); + if (result == null) { + return null; + } + Object resultValue = getResultValue(result); + if (resultValue instanceof ErrorValue errorValue) { + errors.add(errorValue); + channel.close(); + return null; + } + closeChannels(channels); + return result; + } + + private static Object getResultValue(Object result) { + if (result instanceof WorkerDataChannel.WorkerResult workerResult) { + return workerResult.value; + } + return result; + } + + private Object processResulAndError(Strand strand, String[] channels, Object result, boolean allChannelsClosed) { + if (result == null) { + if (errors.size() == channels.length) { + result = errors.get(errors.size() - 1); + } else if (!allChannelsClosed) { + strand.setState(BLOCK_AND_YIELD); + } + } else { + strand.setState(State.RUNNABLE); + } + return getResultValue(result); + } + + private void closeChannels(String[] channels) { + for (String channelName : channels) { + WorkerDataChannel channel = getWorkerDataChannel(channelName); + channel.close(); + channel.callCount = 2; + } + } + + public synchronized void removeCompletedChannels(Strand strand, String channelName) { + if (this.wDChannels != null) { + WorkerDataChannel channel = this.wDChannels.get(channelName); + // callCount is incremented to 2 when the message passing is completed. + if (channel != null && channel.callCount == 2) { + this.wDChannels.remove(channelName); + strand.channelDetails.remove(new ChannelDetails(channelName, true, false)); + } + } + } + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WorkerDataChannel.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WorkerDataChannel.java index 8b68a02d8801..c4ada8ee3efa 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WorkerDataChannel.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/WorkerDataChannel.java @@ -1,22 +1,23 @@ /* -* Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package io.ballerina.runtime.internal.scheduling; +import io.ballerina.runtime.internal.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; import java.util.LinkedList; @@ -43,22 +44,24 @@ public class WorkerDataChannel { private int receiverCounter; private boolean reschedule; - private Lock channelLock; + private final Lock channelLock; - public String chnlName; + protected String chnlName; + protected int callCount = 0; @SuppressWarnings("rawtypes") - private Queue channel = new LinkedList<>(); + private final Queue channel = new LinkedList<>(); + private State state; public WorkerDataChannel() { this.channelLock = new ReentrantLock(); this.senderCounter = 0; this.receiverCounter = 0; + this.state = State.OPEN; } + public WorkerDataChannel(String channelName) { - this.channelLock = new ReentrantLock(); - this.senderCounter = 0; - this.receiverCounter = 0; + this(); this.chnlName = channelName; } @@ -70,24 +73,63 @@ public void releaseChannelLock() { this.channelLock.unlock(); } + public boolean isClosed() { + return this.state == State.CLOSED || this.state == State.AUTO_CLOSED; + } + + private void close(State state) { + try { + acquireChannelLock(); + this.state = state; + if (this.receiver != null) { + this.receiver.scheduler.unblockStrand(this.receiver); + this.receiver = null; + } + } finally { + releaseChannelLock(); + } + } + + public State getState() { + return this.state; + } + + public enum State { + OPEN, AUTO_CLOSED, CLOSED + } + @SuppressWarnings("rawtypes") public void sendData(Object data, Strand sender) { + if (isClosed()) { + callCount++; + return; + } try { acquireChannelLock(); this.channel.add(new WorkerResult(data)); this.senderCounter++; - if (this.receiver != null) { + if (this.receiver != null && receiver.scheduler != null) { this.receiver.scheduler.unblockStrand(this.receiver); this.receiver = null; } + callCount++; } finally { releaseChannelLock(); } } + public void autoClose() { + close(State.AUTO_CLOSED); + } + + public void close() { + close(State.CLOSED); + } + /** * Put data for sync send. - * @param data - data to be sent over the channel + * + * @param data - data to be sent over the channel * @param strand - sending strand, that will be paused * @return error if receiver already in error state, else null * @throws Throwable panic @@ -123,14 +165,17 @@ public Object syncSendData(Object data, Strand strand) throws Throwable { reschedule = false; if (this.panic != null && this.channel.peek() != null) { Throwable e = this.panic; + callCount++; throw e; } else if (this.error != null && this.channel.peek() != null) { ErrorValue ret = this.error; this.waitingSender = null; + callCount++; return ret; } // sync send done + callCount++; return null; } finally { releaseChannelLock(); @@ -139,8 +184,15 @@ public Object syncSendData(Object data, Strand strand) throws Throwable { @SuppressWarnings("rawtypes") public Object tryTakeData(Strand strand) throws Throwable { + return tryTakeData(strand, false); + } + + public Object tryTakeData(Strand strand, boolean isMultiple) throws Throwable { try { acquireChannelLock(); + if (isClosed()) { + return ErrorUtils.createNoMessageError(chnlName); + } WorkerResult result = this.channel.peek(); if (result != null) { this.receiverCounter++; @@ -149,33 +201,38 @@ public Object tryTakeData(Strand strand) throws Throwable { if (result.isSync) { // sync sender will pick the this.error as result, which is null if (this.waitingSender != null) { - Strand waiting = this.waitingSender.waitingStrand; + Strand waiting = this.waitingSender.waitingStrand; waiting.scheduler.unblockStrand(waiting); this.waitingSender = null; } } else if (this.flushSender != null && this.flushSender.flushCount == this.receiverCounter) { this.flushSender.waitingStrand.flushDetail.flushLock.lock(); this.flushSender.waitingStrand.flushDetail.flushedCount++; - if (this.flushSender.waitingStrand.flushDetail.flushedCount - == this.flushSender.waitingStrand.flushDetail.flushChannels.length && + if (this.flushSender.waitingStrand.flushDetail.flushedCount == + this.flushSender.waitingStrand.flushDetail.flushChannels.length && this.flushSender.waitingStrand.isBlocked()) { - //will continue if this is a sync wait, will try to flush again if blocked on flush - this.flushSender.waitingStrand.scheduler.unblockStrand(this.flushSender.waitingStrand); + //will continue if this is a sync wait, will try to flush again if blocked on flush + this.flushSender.waitingStrand.scheduler.unblockStrand(this.flushSender.waitingStrand); } this.flushSender.waitingStrand.flushDetail.flushLock.unlock(); this.flushSender = null; } - return result.value; + callCount++; + return isMultiple ? result : result.value; } else if (this.panic != null && this.senderCounter == this.receiverCounter + 1) { this.receiverCounter++; + callCount++; throw this.panic; } else if (this.error != null && this.senderCounter == this.receiverCounter + 1) { this.receiverCounter++; + callCount++; return error; } else { this.receiver = strand; - strand.setState(BLOCK_AND_YIELD); + if (!isMultiple) { + strand.setState(BLOCK_AND_YIELD); + } return null; } } finally { @@ -185,6 +242,7 @@ public Object tryTakeData(Strand strand) throws Throwable { /** * Set the state as error if the receiving worker is in error state. + * * @param error the BError of the receiving worker */ public void setSendError(ErrorValue error) { @@ -265,7 +323,7 @@ public void removeFlushWait() { public void setSendPanic(Throwable panic) { try { acquireChannelLock(); - this.panic = panic; + this.panic = panic; this.senderCounter++; if (this.receiver != null) { this.receiver.scheduler.unblockStrand(this.receiver); @@ -283,7 +341,7 @@ public void setSendPanic(Throwable panic) { */ public void setReceiverPanic(Throwable panic) { acquireChannelLock(); - this.panic = panic; + this.panic = panic; this.receiverCounter++; if (this.flushSender != null) { this.flushSender.waitingStrand.flushDetail.flushLock.lock(); @@ -314,7 +372,6 @@ public static class WorkerResult { public Object value; public boolean isSync; - public WorkerResult(Object value) { this.value = value; } diff --git a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties index 297d6b945c74..f2c2d0cfde50 100644 --- a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties +++ b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties @@ -263,3 +263,4 @@ regexp.invalid.unicode.general.category.value = invalid Unicode general category regexp.invalid.unicode.property.value = invalid Unicode property value ''{0}'' regexp.empty.character.class.disallowed = empty character class disallowed regexp.invalid.hex.digit = invalid hexadecimal digit +no.worker.message.received = no message received from worker ''{0}'' to worker ''{1}'' diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/NodeFinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/NodeFinder.java index d02f7731e10f..317c9f50fb74 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/NodeFinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/NodeFinder.java @@ -76,6 +76,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -97,6 +98,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -599,6 +601,24 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { setEnclosingNode(asyncSendExpr, asyncSendExpr.workerIdentifier.pos); } + @Override + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + for (BLangWorkerReceive workerReceive : alternateWorkerReceive.getWorkerReceives()) { + if (setEnclosingNode(alternateWorkerReceive, workerReceive.workerIdentifier.pos)) { + return; + } + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField receiveField : multipleWorkerReceive.getReceiveFields()) { + if (setEnclosingNode(multipleWorkerReceive, receiveField.getWorkerReceive().workerIdentifier.pos)) { + return; + } + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { setEnclosingNode(workerReceiveNode, workerReceiveNode.workerIdentifier.pos); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/ReferenceFinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/ReferenceFinder.java index bf3792f0e713..51d90433c385 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/ReferenceFinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/ReferenceFinder.java @@ -81,6 +81,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -105,6 +106,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -768,6 +770,21 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { addIfSameSymbol(asyncSendExpr.workerSymbol, asyncSendExpr.workerIdentifier.pos); } + @Override + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + for (BLangWorkerReceive workerReceive : alternateWorkerReceive.getWorkerReceives()) { + addIfSameSymbol(workerReceive.workerSymbol, workerReceive.workerIdentifier.pos); + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField receiveField : multipleWorkerReceive.getReceiveFields()) { + BLangWorkerReceive workerReceive = receiveField.getWorkerReceive(); + addIfSameSymbol(workerReceive.workerSymbol, workerReceive.workerIdentifier.pos); + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { addIfSameSymbol(workerReceiveNode.workerSymbol, workerReceiveNode.workerIdentifier.pos); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFinder.java index 5f2748734b76..9b9ce5a2741a 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFinder.java @@ -84,6 +84,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -114,6 +115,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -822,6 +824,25 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { lookupNode(asyncSendExpr.expr); } + @Override + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + for (BLangWorkerReceive workerReceive : alternateWorkerReceive.getWorkerReceives()) { + if (setEnclosingNode(workerReceive.workerSymbol, workerReceive.workerIdentifier.pos)) { + return; + } + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField receiveField : multipleWorkerReceive.getReceiveFields()) { + BLangWorkerReceive workerReceive = receiveField.getWorkerReceive(); + if (setEnclosingNode(workerReceive.workerSymbol, workerReceive.workerIdentifier.pos)) { + return; + } + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { setEnclosingNode(workerReceiveNode.workerSymbol, workerReceiveNode.workerIdentifier.pos); diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java index f625ab191cbe..68b530b7b2ac 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java @@ -242,6 +242,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -271,6 +272,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -914,6 +916,14 @@ public static WorkerReceiveNode createWorkerReceiveNode() { return new BLangWorkerReceive(); } + public static BLangAlternateWorkerReceive createAlternateWorkerReceiveNode() { + return new BLangAlternateWorkerReceive(); + } + + public static BLangMultipleWorkerReceive createMultipleWorkerReceiveNode() { + return new BLangMultipleWorkerReceive(); + } + public static WorkerSendExpressionNode createWorkerSendNode() { return new BLangWorkerAsyncSendExpr(); } diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/tree/NodeKind.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/tree/NodeKind.java index cf16f169ebf2..989e0d1edc7c 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/tree/NodeKind.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/tree/NodeKind.java @@ -181,6 +181,8 @@ public enum NodeKind { WHILE, LOCK, WORKER_RECEIVE, + ALTERNATE_WORKER_RECEIVE, + MULTIPLE_WORKER_RECEIVE, WORKER_ASYNC_SEND, WORKER_SYNC_SEND, WORKER_FLUSH, diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index f77dd801510a..90a3a8f7196f 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -119,8 +119,8 @@ public enum DiagnosticErrorCode implements DiagnosticCode { WORKER_SEND_RECEIVE_PARAMETER_COUNT_MISMATCH("BCE2082", "worker.send.receive.parameter.count.mismatch"), INVALID_WORKER_INTERACTION("BCE2083", "worker.invalid.worker.interaction"), WORKER_INTERACTIONS_ONLY_ALLOWED_BETWEEN_PEERS("BCE2084", "worker.interactions.only.allowed.between.peers"), - WORKER_SEND_AFTER_RETURN("BCE2085", "worker.send.after.return"), - WORKER_RECEIVE_AFTER_RETURN("BCE2086", "worker.receive.after.return"), + INVALID_WORKER_SEND_NO_MATCHING_WORKER_RECEIVE("BCE2085", "invalid.worker.send.no.matching.worker.receive"), + INVALID_WORKER_RECEIVE_NO_MATCHING_WORKER_SEND("BCE2086", "invalid.worker.receive.no.matching.worker.send"), EXPLICIT_WORKER_CANNOT_BE_DEFAULT("BCE2087", "explicit.worker.cannot.be.default"), INVALID_MULTIPLE_FORK_JOIN_SEND("BCE2088", "worker.multiple.fork.join.send"), INCOMPATIBLE_TYPE_REFERENCE("BCE2089", "incompatible.type.reference"), @@ -587,9 +587,10 @@ public enum DiagnosticErrorCode implements DiagnosticCode { // Worker receive and send related error codes INVALID_TYPE_FOR_RECEIVE("BCE3840", "invalid.type.for.receive"), INVALID_TYPE_FOR_SEND("BCE3841", "invalid.type.for.send"), - - INVALID_USAGE_OF_RECEIVE_EXPRESSION("BCE3842", "invalid.usage.of.receive.expression"), + RECEIVE_ACTION_NOT_SUPPORTED_WITH_VAR("BCE3842", "receive.action.not.supported.with.var.type"), INVALID_USE_OF_EXPERIMENTAL_FEATURE("BCE3843", "invalid.use.of.experimental.feature"), + MULTIPLE_RECEIVE_COMPATIBLE_TYPE_NOT_FOUND("BCE3844", "multiple.receive.compatible.type.not.found"), + DUPLICATE_KEY_IN_MULTIPLE_RECEIVE("BCE3845", "duplicate.key.in.multiple.receive"), // LangLib related error codes. TYPE_PARAM_OUTSIDE_LANG_MODULE("BCE3900", "type.param.outside.lang.module"), @@ -725,7 +726,6 @@ public enum DiagnosticErrorCode implements DiagnosticCode { MISMATCHED_VISIBILITY_QUALIFIERS_IN_OBJECT_FIELD( "BCE3988", "mismatched.visibility.qualifiers.in.object.field"), INVALID_INCLUSION_OF_OBJECT_WITH_PRIVATE_MEMBERS("BCE3989", "invalid.inclusion.of.object.with.private.members"), - MULTIPLE_RECEIVE_ACTION_NOT_YET_SUPPORTED("BCE3990", "multiple.receive.action.not.yet.supported"), INVALID_READONLY_FIELD_TYPE("BCE3991", "invalid.readonly.field.type"), @@ -817,7 +817,7 @@ public enum DiagnosticErrorCode implements DiagnosticCode { "cannot.use.alternate.wait.action.within.multiple.wait.action"), EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4057", "future.expression.expected"), INSTANTIATION_ERROR("BCE4058", "instantiation.error"), - INVALID_BINDING_PATTERN_IN_ON_FAIL("BCE4059", "invalid.binding.pattern.in.on.fail") + INVALID_BINDING_PATTERN_IN_ON_FAIL("BCE4059", "invalid.binding.pattern.in.on.fail"), ; private String diagnosticId; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java index 706d56bf2b18..2af9c4655574 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java @@ -92,7 +92,6 @@ import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangExternalFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangFunction; -import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import org.wso2.ballerinalang.compiler.tree.BLangImportPackage; import org.wso2.ballerinalang.compiler.tree.BLangNodeVisitor; import org.wso2.ballerinalang.compiler.tree.BLangPackage; @@ -104,6 +103,7 @@ import org.wso2.ballerinalang.compiler.tree.BLangXMLNS; import org.wso2.ballerinalang.compiler.tree.BLangXMLNS.BLangLocalXMLNS; import org.wso2.ballerinalang.compiler.tree.BLangXMLNS.BLangPackageXMLNS; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; import org.wso2.ballerinalang.compiler.tree.expressions.BLangDynamicArgExpr; @@ -130,6 +130,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangTupleLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangReAssertion; import org.wso2.ballerinalang.compiler.tree.expressions.BLangReAtomCharOrEscape; import org.wso2.ballerinalang.compiler.tree.expressions.BLangReAtomQuantifier; @@ -164,6 +165,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerAsyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerFlushExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerReceive; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSendReceiveExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLAttribute; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLCommentLiteral; @@ -600,9 +602,10 @@ public void visit(BLangFunction astFunc) { //create channelDetails array int i = 0; - for (String channelName : astFunc.sendsToThis) { - birFunc.workerChannels[i] = new BIRNode.ChannelDetails(channelName, astFunc.defaultWorkerName.value - .equals(DEFAULT_WORKER_NAME), isWorkerSend(channelName, astFunc.defaultWorkerName.value)); + for (BLangWorkerSendReceiveExpr.Channel channel : astFunc.sendsToThis) { + String channelId = channel.channelId(); + birFunc.workerChannels[i] = new BIRNode.ChannelDetails(channelId, astFunc.defaultWorkerName.value + .equals(DEFAULT_WORKER_NAME), isWorkerSend(channelId, astFunc.defaultWorkerName.value)); i++; } @@ -1173,11 +1176,63 @@ public void visit(BLangForkJoin forkJoin) { forkJoin.workers.forEach(worker -> worker.accept(this)); } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + BIRBasicBlock thenBB = new BIRBasicBlock(this.env.nextBBId()); + addToTrapStack(thenBB); + BIRVariableDcl tempVarDcl = new BIRVariableDcl(altWorkerReceive.getBType(), this.env.nextLocalVarId(names), + VarScope.FUNCTION, VarKind.TEMP); + this.env.enclFunc.localVars.add(tempVarDcl); + BIROperand lhsOp = new BIROperand(tempVarDcl); + this.env.targetOperand = lhsOp; + + boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); + + this.env.enclBB.terminator = new BIRTerminator.WorkerAlternateReceive(altWorkerReceive.pos, + getChannelList(altWorkerReceive), lhsOp, isOnSameStrand, thenBB, this.currentScope); + this.env.enclBasicBlocks.add(thenBB); + this.env.enclBB = thenBB; + } + + private List getChannelList(BLangAlternateWorkerReceive alternateWorkerReceive) { + List channels = new ArrayList<>(); + for (BLangWorkerReceive workerReceive : alternateWorkerReceive.getWorkerReceives()) { + channels.add(workerReceive.getChannel().channelId()); + } + return channels; + } + + private List getChannelList( + BLangMultipleWorkerReceive multipleWorkerReceive) { + List channels = new ArrayList<>(); + for (BLangMultipleWorkerReceive.BLangReceiveField workerReceive : multipleWorkerReceive.getReceiveFields()) { + channels.add(new BIRTerminator.WorkerMultipleReceive.ReceiveField(workerReceive.getKey().value, + workerReceive.getWorkerReceive().getChannel().channelId())); + } + return channels; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + BIRBasicBlock thenBB = new BIRBasicBlock(this.env.nextBBId()); + addToTrapStack(thenBB); + BIRVariableDcl tempVarDcl = new BIRVariableDcl(multipleWorkerReceive.getBType(), this.env.nextLocalVarId(names), + VarScope.FUNCTION, VarKind.TEMP); + this.env.enclFunc.localVars.add(tempVarDcl); + BIROperand lhsOp = new BIROperand(tempVarDcl); + this.env.targetOperand = lhsOp; + boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); + + this.env.enclBB.terminator = new BIRTerminator.WorkerMultipleReceive(multipleWorkerReceive.pos, + getChannelList(multipleWorkerReceive), lhsOp, isOnSameStrand, thenBB, this.currentScope); + this.env.enclBasicBlocks.add(thenBB); + this.env.enclBB = thenBB; + } + @Override public void visit(BLangWorkerReceive workerReceive) { BIRBasicBlock thenBB = new BIRBasicBlock(this.env.nextBBId()); addToTrapStack(thenBB); - String channel = workerReceive.workerIdentifier.value + "->" + env.enclFunc.workerName.value; BIRVariableDcl tempVarDcl = new BIRVariableDcl(workerReceive.getBType(), this.env.nextLocalVarId(names), VarScope.FUNCTION, VarKind.TEMP); @@ -1187,8 +1242,9 @@ public void visit(BLangWorkerReceive workerReceive) { boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); - this.env.enclBB.terminator = new BIRTerminator.WorkerReceive(workerReceive.pos, names.fromString(channel), - lhsOp, isOnSameStrand, thenBB, this.currentScope); + this.env.enclBB.terminator = new BIRTerminator.WorkerReceive(workerReceive.pos, + names.fromString(workerReceive.getChannel().channelId()), lhsOp, isOnSameStrand, thenBB, + this.currentScope); this.env.enclBasicBlocks.add(thenBB); this.env.enclBB = thenBB; @@ -1206,13 +1262,11 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { this.env.enclFunc.localVars.add(tempVarDcl); BIROperand lhsOp = new BIROperand(tempVarDcl); this.env.targetOperand = lhsOp; - - String channelName = this.env.enclFunc.workerName.value + "->" + asyncSendExpr.workerIdentifier.value; boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); this.env.enclBB.terminator = new BIRTerminator.WorkerSend( - asyncSendExpr.pos, names.fromString(channelName), dataOp, isOnSameStrand, false, lhsOp, - thenBB, this.currentScope); + asyncSendExpr.pos, names.fromString(asyncSendExpr.getChannel().channelId()), dataOp, isOnSameStrand, + false, lhsOp, thenBB, this.currentScope); this.env.enclBasicBlocks.add(thenBB); this.env.enclBB = thenBB; @@ -1231,11 +1285,10 @@ public void visit(BLangWorkerSyncSendExpr syncSend) { BIROperand lhsOp = new BIROperand(tempVarDcl); this.env.targetOperand = lhsOp; - String channelName = this.env.enclFunc.workerName.value + "->" + syncSend.workerIdentifier.value; boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); this.env.enclBB.terminator = new BIRTerminator.WorkerSend( - syncSend.pos, names.fromString(channelName), dataOp, isOnSameStrand, true, lhsOp, + syncSend.pos, names.fromString(syncSend.getChannel().channelId()), dataOp, isOnSameStrand, true, lhsOp, thenBB, this.currentScope); this.env.enclBasicBlocks.add(thenBB); @@ -1248,15 +1301,13 @@ public void visit(BLangWorkerFlushExpr flushExpr) { addToTrapStack(thenBB); //create channelDetails array - BIRNode.ChannelDetails[] channels = new BIRNode.ChannelDetails[flushExpr.workerIdentifierList.size()]; + BIRNode.ChannelDetails[] channels = new BIRNode.ChannelDetails[flushExpr.cachedWorkerSendStmts.size()]; int i = 0; - for (BLangIdentifier workerIdentifier : flushExpr.workerIdentifierList) { - String channelName = this.env.enclFunc.workerName.value + "->" + workerIdentifier.value; - boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); - channels[i] = new BIRNode.ChannelDetails(channelName, isOnSameStrand, true); + boolean isOnSameStrand = DEFAULT_WORKER_NAME.equals(this.env.enclFunc.workerName.value); + for (BLangWorkerAsyncSendExpr sendStmt : flushExpr.cachedWorkerSendStmts) { + channels[i] = new BIRNode.ChannelDetails(sendStmt.getChannel().channelId(), isOnSameStrand, true); i++; } - BIRVariableDcl tempVarDcl = new BIRVariableDcl(flushExpr.getBType(), this.env.nextLocalVarId(names), VarScope.FUNCTION, VarKind.TEMP); this.env.enclFunc.localVars.add(tempVarDcl); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java index 388089ae40b0..65333e244cc2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmConstants.java @@ -252,6 +252,7 @@ public class JvmConstants { public static final String SYSTEM = "java/lang/System"; public static final String BIG_DECIMAL = "java/math/BigDecimal"; public static final String STRING_CONCAT_FACTORY = "java/lang/invoke/StringConcatFactory"; + public static final String RECEIVE_FIELD = "io/ballerina/runtime/internal/scheduling/WDChannels$ReceiveField"; // service objects, annotation processing related classes public static final String ANNOTATION_UTILS = "io/ballerina/runtime/internal/AnnotationUtils"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmSignatures.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmSignatures.java index fd7486ed5043..a2d231aef6e2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmSignatures.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmSignatures.java @@ -83,6 +83,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OPTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.PATH; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.READONLY_TYPE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RECEIVE_FIELD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.REF_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.REG_EXP_ASSERTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.REG_EXP_ATOM_QUANTIFIER; @@ -263,6 +264,7 @@ public class JvmSignatures { public static final String GET_TYPE_REF_TYPE_IMPL = "L" + TYPE_REF_TYPE_IMPL + ";"; public static final String GET_WD_CHANNELS = "L" + WD_CHANNELS + ";"; public static final String GET_WORKER_DATA_CHANNEL = "(L" + STRING_VALUE + ";)L" + WORKER_DATA_CHANNEL + ";"; + public static final String REMOVE_WORKER_DATA_CHANNEL = "(L" + STRAND_CLASS + ";L" + STRING_VALUE + ";)V"; public static final String GET_XML = "L" + XML_VALUE + ";"; public static final String HANDLE_CHANNEL_ERROR = "([L" + CHANNEL_DETAILS + ";L" + ERROR_VALUE + ";)V"; public static final String HANDLE_ERROR_RETURN = "(L" + OBJECT + ";)V"; @@ -284,6 +286,7 @@ public class JvmSignatures { public static final String INIT_BAL_ENV_WITH_FUNC_NAME = "(L" + STRAND_CLASS + ";L" + MODULE + ";L" + STRING_VALUE + ";[L" + FUNCTION_PARAMETER + ";)V"; public static final String INIT_CHANNEL_DETAILS = "(L" + STRING_VALUE + ";ZZ)V"; + public static final String INIT_RECEIVE_FIELD = "(L" + STRING_VALUE + ";L" + STRING_VALUE + ";)V"; public static final String INIT_CLI_SPEC = "(L" + OPTION + ";[L" + OPERAND + ";[L" + STRING_VALUE + ";)V"; public static final String INIT_CONFIG = "([L" + STRING_VALUE + ";[L" + PATH + ";L" + STRING_VALUE + ";)V"; public static final String INIT_CONFIGURABLES = @@ -502,6 +505,9 @@ public class JvmSignatures { public static final String GET_TEST_CONFIG_PATH = "(L" + MODULE + ";L" + STRING_VALUE + ";L" + STRING_VALUE + ";)L" + TOML_DETAILS + ";"; public static final String SET_DEFAULT_VALUE_METHOD = "(L" + STRING_VALUE + ";L" + B_FUNCTION_POINTER + ";)V"; + public static final String ALT_RECEIVE_CALL = "(L" + STRAND_CLASS + ";[L" + STRING_VALUE + ";)L" + OBJECT + ";"; + public static final String MULTIPLE_RECEIVE_CALL = "(L" + STRAND_CLASS + ";[L" + RECEIVE_FIELD + ";L" + TYPE + + ";)L" + OBJECT + ";"; private JvmSignatures() { } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java index 5950965e4ef3..11abc5ddffe2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java @@ -46,6 +46,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; @@ -53,11 +54,14 @@ import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.compiler.util.Unifier; +import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static org.objectweb.asm.Opcodes.AALOAD; import static org.objectweb.asm.Opcodes.AASTORE; @@ -134,6 +138,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OBJECT; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.PANIC_FIELD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.PREDEFINED_TYPES; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RECEIVE_FIELD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULE_FUNCTION_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULE_LOCAL_METHOD; @@ -146,12 +151,14 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND_THREAD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND_VALUE_ANY; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRING_CONCAT_FACTORY; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRING_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TYPE_ANYDATA_ARRAY; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.VALUE_OF_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.WD_CHANNELS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.WORKER_DATA_CHANNEL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.WORKER_UTILS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmInstructionGen.addJUnboxInsn; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.ALT_RECEIVE_CALL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.ANNOTATION_GET_STRAND; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.ANY_TO_JBOOLEAN; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.BAL_ENV_PARAM; @@ -179,6 +186,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_ANYDATA_ARRAY; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_BAL_ENV_WITH_FUNC_NAME; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_DECIMAL; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_RECEIVE_FIELD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INT_TO_STRING; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INT_VALUE_OF_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.IS_CONCURRENT; @@ -186,8 +194,10 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_JOBJECT_TYPE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOCK; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MAP_PUT; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MULTIPLE_RECEIVE_CALL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.PANIC_IF_IN_LOCK; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.PASS_OBJECT_RETURN_OBJECT; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.REMOVE_WORKER_DATA_CHANNEL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.RETURN_OBJECT; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.SCHEDULE_FUNCTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.SCHEDULE_LOCAL; @@ -387,6 +397,14 @@ public void genTerminator(BIRTerminator terminator, String moduleClassName, BIRN case WK_RECEIVE: this.genWorkerReceiveIns((BIRTerminator.WorkerReceive) terminator, localVarOffset, invocationVarIndex); return; + case WK_ALT_RECEIVE: + this.genWorkerAlternateReceiveIns((BIRTerminator.WorkerAlternateReceive) terminator, localVarOffset, + invocationVarIndex); + return; + case WK_MULTIPLE_RECEIVE: + this.genWorkerMultipleReceiveIns((BIRTerminator.WorkerMultipleReceive) terminator, localVarOffset, + invocationVarIndex); + return; case FLUSH: this.genFlushIns((BIRTerminator.Flush) terminator, localVarOffset, invocationVarIndex); return; @@ -1229,6 +1247,79 @@ private void genWorkerSendIns(BIRTerminator.WorkerSend ins, int localVarOffset, } } + private void genWorkerAlternateReceiveIns(BIRTerminator.WorkerAlternateReceive ins, int localVarOffset, + int invocationVarIndex) { + BIRNode.BIRVariableDcl listVar = new BIRNode.BIRVariableDcl(symbolTable.anyType, new Name("channels"), + VarScope.FUNCTION, VarKind.LOCAL); + int channelIndex = this.getJVMIndexOfVarRef(listVar); + int channelSize = ins.channels.size(); + this.mv.visitIntInsn(BIPUSH, channelSize); + this.mv.visitTypeInsn(ANEWARRAY, STRING_VALUE); + int i = 0; + while (i < channelSize) { + this.mv.visitInsn(DUP); + this.mv.visitIntInsn(BIPUSH, i); + this.mv.visitVarInsn(ILOAD, invocationVarIndex); + this.mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS, INT_TO_STRING, + new Handle(H_INVOKESTATIC, STRING_CONCAT_FACTORY, MAKE_CONCAT_WITH_CONSTANTS, + HANDLE_DESCRIPTOR_FOR_STRING_CONCAT, false), + ins.channels.get(i) + START_OF_HEADING_WITH_SEMICOLON); + this.mv.visitInsn(AASTORE); + i += 1; + } + this.mv.visitVarInsn(ASTORE, channelIndex); + this.mv.visitVarInsn(ALOAD, localVarOffset); + if (!ins.isSameStrand) { + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "parent", GET_STRAND); + } + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "wdChannels", GET_WD_CHANNELS); + this.mv.visitVarInsn(ALOAD, localVarOffset); + this.mv.visitVarInsn(ALOAD, channelIndex); + this.mv.visitMethodInsn(INVOKEVIRTUAL, WD_CHANNELS, "receiveDataAlternateChannels", ALT_RECEIVE_CALL, false); + + generateReceiveResultStore(ins.lhsOp); + } + + + private void genWorkerMultipleReceiveIns(BIRTerminator.WorkerMultipleReceive ins, int localVarOffset, + int invocationVarIndex) { + BIRNode.BIRVariableDcl listVar = new BIRNode.BIRVariableDcl(symbolTable.anyType, new Name("channels"), + VarScope.FUNCTION, VarKind.LOCAL); + int channelIndex = this.getJVMIndexOfVarRef(listVar); + int channelSize = ins.receiveFields.size(); + this.mv.visitIntInsn(BIPUSH, channelSize); + this.mv.visitTypeInsn(ANEWARRAY, RECEIVE_FIELD); + int i = 0; + while (i < channelSize) { + BIRTerminator.WorkerMultipleReceive.ReceiveField receiveField = ins.receiveFields.get(i); + this.mv.visitInsn(DUP); + this.mv.visitIntInsn(BIPUSH, i); + this.mv.visitTypeInsn(NEW, RECEIVE_FIELD); + this.mv.visitInsn(DUP); + this.mv.visitLdcInsn(receiveField.key()); + this.mv.visitVarInsn(ILOAD, invocationVarIndex); + this.mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS, INT_TO_STRING, + new Handle(H_INVOKESTATIC, STRING_CONCAT_FACTORY, MAKE_CONCAT_WITH_CONSTANTS, + HANDLE_DESCRIPTOR_FOR_STRING_CONCAT, false), + receiveField.workerReceive() + START_OF_HEADING_WITH_SEMICOLON); + this.mv.visitMethodInsn(INVOKESPECIAL, RECEIVE_FIELD, JVM_INIT_METHOD, INIT_RECEIVE_FIELD, false); + this.mv.visitInsn(AASTORE); + i += 1; + } + this.mv.visitVarInsn(ASTORE, channelIndex); + this.mv.visitVarInsn(ALOAD, localVarOffset); + if (!ins.isSameStrand) { + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "parent", GET_STRAND); + } + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "wdChannels", GET_WD_CHANNELS); + this.mv.visitVarInsn(ALOAD, localVarOffset); + this.mv.visitVarInsn(ALOAD, channelIndex); + jvmTypeGen.loadType(this.mv, ins.targetType); + this.mv.visitMethodInsn(INVOKEVIRTUAL, WD_CHANNELS, "receiveDataMultipleChannels", + MULTIPLE_RECEIVE_CALL, false); + generateReceiveResultStore(ins.lhsOp); + } + private void genWorkerReceiveIns(BIRTerminator.WorkerReceive ins, int localVarOffset, int invocationVarIndex) { this.mv.visitVarInsn(ALOAD, localVarOffset); @@ -1245,7 +1336,10 @@ private void genWorkerReceiveIns(BIRTerminator.WorkerReceive ins, int localVarOf this.mv.visitVarInsn(ALOAD, localVarOffset); this.mv.visitMethodInsn(INVOKEVIRTUAL, WORKER_DATA_CHANNEL, "tryTakeData", TRY_TAKE_DATA, false); + generateReceiveResultStore(ins.lhsOp); + } + private void generateReceiveResultStore(BIROperand ins) { BIRNode.BIRVariableDcl tempVar = new BIRNode.BIRVariableDcl(symbolTable.anyType, new Name("wrkMsg"), VarScope.FUNCTION, VarKind.ARG); int wrkResultIndex = this.getJVMIndexOfVarRef(tempVar); @@ -1258,9 +1352,8 @@ private void genWorkerReceiveIns(BIRTerminator.WorkerReceive ins, int localVarOf Label withinReceiveSuccess = new Label(); this.mv.visitLabel(withinReceiveSuccess); this.mv.visitVarInsn(ALOAD, wrkResultIndex); - jvmCastGen.addUnboxInsn(this.mv, ins.lhsOp.variableDcl.type); - this.storeToVar(ins.lhsOp.variableDcl); - + jvmCastGen.addUnboxInsn(this.mv, ins.variableDcl.type); + this.storeToVar(ins.variableDcl); this.mv.visitLabel(jumpAfterReceive); } @@ -1362,6 +1455,26 @@ private void genResourcePathArgs(List pathArgs) { public void genReturnTerm(int returnVarRefIndex, BIRNode.BIRFunction func, int invocationVarIndex, int localVarOffset) { + if (func.workerChannels != null) { + Set uniqueValues = new HashSet<>(); + for (BIRNode.ChannelDetails channel : func.workerChannels) { + if (uniqueValues.add(channel)) { + this.mv.visitVarInsn(ALOAD, localVarOffset); + if (Symbols.isFlagOn(func.flags, Flags.WORKER)) { + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "parent", GET_STRAND); + } + this.mv.visitFieldInsn(GETFIELD, STRAND_CLASS, "wdChannels", GET_WD_CHANNELS); + this.mv.visitVarInsn(ALOAD, localVarOffset); + this.mv.visitVarInsn(ILOAD, invocationVarIndex); + this.mv.visitInvokeDynamicInsn(MAKE_CONCAT_WITH_CONSTANTS, INT_TO_STRING, + new Handle(H_INVOKESTATIC, STRING_CONCAT_FACTORY, MAKE_CONCAT_WITH_CONSTANTS, + HANDLE_DESCRIPTOR_FOR_STRING_CONCAT, false), channel.name + + START_OF_HEADING_WITH_SEMICOLON); + this.mv.visitMethodInsn(INVOKEVIRTUAL, WD_CHANNELS, "removeCompletedChannels", + REMOVE_WORKER_DATA_CHANNEL, false); + } + } + } BType bType = unifier.build(func.type.retType); generateReturnTermFromType(returnVarRefIndex, bType, func, invocationVarIndex, localVarOffset); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRTerminator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRTerminator.java index 4dd37845dfee..0f3181d4e26d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRTerminator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRTerminator.java @@ -20,6 +20,7 @@ import io.ballerina.tools.diagnostics.Location; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.Name; import java.util.ArrayList; @@ -679,6 +680,114 @@ public BIRBasicBlock[] getNextBasicBlocks() { } } + + /** + * A worker receive instruction for alternate receive. + *

+ * e.g., WRK_RECEIVE w1 | w2; + * + * @since 2201.9.0 + */ + public static class WorkerAlternateReceive extends BIRTerminator { + public List channels; + public boolean isSameStrand; + + public WorkerAlternateReceive(Location pos, List channels, BIROperand lhsOp, + boolean isSameStrand, BIRBasicBlock thenBB) { + super(pos, InstructionKind.WK_ALT_RECEIVE); + this.channels = channels; + this.thenBB = thenBB; + this.isSameStrand = isSameStrand; + this.lhsOp = lhsOp; + } + + public WorkerAlternateReceive(Location pos, List channels, BIROperand lhsOp, boolean isSameStrand, + BIRBasicBlock thenBB, BirScope scope) { + this(pos, channels, lhsOp, isSameStrand, thenBB); + this.scope = scope; + } + + @Override + public void accept(BIRVisitor visitor) { + visitor.visit(this); + } + + @Override + public BIROperand[] getRhsOperands() { + return new BIROperand[0]; + } + + @Override + public void setRhsOperands(BIROperand[] operands) { + // do nothing + } + + @Override + public BIRBasicBlock[] getNextBasicBlocks() { + return new BIRBasicBlock[]{thenBB}; + } + } + + /** + * A worker receive instruction for multiple receive. + *

+ * e.g., WRK_RECEIVE {w1 , w2}; + * + * @since 2201.9.0 + */ + public static class WorkerMultipleReceive extends BIRTerminator { + public boolean isSameStrand; + public BType targetType; + public List receiveFields; + + public WorkerMultipleReceive(Location pos, List receiveFields, BIROperand lhsOp, + boolean isSameStrand, BIRBasicBlock thenBB) { + super(pos, InstructionKind.WK_MULTIPLE_RECEIVE); + this.thenBB = thenBB; + this.isSameStrand = isSameStrand; + this.lhsOp = lhsOp; + this.targetType = lhsOp.variableDcl.type; + this.receiveFields = receiveFields; + } + + public WorkerMultipleReceive(Location pos, List receiveFields, BIROperand lhsOp, + boolean isSameStrand, BIRBasicBlock thenBB, BirScope scope) { + this(pos, receiveFields, lhsOp, isSameStrand, thenBB); + this.scope = scope; + } + + @Override + public void accept(BIRVisitor visitor) { + visitor.visit(this); + } + + @Override + public BIROperand[] getRhsOperands() { + return new BIROperand[0]; + } + + @Override + public void setRhsOperands(BIROperand[] operands) { + // do nothing + } + + @Override + public BIRBasicBlock[] getNextBasicBlocks() { + return new BIRBasicBlock[]{thenBB}; + } + + /** + * A worker receive field for multiple receive action. + * @since 2201.9.0 + * @param key the field name of the result + * @param workerReceive the channel name + */ + public record ReceiveField(String key, String workerReceive) { + } + } + + + /** * A worker send instruction. *

diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRVisitor.java index d2fd180c907c..72320e029854 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/BIRVisitor.java @@ -146,6 +146,13 @@ public void visit(BIRTerminator.WorkerSend workerSend) { throw new UnsupportedOperationException(); } + public void visit(BIRTerminator.WorkerAlternateReceive altReceive) { + throw new AssertionError(); + } + + public void visit(BIRTerminator.WorkerMultipleReceive multipleReceive) { + throw new AssertionError(); + } // Non-terminating instructions diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/InstructionKind.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/InstructionKind.java index e2584fa16288..12979792c5bc 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/InstructionKind.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/model/InstructionKind.java @@ -38,6 +38,8 @@ public enum InstructionKind { FIELD_LOCK((byte) 12), UNLOCK((byte) 13), WAIT_ALL((byte) 14), + WK_ALT_RECEIVE((byte) 15), + WK_MULTIPLE_RECEIVE((byte) 16), // Non-terminating instructions MOVE((byte) 20), diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRBasicBlockOptimizer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRBasicBlockOptimizer.java index 20e740584fdf..a53698cbc86a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRBasicBlockOptimizer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRBasicBlockOptimizer.java @@ -281,4 +281,11 @@ public void visit(BIRTerminator.WorkerSend workerSend) { workerSend.thenBB = this.env.nextBB; } } + + @Override + public void visit(BIRTerminator.WorkerAlternateReceive workerAlternateReceive) { + if (workerAlternateReceive.thenBB == this.env.currentBB) { + workerAlternateReceive.thenBB = this.env.nextBB; + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRLockOptimizer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRLockOptimizer.java index 045caf512a88..66871a8ae3c1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRLockOptimizer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIRLockOptimizer.java @@ -248,6 +248,16 @@ public void visit(BIRTerminator.WorkerSend workerSend) { // Do nothing } + @Override + public void visit(BIRTerminator.WorkerAlternateReceive workerAlternateReceive) { + // Do nothing + } + + @Override + public void visit(BIRTerminator.WorkerMultipleReceive workerMultipleReceive) { + // Do nothing + } + @Override public void visit(BIRNode.BIRBasicBlock birBasicBlock) { BIRTerminator terminator = birBasicBlock.terminator; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIROptimizer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIROptimizer.java index b496372de275..53f9300798d9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIROptimizer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/optimizer/BIROptimizer.java @@ -480,6 +480,16 @@ public void visit(BIRTerminator.WorkerSend workerSend) { this.optimizeNode(workerSend.data, this.env); } + @Override + public void visit(BIRTerminator.WorkerAlternateReceive workerReceive) { + this.optimizeNode(workerReceive.lhsOp, this.env); + } + + @Override + public void visit(BIRTerminator.WorkerMultipleReceive workerReceive) { + this.optimizeNode(workerReceive.lhsOp, this.env); + } + // Non-terminating instructions @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRInstructionWriter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRInstructionWriter.java index 06f6c726f850..fa5c61257c1a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRInstructionWriter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRInstructionWriter.java @@ -241,6 +241,27 @@ public void visit(BIRTerminator.WorkerSend entry) { addCpAndWriteString(entry.thenBB.id.value); } + @Override + public void visit(BIRTerminator.WorkerAlternateReceive entry) { + entry.channels.forEach(key -> buf.writeInt(addStringCPEntry(key))); + entry.lhsOp.accept(this); + buf.writeBoolean(entry.isSameStrand); + addCpAndWriteString(entry.thenBB.id.value); + } + + @Override + public void visit(BIRTerminator.WorkerMultipleReceive entry) { + entry.receiveFields.forEach(key -> { + buf.writeInt(addStringCPEntry(key.key())); + buf.writeInt(addStringCPEntry(key.workerReceive())); + }); + writeType(entry.targetType); + entry.lhsOp.accept(this); + buf.writeBoolean(entry.isSameStrand); + addCpAndWriteString(entry.thenBB.id.value); + } + + @Override public void visit(BIRTerminator.WaitAll waitAll) { waitAll.lhsOp.accept(this); buf.writeInt(waitAll.keys.size()); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java index 1f03f5a2a32c..0c97c5d06c64 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java @@ -64,6 +64,7 @@ import org.wso2.ballerinalang.compiler.tree.BLangVariable; import org.wso2.ballerinalang.compiler.tree.BLangXMLNS; import org.wso2.ballerinalang.compiler.tree.OCEDynamicEnvironmentData; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -90,6 +91,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownDocumentationLine; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangRecordLiteral; @@ -1507,6 +1509,16 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr) { result = syncSendExpr; } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + result = altWorkerReceive; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + result = multipleWorkerReceive; + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { result = workerReceiveNode; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java index 8919bbaed9ca..0e8370446e0e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java @@ -76,6 +76,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -105,6 +106,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownDocumentationLine; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -1218,6 +1220,16 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr) { result = syncSendExpr; } + @Override + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + result = alternateWorkerReceive; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + result = multipleWorkerReceive; + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { result = workerReceiveNode; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ConstantPropagation.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ConstantPropagation.java index 5f3c66d95b1f..496d85290cdb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ConstantPropagation.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ConstantPropagation.java @@ -62,6 +62,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -85,6 +86,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -615,6 +617,16 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { result = asyncSendExpr; } + @Override + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + result = alternateWorkerReceive; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + result = multipleWorkerReceive; + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { result = workerReceiveNode; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java index f2f113454d19..dfb4df771baf 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java @@ -129,6 +129,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangMatchClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangOnFailClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -169,6 +170,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangTupleLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -219,6 +221,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerAsyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerFlushExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerReceive; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSendReceiveExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLAttribute; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLCommentLiteral; @@ -361,6 +364,7 @@ public class Desugar extends BLangNodeVisitor { private static final String GENERATED_ERROR_VAR = "$error$"; private static final String HAS_KEY = "hasKey"; private static final String CREATE_RECORD_VALUE = "createRecordFromMap"; + private static final String CHANNEL_AUTO_CLOSE_FUNC_NAME = "autoClose"; public static final String XML_INTERNAL_SELECT_DESCENDANTS = "selectDescendants"; public static final String XML_INTERNAL_CHILDREN = "children"; @@ -414,6 +418,9 @@ public class Desugar extends BLangNodeVisitor { private boolean isVisitingQuery; private boolean desugarToReturn; + // Worker related variables + private Set channelsWithinIfStmt = new LinkedHashSet<>(); + // Safe navigation related variables private Stack matchStmtStack = new Stack<>(); Stack accessExprStack = new Stack<>(); @@ -3470,13 +3477,101 @@ public void visit(BLangExpressionStmt exprStmtNode) { @Override public void visit(BLangIf ifNode) { + Set prevChannels = this.channelsWithinIfStmt; ifNode.expr = rewriteExpr(ifNode.expr); + + Set ifBlockChannels = new LinkedHashSet<>(); + this.channelsWithinIfStmt = ifBlockChannels; ifNode.body = rewrite(ifNode.body, env); + + Set elseBlockChannels = new LinkedHashSet<>(); + this.channelsWithinIfStmt = elseBlockChannels; ifNode.elseStmt = rewrite(ifNode.elseStmt, env); + addWorkerChannelAutoCloseStmts(ifNode, ifBlockChannels, elseBlockChannels); + + prevChannels.addAll(ifBlockChannels); + prevChannels.addAll(elseBlockChannels); + this.channelsWithinIfStmt = prevChannels; result = ifNode; } + /** + * We need to close the channels that the send actions are not going to execute in the if-else block. + * This method will add channel auto close function calls at the beginning of if and/or else blocks. + *

+ * If else will be generated as follows: + *

+ * + * if expr { + * autoClose(string ... channelIds); + * stmti1; + * stmti2; + * ... + * } else { + * autoClose(string ... channelIds); + * stmtj1; + * stmtj2; + * ... + * } + * + * + * @param ifNode if-else node + * @param ifBlockChannels channels used in if block + * @param elseBlockChannels channels used in else block + */ + private void addWorkerChannelAutoCloseStmts(BLangIf ifNode, + Set ifBlockChannels, + Set elseBlockChannels) { + if (!elseBlockChannels.isEmpty()) { + ifNode.body.stmts.add(0, generateChannelAutoCloseStmt(elseBlockChannels)); + } + if (!ifBlockChannels.isEmpty()) { + // Three cases to handle: + // 1) No else block + // 2) There is an else block + // 3) Else block itself is an if-else node + + BLangStatement elseStmt = ifNode.elseStmt; + + if (elseStmt == null) { + List stmts = + new ArrayList<>(Collections.singletonList(generateChannelAutoCloseStmt(ifBlockChannels))); + ifNode.elseStmt = rewrite(ASTBuilderUtil.createBlockStmt(symTable.builtinPos, stmts), env); + } else if (elseStmt.getKind() == NodeKind.BLOCK) { + ((BLangBlockStmt) elseStmt).stmts.add(0, generateChannelAutoCloseStmt(ifBlockChannels)); + } else { + assert elseStmt.getKind() == NodeKind.IF; + List stmts = new ArrayList<>(2); + stmts.add(generateChannelAutoCloseStmt(ifBlockChannels)); + stmts.add(elseStmt); + ifNode.elseStmt = rewrite(ASTBuilderUtil.createBlockStmt(elseStmt.pos, stmts), env); + } + } + } + + private BLangExpressionStmt generateChannelAutoCloseStmt(Set channels) { + List restArgs = generateChannelIdLiteralList(channels); + BLangInvocation funcInvocation = createLangLibInvocationNode(CHANNEL_AUTO_CLOSE_FUNC_NAME, new ArrayList<>(), + restArgs, symTable.nilType, symTable.builtinPos); + funcInvocation.internal = true; + + BLangExpressionStmt exprStmt = (BLangExpressionStmt) TreeBuilder.createExpressionStatementNode(); + exprStmt.internal = true; + exprStmt.expr = funcInvocation; + exprStmt.pos = symTable.builtinPos; + return rewrite(exprStmt, env); + } + + private List generateChannelIdLiteralList(Set channels) { + List exprs = new ArrayList<>(); + for (BLangWorkerSendReceiveExpr.Channel channel : channels) { + BLangLiteral channelIdLiteral = createStringLiteral(symTable.builtinPos, channel.channelId()); + exprs.add(channelIdLiteral); + } + return exprs; + } + @Override public void visit(BLangMatchStatement matchStatement) { BLangOnFailClause currentOnFailClause = this.onFailClause; @@ -8175,15 +8270,27 @@ private BLangFunction createUserDefinedObjectInitFn(BLangClassDefinition classDe @Override public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { asyncSendExpr.expr = visitCloneInvocation(rewriteExpr(asyncSendExpr.expr), asyncSendExpr.expr.getBType()); + this.channelsWithinIfStmt.add(asyncSendExpr.getChannel()); result = asyncSendExpr; } @Override public void visit(BLangWorkerSyncSendExpr syncSendExpr) { syncSendExpr.expr = visitCloneInvocation(rewriteExpr(syncSendExpr.expr), syncSendExpr.expr.getBType()); + this.channelsWithinIfStmt.add(syncSendExpr.getChannel()); result = syncSendExpr; } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + result = altWorkerReceive; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + result = multipleWorkerReceive; + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { result = workerReceiveNode; @@ -8891,7 +8998,15 @@ protected BLangInvocation createLangLibInvocationNode(String functionName, } private BLangInvocation createLangLibInvocationNode(String functionName, - List args, + List requiredArgs, + BType retType, + Location pos) { + return createLangLibInvocationNode(functionName, requiredArgs, new ArrayList<>(), retType, pos); + } + + private BLangInvocation createLangLibInvocationNode(String functionName, + List requiredArgs, + List restArgs, BType retType, Location pos) { BLangInvocation invocationNode = (BLangInvocation) TreeBuilder.createInvocationNode(); @@ -8906,9 +9021,8 @@ private BLangInvocation createLangLibInvocationNode(String functionName, invocationNode.symbol = symResolver.lookupMethodInModule(symTable.langInternalModuleSymbol, names.fromString(functionName), env); - ArrayList requiredArgs = new ArrayList<>(); - requiredArgs.addAll(args); - invocationNode.requiredArgs = requiredArgs; + invocationNode.requiredArgs = new ArrayList<>(requiredArgs); + invocationNode.restArgs = new ArrayList<>(restArgs); invocationNode.setBType(retType != null ? retType : ((BInvokableSymbol) invocationNode.symbol).retType); invocationNode.langLibInvocation = true; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java index 707d664861a6..ebb259a9c489 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java @@ -81,6 +81,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -107,6 +108,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -2713,9 +2715,25 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { this.acceptNode(asyncSendExpr.expr); } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + for (BLangWorkerReceive bLangWorkerReceive : altWorkerReceive.getWorkerReceives()) { + acceptNode(bLangWorkerReceive); + } + result = altWorkerReceive; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : multipleWorkerReceive.getReceiveFields()) { + acceptNode(rvField.getKey()); + acceptNode(rvField.getWorkerReceive()); + } + result = multipleWorkerReceive; + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { - workerReceiveNode.sendExpression = rewrite(workerReceiveNode.sendExpression); result = workerReceiveNode; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java index 5502a2e3d35a..e1287f447eeb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.parser; +import io.ballerina.compiler.syntax.tree.AlternateReceiveNode; import io.ballerina.compiler.syntax.tree.AnnotAccessExpressionNode; import io.ballerina.compiler.syntax.tree.AnnotationAttachPointNode; import io.ballerina.compiler.syntax.tree.AnnotationDeclarationNode; @@ -179,6 +180,8 @@ import io.ballerina.compiler.syntax.tree.ReUnicodePropertyEscapeNode; import io.ballerina.compiler.syntax.tree.ReUnicodeScriptNode; import io.ballerina.compiler.syntax.tree.ReceiveActionNode; +import io.ballerina.compiler.syntax.tree.ReceiveFieldNode; +import io.ballerina.compiler.syntax.tree.ReceiveFieldsNode; import io.ballerina.compiler.syntax.tree.RecordFieldNode; import io.ballerina.compiler.syntax.tree.RecordFieldWithDefaultValueNode; import io.ballerina.compiler.syntax.tree.RecordRestDescriptorNode; @@ -341,6 +344,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -370,6 +374,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -1742,7 +1747,18 @@ public BLangNode transform(NamedWorkerDeclarationNode namedWorkerDeclNode) { // Set the function body BLangBlockStmt blockStmt = (BLangBlockStmt) namedWorkerDeclNode.workerBody().apply(this); BLangBlockFunctionBody bodyNode = (BLangBlockFunctionBody) TreeBuilder.createBlockFunctionBodyNode(); - bodyNode.stmts = blockStmt.stmts; + if (namedWorkerDeclNode.onFailClause().isPresent()) { + BLangDo bLDo = (BLangDo) TreeBuilder.createDoNode(); + bLDo.pos = workerBodyPos; + bLDo.setBody(blockStmt); + OnFailClauseNode onFailClauseNode = namedWorkerDeclNode.onFailClause().get(); + bLDo.setOnFailClause( + (org.ballerinalang.model.clauses.OnFailClauseNode) (onFailClauseNode.apply(this))); + bodyNode.addStatement(bLDo); + } else { + bodyNode.stmts = blockStmt.stmts; + } + bodyNode.pos = workerBodyPos; bLFunction.body = bodyNode; bLFunction.internal = true; @@ -2509,20 +2525,57 @@ public BLangNode transform(TrapExpressionNode trapExpressionNode) { @Override public BLangNode transform(ReceiveActionNode receiveActionNode) { - BLangWorkerReceive workerReceiveExpr = (BLangWorkerReceive) TreeBuilder.createWorkerReceiveNode(); + Location receiveActionPos = getPosition(receiveActionNode); Node receiveWorkers = receiveActionNode.receiveWorkers(); - Token workerName; + if (receiveWorkers.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { - workerName = ((SimpleNameReferenceNode) receiveWorkers).name(); - } else { - // TODO: implement multiple-receive-action support - Location receiveFieldsPos = getPosition(receiveWorkers); - dlog.error(receiveFieldsPos, DiagnosticErrorCode.MULTIPLE_RECEIVE_ACTION_NOT_YET_SUPPORTED); - workerName = NodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN, - NodeFactory.createEmptyMinutiaeList(), NodeFactory.createEmptyMinutiaeList()); + BLangWorkerReceive singleWorkerRecv = + createSimpleWorkerReceive(((SimpleNameReferenceNode) receiveWorkers).name()); + singleWorkerRecv.pos = receiveActionPos; + return singleWorkerRecv; + } + + if (receiveWorkers.kind() == SyntaxKind.ALTERNATE_RECEIVE) { + SeparatedNodeList alternateWorkers = + ((AlternateReceiveNode) receiveWorkers).workers(); + List workerReceives = new ArrayList<>(alternateWorkers.size()); + for (SimpleNameReferenceNode w : alternateWorkers) { + workerReceives.add(createSimpleWorkerReceive(w.name())); + } + + BLangAlternateWorkerReceive alternateWorkerRecv = TreeBuilder.createAlternateWorkerReceiveNode(); + alternateWorkerRecv.setWorkerReceives(workerReceives); + alternateWorkerRecv.pos = receiveActionPos; + return alternateWorkerRecv; + } + + ReceiveFieldsNode receiveFieldsNode = (ReceiveFieldsNode) receiveWorkers; + SeparatedNodeList receiveFields = receiveFieldsNode.receiveFields(); + List fields = new ArrayList<>(receiveFields.size()); + for (Node receiveField : receiveFields) { + BLangMultipleWorkerReceive.BLangReceiveField rvField = new BLangMultipleWorkerReceive.BLangReceiveField(); + if (receiveField.kind() == SyntaxKind.SIMPLE_NAME_REFERENCE) { + Token name = ((SimpleNameReferenceNode) receiveField).name(); + rvField.setKey(createIdentifier(name)); + rvField.setWorkerReceive(createSimpleWorkerReceive(name)); + } else { + ReceiveFieldNode receiveFieldNode = (ReceiveFieldNode) receiveField; + rvField.setKey(createIdentifier(receiveFieldNode.fieldName().name())); + rvField.setWorkerReceive(createSimpleWorkerReceive(receiveFieldNode.peerWorker().name())); + } + fields.add(rvField); } - workerReceiveExpr.setWorkerName(createIdentifier(workerName)); - workerReceiveExpr.pos = getPosition(receiveActionNode); + + BLangMultipleWorkerReceive multipleWorkerRv = TreeBuilder.createMultipleWorkerReceiveNode(); + multipleWorkerRv.setReceiveFields(fields); + multipleWorkerRv.pos = receiveActionPos; + return multipleWorkerRv; + } + + private BLangWorkerReceive createSimpleWorkerReceive(Token workerRef) { + BLangWorkerReceive workerReceiveExpr = (BLangWorkerReceive) TreeBuilder.createWorkerReceiveNode(); + workerReceiveExpr.setWorkerName(createIdentifier(workerRef)); + workerReceiveExpr.pos = getPosition(workerRef); return workerReceiveExpr; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java index 17bd8441aff5..ca13f4df3bb4 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java @@ -85,6 +85,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -116,6 +117,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -1025,6 +1027,40 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { asyncSendExpr.cloneRef = clone; clone.expr = clone(asyncSendExpr.expr); clone.workerIdentifier = asyncSendExpr.workerIdentifier; + clone.setChannel(asyncSendExpr.getChannel()); + } + + @Override + public void visit(BLangAlternateWorkerReceive source) { + BLangAlternateWorkerReceive clone = new BLangAlternateWorkerReceive(); + source.cloneRef = clone; + + List workerReceives = new ArrayList<>(source.getWorkerReceives().size()); + for (BLangWorkerReceive workerReceive : source.getWorkerReceives()) { + workerReceives.add(clone(workerReceive)); + } + + clone.setWorkerReceives(workerReceives); + } + + @Override + public void visit(BLangMultipleWorkerReceive source) { + BLangMultipleWorkerReceive clone = new BLangMultipleWorkerReceive(); + source.cloneRef = clone; + + List cloneFields = + new ArrayList<>(source.getReceiveFields().size()); + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : source.getReceiveFields()) { + BLangMultipleWorkerReceive.BLangReceiveField clonedRvField = + new BLangMultipleWorkerReceive.BLangReceiveField(); + clonedRvField.setKey(clone(rvField.getKey())); + BLangWorkerReceive workerReceiveClone = clone(rvField.getWorkerReceive()); + workerReceiveClone.parent = clone; + clonedRvField.setWorkerReceive(workerReceiveClone); + cloneFields.add(clonedRvField); + } + + clone.setReceiveFields(cloneFields); } @Override @@ -1034,6 +1070,7 @@ public void visit(BLangWorkerReceive source) { source.cloneRef = clone; clone.workerIdentifier = source.workerIdentifier; + clone.setChannel(source.getChannel()); } @Override @@ -2191,6 +2228,7 @@ public void visit(BLangWorkerSyncSendExpr source) { source.cloneRef = clone; clone.workerIdentifier = source.workerIdentifier; clone.expr = clone(source.expr); + clone.setChannel(source.getChannel()); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java index 52d0502b62db..c5c5eb759a53 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java @@ -72,6 +72,7 @@ import org.wso2.ballerinalang.compiler.tree.BLangExprFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangExternalFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.BLangFunctionBody; import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import org.wso2.ballerinalang.compiler.tree.BLangImportPackage; import org.wso2.ballerinalang.compiler.tree.BLangInvokableNode; @@ -115,6 +116,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderByClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -139,6 +141,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -169,6 +172,8 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerAsyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerFlushExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerReceive; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSendExpr; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSendReceiveExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSyncSendExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLAttribute; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLCommentLiteral; @@ -259,6 +264,7 @@ import java.util.Objects; import java.util.Set; import java.util.Stack; +import java.util.function.Predicate; import java.util.stream.Collectors; import static org.ballerinalang.model.tree.NodeKind.LITERAL; @@ -279,8 +285,8 @@ */ public class CodeAnalyzer extends SimpleBLangNodeAnalyzer { - private static final CompilerContext.Key CODE_ANALYZER_KEY = - new CompilerContext.Key<>(); + private static final CompilerContext.Key CODE_ANALYZER_KEY = new CompilerContext.Key<>(); + private static final String NO_MESSAGE_ERROR_TYPE = "NoMessage"; private final SymbolResolver symResolver; private final SymbolTable symTable; @@ -1947,16 +1953,54 @@ public void visit(BLangExpressionStmt exprStmtNode, AnalyzerData data) { analyzeExpr(expr, data); } - private boolean isTopLevel(SymbolEnv env) { - return env.enclInvokable.body == env.node; - } - private boolean isInWorker(SymbolEnv env) { return env.enclInvokable.flagSet.contains(Flag.WORKER); } - private boolean isCommunicationAllowedLocation(SymbolEnv env) { - return isTopLevel(env); + private boolean isSendAllowedLocation(BLangFunctionBody enclInvokableBody, BLangNode node) { + return isCommunicationAllowedContext(enclInvokableBody, node, this::isSendAllowedContext); + } + + private boolean isReceiveAllowedLocation(BLangFunctionBody enclInvokableBody, BLangNode node) { + return isCommunicationAllowedContext(enclInvokableBody, node, this::isReceiveAllowedContext); + } + + private boolean isSendAllowedContext(BLangNode bLangNode) { + return isReceiveAllowedContext(bLangNode) || bLangNode.getKind() == NodeKind.IF; + } + + private boolean isReceiveAllowedContext(BLangNode bLangNode) { + return switch (bLangNode.getKind()) { + case BLOCK_FUNCTION_BODY, BLOCK, ON_FAIL, DO_STMT -> true; + default -> false; + }; + } + + private boolean isCommunicationAllowedContext(BLangFunctionBody enclInvokableBody, BLangNode node, + Predicate contextChecker) { + if (enclInvokableBody == node) { + return true; + } + + BLangNode parentNode = node.parent; + if (contextChecker.test(parentNode)) { + return isCommunicationAllowedContext(enclInvokableBody, parentNode, contextChecker); + } + + return false; + } + + private boolean withinIf(BLangFunctionBody enclInvokableBody, BLangNode node) { + if (enclInvokableBody == node) { + return false; + } + + BLangNode parentNode = node.parent; + if (parentNode.getKind() == NodeKind.IF) { + return true; + } + + return withinIf(enclInvokableBody, parentNode); } private boolean isDefaultWorkerCommunication(String workerIdentifier) { @@ -1999,45 +2043,72 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr, AnalyzerData data) { this.dlog.error(asyncSendExpr.expr.pos, DiagnosticErrorCode.INVALID_SEND_EXPR); } - String workerName = asyncSendExpr.workerIdentifier.getValue(); - if (data.withinQuery || (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt)) { + boolean invalidSendPos = data.withinQuery || + (!isSendAllowedLocation(data.env.enclInvokable.body, data.env.node) && + !data.inInternallyDefinedBlockStmt); + if (invalidSendPos) { this.dlog.error(asyncSendExpr.pos, DiagnosticErrorCode.UNSUPPORTED_WORKER_SEND_POSITION); was.hasErrors = true; } + String workerName = asyncSendExpr.workerIdentifier.getValue(); if (!this.workerExists(asyncSendExpr.workerType, workerName, data.env) || (!isWorkerFromFunction(data.env, names.fromString(workerName)) && !workerName.equals("function"))) { this.dlog.error(asyncSendExpr.pos, DiagnosticErrorCode.UNDEFINED_WORKER, workerName); was.hasErrors = true; } - asyncSendExpr.sendType = - createAccumulatedErrorTypeForMatchingReceive(asyncSendExpr.pos, asyncSendExpr.expr.getBType(), data); + boolean withinIf = !invalidSendPos && withinIf(data.env.enclInvokable.body, data.env.node); + setWorkerSendSendTypeDetails(asyncSendExpr, asyncSendExpr.expr.getBType(), withinIf, data); was.addWorkerAction(asyncSendExpr); analyzeExpr(asyncSendExpr.expr, data); validateActionParentNode(asyncSendExpr.pos, asyncSendExpr.expr); } - private BType createAccumulatedErrorTypeForMatchingReceive(Location pos, BType exprType, AnalyzerData data) { + private void setWorkerSendSendTypeDetails(BLangWorkerSendExpr workerSendExpr, BType exprType, + boolean withinIf, AnalyzerData data) { Set returnTypesUpToNow = data.returnTypes.peek(); LinkedHashSet returnTypeAndSendType = new LinkedHashSet<>() { { Comparator.comparing(BType::toString); } }; + + boolean hasNonErrorReturn = false; for (BType returnType : returnTypesUpToNow) { - if (onlyContainErrors(returnType)) { - returnTypeAndSendType.add(returnType); - } else { - this.dlog.error(pos, DiagnosticErrorCode.WORKER_SEND_AFTER_RETURN); + addErrorTypesToSet(returnType, returnTypeAndSendType); + if (hasNonErrorType(returnType)) { + hasNonErrorReturn = true; } } returnTypeAndSendType.add(exprType); + + BType sendTypeWithNoMsgIgnored; if (returnTypeAndSendType.size() > 1) { - return BUnionType.create(null, returnTypeAndSendType); + sendTypeWithNoMsgIgnored = BUnionType.create(null, returnTypeAndSendType); } else { - return exprType; + sendTypeWithNoMsgIgnored = exprType; + } + + BType sendType; + boolean noMessagePossible = withinIf || hasNonErrorReturn; + if (noMessagePossible) { + // There is a possibility that the send action may not be executed, thus adding NoMessageError type. + BSymbol noMsgErrSymbol = symTable.langErrorModuleSymbol.scope. + lookup(Names.fromString(NO_MESSAGE_ERROR_TYPE)).symbol; + returnTypeAndSendType.add(noMsgErrSymbol.getType()); + if (returnTypeAndSendType.size() > 1) { + sendType = BUnionType.create(null, returnTypeAndSendType); + } else { + sendType = exprType; + } + } else { + sendType = sendTypeWithNoMsgIgnored; } + + workerSendExpr.sendType = sendType; + workerSendExpr.sendTypeWithNoMsgIgnored = sendTypeWithNoMsgIgnored; + workerSendExpr.noMessagePossible = noMessagePossible; } @Override @@ -2059,7 +2130,10 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr, AnalyzerData data) { was.hasErrors = true; } - if (data.withinQuery || (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt)) { + boolean invalidSendPos = data.withinQuery || + (!isSendAllowedLocation(data.env.enclInvokable.body, data.env.node) && + !data.inInternallyDefinedBlockStmt); + if (invalidSendPos) { this.dlog.error(syncSendExpr.pos, DiagnosticErrorCode.UNSUPPORTED_WORKER_SEND_POSITION); was.hasErrors = true; } @@ -2070,12 +2144,27 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr, AnalyzerData data) { } syncSendExpr.setBType(BUnionType.create(null, symTable.nilType, symTable.errorType)); - syncSendExpr.sendType = - createAccumulatedErrorTypeForMatchingReceive(syncSendExpr.pos, syncSendExpr.expr.getBType(), data); + boolean withinIf = !invalidSendPos && withinIf(data.env.enclInvokable.body, data.env.node); + setWorkerSendSendTypeDetails(syncSendExpr, syncSendExpr.expr.getBType(), withinIf, data); was.addWorkerAction(syncSendExpr); analyzeExpr(syncSendExpr.expr, data); } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive, AnalyzerData data) { + data.workerActionSystemStack.peek().alternateWorkerReceives.add(altWorkerReceive); + for (BLangWorkerReceive bLangWorkerReceive : altWorkerReceive.getWorkerReceives()) { + analyzeExpr(bLangWorkerReceive, data); + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive, AnalyzerData data) { + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : multipleWorkerReceive.getReceiveFields()) { + analyzeExpr(rvField.getWorkerReceive(), data); + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode, AnalyzerData data) { // Validate worker receive @@ -2096,7 +2185,8 @@ public void visit(BLangWorkerReceive workerReceiveNode, AnalyzerData data) { } String workerName = workerReceiveNode.workerIdentifier.getValue(); - if (data.withinQuery || (!isCommunicationAllowedLocation(data.env) && !data.inInternallyDefinedBlockStmt)) { + if (data.withinQuery || (!isReceiveAllowedLocation(data.env.enclInvokable.body, data.env.node) && + !data.inInternallyDefinedBlockStmt)) { this.dlog.error(workerReceiveNode.pos, DiagnosticErrorCode.INVALID_WORKER_RECEIVE_POSITION); was.hasErrors = true; } @@ -2106,7 +2196,7 @@ public void visit(BLangWorkerReceive workerReceiveNode, AnalyzerData data) { was.hasErrors = true; } - workerReceiveNode.matchingSendsError = createAccumulatedErrorTypeForMatchingSyncSend(workerReceiveNode, data); + workerReceiveNode.matchingSendsError = createAccumulatedErrorTypeForMatchingSyncSend(data); was.addWorkerAction(workerReceiveNode); } @@ -2144,16 +2234,11 @@ private void verifyPeerCommunication(Location pos, BSymbol otherWorker, String o } } - public BType createAccumulatedErrorTypeForMatchingSyncSend(BLangWorkerReceive workerReceiveNode, - AnalyzerData data) { - Set returnTypesUpToNow = data.returnTypes.peek(); + public BType createAccumulatedErrorTypeForMatchingSyncSend(AnalyzerData data) { + LinkedHashSet returnTypesUpToNow = data.returnTypes.peek(); LinkedHashSet returnTypeAndSendType = new LinkedHashSet<>(); for (BType returnType : returnTypesUpToNow) { - if (onlyContainErrors(returnType)) { - returnTypeAndSendType.add(returnType); - } else { - this.dlog.error(workerReceiveNode.pos, DiagnosticErrorCode.WORKER_RECEIVE_AFTER_RETURN); - } + addErrorTypesToSet(returnType, returnTypeAndSendType); } returnTypeAndSendType.add(symTable.nilType); if (returnTypeAndSendType.size() > 1) { @@ -2163,27 +2248,44 @@ public BType createAccumulatedErrorTypeForMatchingSyncSend(BLangWorkerReceive wo } } - private boolean onlyContainErrors(BType returnType) { + private void addErrorTypesToSet(BType returnType, LinkedHashSet errorTypes) { + if (returnType == null) { + return; + } + + BType effType = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(returnType)); + if (effType.tag == TypeTags.ERROR) { + errorTypes.add(returnType); + } else if (effType.tag == TypeTags.UNION) { + for (BType memberType : ((BUnionType) effType).getMemberTypes()) { + BType t = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(memberType)); + if (t.tag == TypeTags.ERROR) { + errorTypes.add(memberType); + } + } + } + } + + private boolean hasNonErrorType(BType returnType) { if (returnType == null) { return false; } - returnType = types.getTypeWithEffectiveIntersectionTypes(returnType); - returnType = Types.getImpliedType(returnType); - if (returnType.tag == TypeTags.ERROR) { - return true; + BType effType = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(returnType)); + if (effType.tag == TypeTags.ERROR) { + return false; } - if (returnType.tag == TypeTags.UNION) { + if (effType.tag == TypeTags.UNION) { for (BType memberType : ((BUnionType) returnType).getMemberTypes()) { - BType t = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(memberType)); - if (t.tag != TypeTags.ERROR) { - return false; + if (hasNonErrorType(memberType)) { + return true; } } - return true; + return false; } - return false; + + return true; } @Override @@ -3580,6 +3682,13 @@ private static boolean isWorkerSyncSend(BLangNode action) { return action.getKind() == NodeKind.WORKER_SYNC_SEND; } + private static boolean isWorkerSendOrReceive(BLangNode action) { + return switch (action.getKind()) { + case WORKER_ASYNC_SEND, WORKER_SYNC_SEND, WORKER_RECEIVE -> true; + default -> false; + }; + } + private static boolean isWaitAction(BLangNode action) { return action.getKind() == NodeKind.WAIT_EXPR; } @@ -3634,22 +3743,21 @@ private void validateWorkerInteractions(WorkerActionSystem workerActionSystem, A continue; } BLangWorkerReceive receive = (BLangWorkerReceive) otherSM.currentAction(); - if (isWorkerSyncSend(currentAction)) { - this.validateWorkerActionParameters((BLangWorkerSyncSendExpr) currentAction, receive); - } else { - this.validateWorkerActionParameters((BLangWorkerAsyncSendExpr) currentAction, receive); - } + BLangWorkerSendExpr send = (BLangWorkerSendExpr) currentAction; + validateWorkerActionParameters(send, receive); + + BLangWorkerSendReceiveExpr.Channel channel = createChannel(workerActionSystem, worker, otherSM); + receive.setChannel(channel); + send.setChannel(channel); + otherSM.next(); data.workerSystemMovementSequence++; worker.next(); data.workerSystemMovementSequence++; - systemRunning = true; - String channelName = generateChannelName(worker.workerId, otherSM.workerId); - otherSM.node.sendsToThis.add(channelName); - - worker.node.sendsToThis.add(channelName); + otherSM.node.sendsToThis.add(channel); + worker.node.sendsToThis.add(channel); } // If we iterated move than the number of workers in the system and did not progress, @@ -3663,11 +3771,53 @@ private void validateWorkerInteractions(WorkerActionSystem workerActionSystem, A } } while (systemRunning); - if (!workerActionSystem.everyoneDone()) { + if (workerActionSystem.everyoneDone()) { + // By now, alternative receive corresponding senders have been paired. Hence, doing type-checking here + for (BLangAlternateWorkerReceive alternateWorkerRv : workerActionSystem.alternateWorkerReceives) { + typeCheckAlternateReceive(alternateWorkerRv); + } + } else { this.reportInvalidWorkerInteractionDiagnostics(workerActionSystem); } } + private void typeCheckAlternateReceive(BLangAlternateWorkerReceive altWorkerRv) { + LinkedHashSet altTypes = new LinkedHashSet<>(); + boolean noMessagePossible = true; + + // Result will have error:NoMessage if and only if all receives have it. + for (BLangWorkerReceive workerRv : altWorkerRv.getWorkerReceives()) { + altTypes.add(workerRv.send.sendTypeWithNoMsgIgnored); + if (!workerRv.send.noMessagePossible) { + noMessagePossible = false; + } + } + + if (noMessagePossible) { + BSymbol noMsgErrSymbol = symTable.langErrorModuleSymbol.scope. + lookup(Names.fromString(NO_MESSAGE_ERROR_TYPE)).symbol; + altTypes.add(noMsgErrSymbol.getType()); + } + + BType actualType; + if (altTypes.size() > 1) { + actualType = BUnionType.create(null, altTypes); + } else { + actualType = altTypes.iterator().next(); + } + + types.checkType(altWorkerRv, actualType, altWorkerRv.expectedType); + } + + private static BLangWorkerSendReceiveExpr.Channel createChannel(WorkerActionSystem workerActionSystem, + WorkerActionStateMachine worker, + WorkerActionStateMachine otherSM) { + String workerPairId = BLangWorkerSendReceiveExpr.Channel.workerPairId(worker.workerId, otherSM.workerId); + Integer eventIndex = workerActionSystem.workerEventIndexMap.getOrDefault(workerPairId, 0); + workerActionSystem.workerEventIndexMap.put(workerPairId, ++eventIndex); + return new BLangWorkerSendReceiveExpr.Channel(worker.workerId, otherSM.workerId, eventIndex); + } + private boolean validateWorkerInteractionsAfterWaitAction(WorkerActionSystem workerActionSystem) { boolean isValid = true; for (WorkerActionStateMachine worker : workerActionSystem.finshedWorkers) { @@ -3805,27 +3955,46 @@ private boolean isWorkerSymbol(BSymbol symbol) { } private void reportInvalidWorkerInteractionDiagnostics(WorkerActionSystem workerActionSystem) { - this.dlog.error(workerActionSystem.getRootPosition(), DiagnosticErrorCode.INVALID_WORKER_INTERACTION, - workerActionSystem.toString()); + boolean hasSendReceivePairingError = false; + for (WorkerActionStateMachine worker : workerActionSystem.finshedWorkers) { + for (BLangNode action : worker.actions) { + if (isWorkerSendOrReceive(action) && ((BLangWorkerSendReceiveExpr) action).getChannel() == null) { + hasSendReceivePairingError = true; + DiagnosticErrorCode errorCode = action.getKind() == NodeKind.WORKER_RECEIVE ? + DiagnosticErrorCode.INVALID_WORKER_RECEIVE_NO_MATCHING_WORKER_SEND : + DiagnosticErrorCode.INVALID_WORKER_SEND_NO_MATCHING_WORKER_RECEIVE; + dlog.error(action.pos, errorCode); + } + } + } + + if (!hasSendReceivePairingError) { + this.dlog.error(workerActionSystem.getRootPosition(), DiagnosticErrorCode.INVALID_WORKER_INTERACTION, + workerActionSystem.toString()); + } } - private void validateWorkerActionParameters(BLangWorkerAsyncSendExpr send, BLangWorkerReceive receive) { + private void validateWorkerActionParameters(BLangWorkerSendExpr send, BLangWorkerReceive receive) { send.receive = receive; - types.checkType(send.pos, symTable.nilType, send.expectedType, - DiagnosticErrorCode.INCOMPATIBLE_TYPES); - - types.checkType(receive, send.sendType, receive.getBType()); - addImplicitCast(send.sendType, receive); - NodeKind kind = receive.parent.getKind(); - if (kind == NodeKind.TRAP_EXPR || kind == NodeKind.CHECK_EXPR || kind == NodeKind.CHECK_PANIC_EXPR || - kind == NodeKind.FAIL) { + receive.send = send; + + NodeKind parentKind = receive.parent.getKind(); + if (NodeKind.ALTERNATE_WORKER_RECEIVE != receive.parent.getKind()) { + types.checkType(receive, send.sendType, receive.getBType()); + addImplicitCast(send.sendType, receive); + } + + if (parentKind == NodeKind.TRAP_EXPR || parentKind == NodeKind.CHECK_EXPR || + parentKind == NodeKind.CHECK_PANIC_EXPR || parentKind == NodeKind.FAIL) { typeChecker.checkExpr((BLangExpression) receive.parent, receive.env); } - receive.sendExpression = send.expr; - } - private void validateWorkerActionParameters(BLangWorkerSyncSendExpr send, BLangWorkerReceive receive) { - send.receive = receive; + if (send.getKind() == NodeKind.WORKER_ASYNC_SEND) { + types.checkType(send.pos, symTable.nilType, send.expectedType, + DiagnosticErrorCode.INCOMPATIBLE_TYPES); + return; + } + NodeKind parentNodeKind = send.parent.getKind(); if (parentNodeKind == NodeKind.VARIABLE) { BLangSimpleVariable variable = (BLangSimpleVariable) send.parent; @@ -3847,17 +4016,8 @@ private void validateWorkerActionParameters(BLangWorkerSyncSendExpr send, BLangW dlog.error(send.pos, DiagnosticErrorCode.ASSIGNMENT_REQUIRED, send.workerSymbol); } else { types.checkType(send.pos, receive.matchingSendsError, send.expectedType, - DiagnosticErrorCode.INCOMPATIBLE_TYPES); + DiagnosticErrorCode.INCOMPATIBLE_TYPES); } - - types.checkType(receive, send.sendType, receive.getBType()); - - addImplicitCast(send.sendType, receive); - NodeKind kind = receive.parent.getKind(); - if (kind == NodeKind.TRAP_EXPR || kind == NodeKind.CHECK_EXPR || kind == NodeKind.CHECK_PANIC_EXPR) { - typeChecker.checkExpr((BLangExpression) receive.parent, receive.env); - } - receive.sendExpression = send; } private void addImplicitCast(BType actualType, BLangWorkerReceive receive) { @@ -3983,9 +4143,11 @@ private boolean reportIfDeprecatedUsage(BSymbol constructSymbol, BLangExpression */ private static class WorkerActionSystem { + public final List alternateWorkerReceives = new ArrayList<>(); public List finshedWorkers = new ArrayList<>(); private Stack workerActionStateMachines = new Stack<>(); private Map workerInteractionEnvironments = new IdentityHashMap<>(); + private Map workerEventIndexMap = new HashMap<>(); private boolean hasErrors = false; @@ -4099,11 +4261,6 @@ public String toString() { } } - public static String generateChannelName(String source, String target) { - - return source + "->" + target; - } - private BLangNode getEnclosingClass(SymbolEnv env) { BLangNode node = env.node; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java index d20fdefbfe2e..c9455cadd312 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java @@ -94,6 +94,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -123,6 +124,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -1082,6 +1084,21 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr) { analyzeNode(syncSendExpr.expr, env); } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + for (BLangWorkerReceive workerReceive : altWorkerReceive.getWorkerReceives()) { + analyzeNode(workerReceive, env); + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : multipleWorkerReceive.getReceiveFields()) { + analyzeNode(rvField.getKey(), env); + analyzeNode(rvField.getWorkerReceive(), env); + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { // todo diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java index 5718e6b33668..02c8d4f83e0d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java @@ -132,6 +132,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -156,6 +157,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -1103,6 +1105,21 @@ public void visit(BLangForkJoin forkJoin) { public void visit(BLangWorkerAsyncSendExpr asyncSendExpr) { } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive) { + for (BLangWorkerReceive workerReceive : altWorkerReceive.getWorkerReceives()) { + analyzeNode(workerReceive, env); + } + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : multipleWorkerReceive.getReceiveFields()) { + analyzeNode(rvField.getKey(), env); + analyzeNode(rvField.getWorkerReceive(), env); + } + } + @Override public void visit(BLangWorkerReceive workerReceiveNode) { } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java index 30d26a889b16..e189ac1916b9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java @@ -99,6 +99,7 @@ import org.wso2.ballerinalang.compiler.tree.SimpleBLangNodeAnalyzer; import org.wso2.ballerinalang.compiler.tree.clauses.BLangOnFailClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -120,6 +121,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangListConstructorExpr.BLangListConstructorSpreadOpExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -2958,6 +2960,137 @@ public void visit(BLangWorkerAsyncSendExpr asyncSendExpr, AnalyzerData data) { data.resultType = symTable.nilType; } + @Override + public void visit(BLangAlternateWorkerReceive altWorkerReceive, AnalyzerData data) { + for (BLangWorkerReceive bLangWorkerReceive : altWorkerReceive.getWorkerReceives()) { + bLangWorkerReceive.accept(this, data); + } + altWorkerReceive.setBType(data.expType); + data.resultType = data.expType; + } + + @Override + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive, AnalyzerData data) { + BType compatibleType = validateAndGetMultipleReceiveCompatibleType(data.expType, multipleWorkerReceive, data); + if (symTable.semanticError.tag == compatibleType.tag) { + data.resultType = symTable.semanticError; + return; + } + + multipleWorkerReceive.setBType(compatibleType); + + if (TypeTags.RECORD == compatibleType.tag) { + BRecordType recordType = (BRecordType) compatibleType; + for (BLangMultipleWorkerReceive.BLangReceiveField receiveFiled : multipleWorkerReceive.getReceiveFields()) { + BField bField = recordType.fields.get(receiveFiled.getKey().value); + BType receiveFieldExpType; + if (bField != null) { + receiveFieldExpType = bField.type; + } else { + receiveFieldExpType = recordType.restFieldType; + } + + checkExpr(receiveFiled.getWorkerReceive(), receiveFieldExpType, data); + } + data.resultType = compatibleType; + return; + } + + BType receiveFieldExpType; + if (TypeTags.MAP == compatibleType.tag) { + receiveFieldExpType = ((BMapType) compatibleType).constraint; + } else { + assert TypeTags.READONLY == compatibleType.tag; + receiveFieldExpType = compatibleType; + } + + for (BLangMultipleWorkerReceive.BLangReceiveField receiveFiled : multipleWorkerReceive.getReceiveFields()) { + checkExpr(receiveFiled.getWorkerReceive(), receiveFieldExpType, data); + } + data.resultType = compatibleType; + } + + private BType validateAndGetMultipleReceiveCompatibleType(BType bType, + BLangMultipleWorkerReceive multipleWorkerReceive, + AnalyzerData data) { + HashSet multipleReceiveFieldNames = new HashSet<>(); + for (BLangMultipleWorkerReceive.BLangReceiveField receiveField : multipleWorkerReceive.getReceiveFields()) { + BLangIdentifier key = receiveField.getKey(); + String fieldName = key.value; + if (!multipleReceiveFieldNames.add(fieldName)) { + dlog.error(key.pos, DiagnosticErrorCode.DUPLICATE_KEY_IN_MULTIPLE_RECEIVE, fieldName); + } + } + + BType impliedType = Types.getImpliedType(bType); + if (impliedType.tag == TypeTags.NONE) { + dlog.error(multipleWorkerReceive.pos, DiagnosticErrorCode.RECEIVE_ACTION_NOT_SUPPORTED_WITH_VAR); + return symTable.semanticError; + } + + if (impliedType.tag != TypeTags.UNION) { + BType compatibleType = getMappingConstructorCompatibleNonUnionType(impliedType, data); + if (symTable.semanticError.tag == compatibleType.tag || (TypeTags.RECORD == compatibleType.tag && + !fieldsCompatibleWithRecord(multipleReceiveFieldNames, (BRecordType) compatibleType))) { + dlog.error(multipleWorkerReceive.pos, DiagnosticErrorCode.MULTIPLE_RECEIVE_COMPATIBLE_TYPE_NOT_FOUND, + impliedType); + return symTable.semanticError; + } + return compatibleType; + } + + List compatibleTypes = new ArrayList<>(); + for (BType memberType : ((BUnionType) impliedType).getMemberTypes()) { + BType impType = Types.getImpliedType(memberType); + BType compatibleType = getMappingConstructorCompatibleNonUnionType(impType, data); + + if (TypeTags.RECORD == compatibleType.tag && + !fieldsCompatibleWithRecord(multipleReceiveFieldNames, (BRecordType) compatibleType)) { + continue; + } + + if (symTable.semanticError.tag != compatibleType.tag) { + compatibleTypes.add(compatibleType); + } + } + + if (compatibleTypes.isEmpty()) { + dlog.error(multipleWorkerReceive.pos, DiagnosticErrorCode.MULTIPLE_RECEIVE_COMPATIBLE_TYPE_NOT_FOUND, + impliedType); + return symTable.semanticError; + } + + if (compatibleTypes.size() > 1) { + dlog.error(multipleWorkerReceive.pos, DiagnosticErrorCode.AMBIGUOUS_TYPES, impliedType); + return symTable.semanticError; + } + + return compatibleTypes.get(0); + } + + private boolean fieldsCompatibleWithRecord(HashSet fieldNames, BRecordType recordType) { + HashSet clonedFieldNames = new HashSet<>(fieldNames); + for (BField field : recordType.fields.values()) { + if (!types.isNeverTypeOrStructureTypeWithARequiredNeverMember(field.type)) { + // matching up field names against the record fields + if (clonedFieldNames.remove(field.name.value)) { + continue; + } + + if (Symbols.isFlagOn(field.symbol.flags, Flags.REQUIRED)) { + return false; + } + } + } + + if (!clonedFieldNames.isEmpty()) { + // matching the remaining field names to record rest field + return recordType.restFieldType != null && recordType.restFieldType.tag != TypeTags.NONE; + } + + return true; + } + @Override public void visit(BLangWorkerReceive workerReceiveExpr, AnalyzerData data) { BSymbol symbol = @@ -2972,9 +3105,9 @@ public void visit(BLangWorkerReceive workerReceiveExpr, AnalyzerData data) { workerReceiveExpr.workerType = symbol.type; workerReceiveExpr.workerSymbol = symbol; } - // The receive expression cannot be assigned to var, since we cannot infer the type. + // The receive-action cannot be assigned to var, since we cannot infer the type. if (symTable.noType == data.expType) { - this.dlog.error(workerReceiveExpr.pos, DiagnosticErrorCode.INVALID_USAGE_OF_RECEIVE_EXPRESSION); + this.dlog.error(workerReceiveExpr.pos, DiagnosticErrorCode.RECEIVE_ACTION_NOT_SUPPORTED_WITH_VAR); } // We cannot predict the type of the receive expression as it depends on the type of the data sent by the other // worker/channel. Since receive is an expression now we infer the type of it from the lhs of the statement. diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangFunction.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangFunction.java index cc8ccfdd28a4..8cb9ce6d101a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangFunction.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangFunction.java @@ -23,6 +23,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangWorkerSendReceiveExpr; import org.wso2.ballerinalang.compiler.tree.statements.BLangStatement; import org.wso2.ballerinalang.compiler.util.ClosureVarSymbol; @@ -57,7 +58,7 @@ public class BLangFunction extends BLangInvokableNode implements FunctionNode { public BInvokableSymbol originalFuncSymbol; - public LinkedHashSet sendsToThis = new LinkedHashSet<>(); + public LinkedHashSet sendsToThis = new LinkedHashSet<>(); // This only set when we encounter worker inside a fork statement. public String anonForkName; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeAnalyzer.java index 12bfd933e341..fd383d9bdffa 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeAnalyzer.java @@ -45,6 +45,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -76,6 +77,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -517,6 +519,10 @@ public abstract class BLangNodeAnalyzer { public abstract void visit(BLangWorkerReceive node, T data); + public abstract void visit(BLangAlternateWorkerReceive node, T data); + + public abstract void visit(BLangMultipleWorkerReceive node, T data); + public abstract void visit(BLangWorkerSyncSendExpr node, T data); public abstract void visit(BLangXMLAttribute node, T data); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeTransformer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeTransformer.java index 43c21c27b8fb..8a4386fc84a5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeTransformer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeTransformer.java @@ -45,6 +45,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -75,6 +76,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -822,6 +824,14 @@ public R transform(BLangWorkerReceive node, T data) { return transformNode(node, data); } + public R transform(BLangAlternateWorkerReceive node, T data) { + return transformNode(node, data); + } + + public R transform(BLangMultipleWorkerReceive node, T data) { + return transformNode(node, data); + } + public R transform(BLangWorkerSyncSendExpr node, T data) { return transformNode(node, data); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeVisitor.java index 2ed8668730b1..be815733febf 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangNodeVisitor.java @@ -47,6 +47,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangOrderKey; import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -91,6 +92,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; @@ -578,6 +580,14 @@ public void visit(BLangWorkerReceive workerReceiveNode) { throw new AssertionError(); } + public void visit(BLangAlternateWorkerReceive alternateWorkerReceive) { + throw new AssertionError(); + } + + public void visit(BLangMultipleWorkerReceive multipleWorkerReceive) { + throw new AssertionError(); + } + public void visit(BLangRollback rollbackNode) { throw new AssertionError(); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/SimpleBLangNodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/SimpleBLangNodeAnalyzer.java index b63a8ab91c6e..e08665a735e8 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/SimpleBLangNodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/SimpleBLangNodeAnalyzer.java @@ -48,6 +48,7 @@ import org.wso2.ballerinalang.compiler.tree.clauses.BLangSelectClause; import org.wso2.ballerinalang.compiler.tree.clauses.BLangWhereClause; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAccessExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangAlternateWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangAnnotAccessExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangArrowFunction; import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; @@ -79,6 +80,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMarkdownReturnParameterDocumentation; import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangMultipleWorkerReceive; import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction; @@ -1044,6 +1046,19 @@ public void visit(BLangWorkerFlushExpr node, T data) { visitNode(node.workerIdentifier, data); } + public void visit(BLangAlternateWorkerReceive node, T data) { + analyzeNode(node, data); + visitNode(node.getWorkerReceives(), data); + } + + public void visit(BLangMultipleWorkerReceive node, T data) { + analyzeNode(node, data); + for (BLangMultipleWorkerReceive.BLangReceiveField rvField : node.getReceiveFields()) { + visitNode(rvField.getKey(), data); + visitNode(rvField.getWorkerReceive(), data); + } + } + public void visit(BLangWorkerReceive node, T data) { analyzeNode(node, data); visitNode(node.workerIdentifier, data); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangAlternateWorkerReceive.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangAlternateWorkerReceive.java new file mode 100644 index 000000000000..1281fae42aa9 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangAlternateWorkerReceive.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.tree.expressions; + +import org.ballerinalang.model.tree.ActionNode; +import org.ballerinalang.model.tree.NodeKind; +import org.wso2.ballerinalang.compiler.tree.BLangNodeAnalyzer; +import org.wso2.ballerinalang.compiler.tree.BLangNodeTransformer; +import org.wso2.ballerinalang.compiler.tree.BLangNodeVisitor; + +import java.util.List; + +/** + * Represents alternate-receive in worker communication. + * + * @since 2201.9.0 + */ +public class BLangAlternateWorkerReceive extends BLangExpression implements ActionNode { + + private List workerReceives; + + @Override + public NodeKind getKind() { + return NodeKind.ALTERNATE_WORKER_RECEIVE; + } + + @Override + public void accept(BLangNodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public void accept(BLangNodeAnalyzer analyzer, T props) { + analyzer.visit(this, props); + } + + @Override + public R apply(BLangNodeTransformer modifier, T props) { + return modifier.transform(this, props); + } + + public List getWorkerReceives() { + return workerReceives; + } + + public void setWorkerReceives(List workerReceives) { + this.workerReceives = workerReceives; + } + + @Override + public String toString() { + return "CombinedWorkerReceive: " + this.toActionString(); + } + + public String toActionString() { + StringBuilder sb = new StringBuilder(" <- "); + int size = workerReceives.size(); + for (int i = 0; i < size - 1; i++) { + sb.append(workerReceives.get(i)).append(" | "); + } + sb.append(workerReceives.get(size - 1)); + return sb.toString(); + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangMultipleWorkerReceive.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangMultipleWorkerReceive.java new file mode 100644 index 000000000000..76696b204fd0 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangMultipleWorkerReceive.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.tree.expressions; + +import org.ballerinalang.model.tree.ActionNode; +import org.ballerinalang.model.tree.NodeKind; +import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; +import org.wso2.ballerinalang.compiler.tree.BLangNodeAnalyzer; +import org.wso2.ballerinalang.compiler.tree.BLangNodeTransformer; +import org.wso2.ballerinalang.compiler.tree.BLangNodeVisitor; + +import java.util.List; + +/** + * Represents multiple-receive in worker communication. + * + * @since 2201.9.0 + */ +public class BLangMultipleWorkerReceive extends BLangExpression implements ActionNode { + + private List receiveFields; + + @Override + public NodeKind getKind() { + return NodeKind.MULTIPLE_WORKER_RECEIVE; + } + + @Override + public void accept(BLangNodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public void accept(BLangNodeAnalyzer analyzer, T props) { + analyzer.visit(this, props); + } + + @Override + public R apply(BLangNodeTransformer modifier, T props) { + return modifier.transform(this, props); + } + + public List getReceiveFields() { + return receiveFields; + } + + public void setReceiveFields(List receiveFields) { + this.receiveFields = receiveFields; + } + + /** + * This static inner class represents key-value pair in a multiple worker. + * + * @since 2201.9.0 + */ + public static class BLangReceiveField { + private BLangIdentifier key; + private BLangWorkerReceive workerReceive; + + public BLangIdentifier getKey() { + return key; + } + + public void setKey(BLangIdentifier key) { + this.key = key; + } + + public BLangWorkerReceive getWorkerReceive() { + return workerReceive; + } + + public void setWorkerReceive(BLangWorkerReceive workerReceive) { + this.workerReceive = workerReceive; + } + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerAsyncSendExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerAsyncSendExpr.java index e7d886d854bd..b0c07f3d6594 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerAsyncSendExpr.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerAsyncSendExpr.java @@ -17,13 +17,7 @@ */ package org.wso2.ballerinalang.compiler.tree.expressions; -import org.ballerinalang.model.tree.IdentifierNode; import org.ballerinalang.model.tree.NodeKind; -import org.ballerinalang.model.tree.expressions.WorkerSendExpressionNode; -import org.wso2.ballerinalang.compiler.semantics.model.SymbolEnv; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import org.wso2.ballerinalang.compiler.tree.BLangNodeAnalyzer; import org.wso2.ballerinalang.compiler.tree.BLangNodeTransformer; import org.wso2.ballerinalang.compiler.tree.BLangNodeVisitor; @@ -33,33 +27,7 @@ * * @since 0.94 */ -public class BLangWorkerAsyncSendExpr extends BLangExpression implements WorkerSendExpressionNode { - - // BLangNodes - public BLangExpression expr; - public BLangIdentifier workerIdentifier; - - // Semantic Data - public BLangWorkerReceive receive; - public SymbolEnv env; - public BSymbol workerSymbol; - public BType workerType; - public BType sendType; - - @Override - public BLangExpression getExpression() { - return expr; - } - - @Override - public BLangIdentifier getWorkerName() { - return workerIdentifier; - } - - @Override - public void setWorkerName(IdentifierNode identifierNode) { - this.workerIdentifier = (BLangIdentifier) identifierNode; - } +public class BLangWorkerAsyncSendExpr extends BLangWorkerSendExpr { @Override public NodeKind getKind() { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerReceive.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerReceive.java index c84aa6bf239a..b078a5c7a5c3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerReceive.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerReceive.java @@ -20,8 +20,6 @@ import org.ballerinalang.model.tree.IdentifierNode; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.tree.statements.WorkerReceiveNode; -import org.wso2.ballerinalang.compiler.semantics.model.SymbolEnv; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import org.wso2.ballerinalang.compiler.tree.BLangNodeAnalyzer; @@ -33,16 +31,10 @@ * * @since 0.94 */ -public class BLangWorkerReceive extends BLangExpression implements WorkerReceiveNode { - - // BLangNodes - public BLangIdentifier workerIdentifier; +public class BLangWorkerReceive extends BLangWorkerSendReceiveExpr implements WorkerReceiveNode { // Semantic Data - public BLangExpression sendExpression; // TODO: #AST_CLEAN - No Transformer ? - public BSymbol workerSymbol; - public SymbolEnv env; - public BType workerType; + public BLangWorkerSendExpr send; public BType matchingSendsError; @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendExpr.java new file mode 100644 index 000000000000..ac912998943b --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendExpr.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.tree.expressions; + +import org.ballerinalang.model.tree.IdentifierNode; +import org.ballerinalang.model.tree.expressions.WorkerSendExpressionNode; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; +import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; + +/** + * Represents commons in worker async-send and sync-send. + * + * @since 2201.9.0 + */ +public abstract class BLangWorkerSendExpr extends BLangWorkerSendReceiveExpr implements WorkerSendExpressionNode { + + // BLangNodes + public BLangExpression expr; + + // Semantic Data + public BLangWorkerReceive receive; + public BType sendType; + + // For alternate receive type inference + public boolean noMessagePossible; + public BType sendTypeWithNoMsgIgnored; + + @Override + public BLangExpression getExpression() { + return expr; + } + + @Override + public BLangIdentifier getWorkerName() { + return workerIdentifier; + } + + @Override + public void setWorkerName(IdentifierNode identifierNode) { + this.workerIdentifier = (BLangIdentifier) identifierNode; + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendReceiveExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendReceiveExpr.java new file mode 100644 index 000000000000..aae3c1acd329 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSendReceiveExpr.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.tree.expressions; + +import org.wso2.ballerinalang.compiler.semantics.model.SymbolEnv; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; +import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; + +/** + * Represents commons in worker async-send, sync-send and single-receive. + * + * @since 2201.9.0 + */ +public abstract class BLangWorkerSendReceiveExpr extends BLangExpression { + + public SymbolEnv env; + public BSymbol workerSymbol; + public BType workerType; + public BLangIdentifier workerIdentifier; + private Channel channel; + + public Channel getChannel() { + return channel; + } + + public void setChannel(Channel channel) { + this.channel = channel; + } + + public record Channel(String sender, String receiver, int eventIndex) { + + public String workerPairId() { + return workerPairId(sender, receiver); + } + + public static String workerPairId(String sender, String receiver) { + return sender + "->" + receiver; + } + + public String channelId() { + return sender + "->" + receiver + ":" + eventIndex; + } + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSyncSendExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSyncSendExpr.java index 4b7f67adbd56..16a0b9e32fbc 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSyncSendExpr.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangWorkerSyncSendExpr.java @@ -17,14 +17,7 @@ */ package org.wso2.ballerinalang.compiler.tree.expressions; -import org.ballerinalang.model.tree.IdentifierNode; import org.ballerinalang.model.tree.NodeKind; -import org.ballerinalang.model.tree.expressions.ExpressionNode; -import org.ballerinalang.model.tree.expressions.WorkerSendExpressionNode; -import org.wso2.ballerinalang.compiler.semantics.model.SymbolEnv; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.tree.BLangIdentifier; import org.wso2.ballerinalang.compiler.tree.BLangNodeAnalyzer; import org.wso2.ballerinalang.compiler.tree.BLangNodeTransformer; import org.wso2.ballerinalang.compiler.tree.BLangNodeVisitor; @@ -34,18 +27,7 @@ * * @since 0.985 */ -public class BLangWorkerSyncSendExpr extends BLangExpression implements WorkerSendExpressionNode { - - // BLangNodes - public BLangIdentifier workerIdentifier; - public BLangExpression expr; - - // Semantic Data - public BLangWorkerReceive receive; - public BSymbol workerSymbol; - public SymbolEnv env; - public BType workerType; - public BType sendType; +public class BLangWorkerSyncSendExpr extends BLangWorkerSendExpr { @Override public NodeKind getKind() { @@ -67,21 +49,6 @@ public R apply(BLangNodeTransformer modifier, T props) { return modifier.transform(this, props); } - @Override - public ExpressionNode getExpression() { - return expr; - } - - @Override - public IdentifierNode getWorkerName() { - return workerIdentifier; - } - - @Override - public void setWorkerName(IdentifierNode identifierNode) { - this.workerIdentifier = (BLangIdentifier) identifierNode; - } - public String toActionString() { return this.expr + " ->> " + this.workerIdentifier; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java index 5131bd521144..a8e81b4d5fde 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java @@ -25,9 +25,9 @@ public class ProgramFileConstants { public static final int MAGIC_NUMBER = 0xBA1DA4CE; public static final short VERSION_NUMBER = 50; - public static final int BIR_VERSION_NUMBER = 71; - public static final short MIN_SUPPORTED_VERSION = 71; - public static final short MAX_SUPPORTED_VERSION = 71; + public static final int BIR_VERSION_NUMBER = 72; + public static final short MIN_SUPPORTED_VERSION = 72; + public static final short MAX_SUPPORTED_VERSION = 72; // todo move this to a proper place public static final String[] SUPPORTED_PLATFORMS = {"java17", "java11"}; diff --git a/compiler/ballerina-lang/src/main/resources/compiler.properties b/compiler/ballerina-lang/src/main/resources/compiler.properties index b5476fad74b9..4b48c5d2dec4 100644 --- a/compiler/ballerina-lang/src/main/resources/compiler.properties +++ b/compiler/ballerina-lang/src/main/resources/compiler.properties @@ -438,8 +438,14 @@ error.invalid.type.for.receive=\ error.invalid.type.for.send=\ invalid type for worker send ''{0}'', expected value:Cloneable -error.invalid.usage.of.receive.expression=\ - invalid usage of receive expression, var not allowed +error.receive.action.not.supported.with.var.type=\ + receive action not supported wth ''var'' type + +error.multiple.receive.compatible.type.not.found=\ + a type compatible with multiple receive not found in type ''{0}'' + +error.duplicate.key.in.multiple.receive=\ + invalid multiple receive: duplicate key ''{0}'' error.invalid.wait.future.expr.mapping.constructors=\ ''wait'' cannot be used with mapping constructors @@ -447,6 +453,12 @@ error.invalid.wait.future.expr.mapping.constructors=\ error.invalid.wait.future.expr.actions=\ ''wait'' cannot be used with actions +error.invalid.worker.send.no.matching.worker.receive=\ + invalid worker send, no matching worker receive + +error.invalid.worker.receive.no.matching.worker.send=\ + invalid worker receive, no matching worker send + error.invalid.send.expr=\ expected an expression, but found an action diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java index fe2e35187276..ba4b00acb172 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParser.java @@ -9978,9 +9978,9 @@ private STNode parseNamespacePrefix() { * Parse named worker declaration. *

* named-worker-decl := [annots] [transactional] worker worker-name return-type-descriptor { sequence-stmt } - * + * [on-fail-clause] * - * @param annots Annotations attached to the worker decl + * @param annots Annotations attached to the worker decl * @param qualifiers Preceding transactional keyword in a list * @return Parsed node */ @@ -9992,8 +9992,9 @@ private STNode parseNamedWorkerDeclaration(STNode annots, List qualifier STNode returnTypeDesc = parseReturnTypeDescriptor(); STNode workerBody = parseBlockNode(); endContext(); + STNode onFailClause = parseOptionalOnFailClause(); return STNodeFactory.createNamedWorkerDeclarationNode(annots, transactionalKeyword, workerKeyword, workerName, - returnTypeDesc, workerBody); + returnTypeDesc, workerBody, onFailClause); } private STNode getTransactionalKeyword(List qualifierList) { @@ -13069,7 +13070,14 @@ private STNode parseSyncSendToken() { /** * Parse receive action. *

- * receive-action := single-receive-action | multiple-receive-action + * receive-action := single-receive-action | multiple-receive-action | alternate-receive-action + *

+ * single-receive-action := <- peer-worker + *

+ * multiple-receive-action := <- { receive-field (, receive-field)* } + *

+ * alternate-receive-action := <- peer-worker (| peer-worker)* + *
* * @return Receive action */ @@ -13083,7 +13091,7 @@ private STNode parseReceiveWorkers() { switch (peek().kind) { case FUNCTION_KEYWORD: case IDENTIFIER_TOKEN: - return parsePeerWorkerName(); + return parseSingleOrAlternateReceiveWorkers(); case OPEN_BRACE_TOKEN: return parseMultipleReceiveWorkers(); default: @@ -13092,6 +13100,32 @@ private STNode parseReceiveWorkers() { } } + private STNode parseSingleOrAlternateReceiveWorkers() { + startContext(ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER); + List workers = new ArrayList<>(); + // Parse first peer worker name, that has no leading comma + STNode peerWorker = parsePeerWorkerName(); + workers.add(peerWorker); + + STToken nextToken = peek(); + if (nextToken.kind != SyntaxKind.PIPE_TOKEN) { + endContext(); + return peerWorker; + } + + // Parse the remaining peer worker names + while (nextToken.kind == SyntaxKind.PIPE_TOKEN) { + STNode pipeToken = consume(); + workers.add(pipeToken); + peerWorker = parsePeerWorkerName(); + workers.add(peerWorker); + nextToken = peek(); + } + + endContext(); + return STNodeFactory.createAlternateReceiveNode(STNodeFactory.createNodeList(workers)); + } + /** * Parse multiple worker receivers. *

@@ -13178,21 +13212,22 @@ private STNode parseReceiveField() { return STNodeFactory.createSimpleNameReferenceNode(functionKeyword); case IDENTIFIER_TOKEN: STNode identifier = parseIdentifier(ParserRuleContext.RECEIVE_FIELD_NAME); - return createQualifiedReceiveField(identifier); + return createReceiveField(identifier); default: recover(peek(), ParserRuleContext.RECEIVE_FIELD); return parseReceiveField(); } } - private STNode createQualifiedReceiveField(STNode identifier) { + private STNode createReceiveField(STNode identifier) { if (peek().kind != SyntaxKind.COLON_TOKEN) { - return identifier; + return STNodeFactory.createSimpleNameReferenceNode(identifier); } + identifier = STNodeFactory.createSimpleNameReferenceNode(identifier); STNode colon = parseColon(); STNode peerWorker = parsePeerWorkerName(); - return createQualifiedNameReferenceNode(identifier, colon, peerWorker); + return STNodeFactory.createReceiveFieldNode(identifier, colon, peerWorker); } /** diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParserErrorHandler.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParserErrorHandler.java index e2b58b8962bd..20b922cfe6bf 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParserErrorHandler.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/BallerinaParserErrorHandler.java @@ -522,7 +522,10 @@ public class BallerinaParserErrorHandler extends AbstractParserErrorHandler { { ParserRuleContext.ARG_LIST_OPEN_PAREN, ParserRuleContext.SEMICOLON }; private static final ParserRuleContext[] RECEIVE_WORKERS = - { ParserRuleContext.PEER_WORKER_NAME, ParserRuleContext.MULTI_RECEIVE_WORKERS }; + { ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER, ParserRuleContext.MULTI_RECEIVE_WORKERS }; + + private static final ParserRuleContext[] SINGLE_OR_ALTERNATE_WORKER_SEPARATOR = + { ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER_END, ParserRuleContext.PIPE }; private static final ParserRuleContext[] RECEIVE_FIELD = { ParserRuleContext.PEER_WORKER_NAME, ParserRuleContext.RECEIVE_FIELD_NAME }; @@ -1607,6 +1610,7 @@ protected boolean hasAlternativePaths(ParserRuleContext currentCtx) { case GROUPING_KEY_LIST_ELEMENT: case GROUPING_KEY_LIST_ELEMENT_END: case RESULT_CLAUSE: + case SINGLE_OR_ALTERNATE_WORKER_SEPARATOR: return true; default: return false; @@ -2075,6 +2079,8 @@ protected ParserRuleContext getShortestAlternative(ParserRuleContext currentCtx) return ParserRuleContext.TYPE_DESC_IN_TUPLE; case RESULT_CLAUSE: return ParserRuleContext.SELECT_CLAUSE; + case SINGLE_OR_ALTERNATE_WORKER_SEPARATOR: + return ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER_END; default: throw new IllegalStateException("Alternative path entry not found"); } @@ -2845,6 +2851,9 @@ private Result seekMatchInExprRelatedAlternativePaths(ParserRuleContext currentC case ERROR_CONSTRUCTOR_RHS: alternativeRules = ERROR_CONSTRUCTOR_RHS; break; + case SINGLE_OR_ALTERNATE_WORKER_SEPARATOR: + alternativeRules = SINGLE_OR_ALTERNATE_WORKER_SEPARATOR; + break; default: throw new IllegalStateException("seekMatchInExprRelatedAlternativePaths found: " + currentCtx); } @@ -3481,6 +3490,11 @@ protected ParserRuleContext getNextRule(ParserRuleContext currentCtx, int nextLo return getNextRuleForBindingPattern(); case TUPLE_MEMBERS: return ParserRuleContext.TUPLE_MEMBER; + case SINGLE_OR_ALTERNATE_WORKER: + return ParserRuleContext.PEER_WORKER_NAME; + case SINGLE_OR_ALTERNATE_WORKER_END: + endContext(); // end single-or-alternate-worker + return ParserRuleContext.EXPRESSION_RHS; default: return getNextRuleInternal(currentCtx, nextLookahead); } @@ -3504,6 +3518,8 @@ private ParserRuleContext getNextRuleInternal(ParserRuleContext currentCtx, int return ParserRuleContext.XML_ATOMIC_NAME_PATTERN; } else if (parentCtx == ParserRuleContext.MATCH_PATTERN) { return ParserRuleContext.MATCH_PATTERN_START; + } else if (parentCtx == ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER) { + return ParserRuleContext.PEER_WORKER_NAME; } return ParserRuleContext.TYPE_DESCRIPTOR; case TABLE_CONSTRUCTOR: @@ -4091,8 +4107,11 @@ private ParserRuleContext getNextRuleForKeywords(ParserRuleContext currentCtx, i case FLUSH_KEYWORD: return ParserRuleContext.OPTIONAL_PEER_WORKER; case PEER_WORKER_NAME: - if (getParentContext() == ParserRuleContext.MULTI_RECEIVE_WORKERS) { + parentCtx = getParentContext(); + if (parentCtx == ParserRuleContext.MULTI_RECEIVE_WORKERS) { return ParserRuleContext.RECEIVE_FIELD_END; + } else if (parentCtx == ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER) { + return ParserRuleContext.SINGLE_OR_ALTERNATE_WORKER_SEPARATOR; } return ParserRuleContext.EXPRESSION_RHS; case WAIT_KEYWORD: @@ -4269,6 +4288,7 @@ private void startContextIfRequired(ParserRuleContext currentCtx) { case BRACED_EXPRESSION: case CLIENT_RESOURCE_ACCESS_ACTION: case TUPLE_MEMBERS: + case SINGLE_OR_ALTERNATE_WORKER: // Contexts that expect a type case TYPE_DESC_IN_ANNOTATION_DECL: @@ -4824,10 +4844,10 @@ private ParserRuleContext getNextRuleForCloseBrace(int nextLookahead) { case CLOSE_BRACE_TOKEN: return ParserRuleContext.CLOSE_BRACE; default: - return ParserRuleContext.STATEMENT; + return ParserRuleContext.REGULAR_COMPOUND_STMT_RHS; } } else { - return ParserRuleContext.STATEMENT; + return ParserRuleContext.REGULAR_COMPOUND_STMT_RHS; } case MATCH_BODY: return ParserRuleContext.MATCH_PATTERN; diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/ParserRuleContext.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/ParserRuleContext.java index b336e39c4454..7463f0a3281b 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/ParserRuleContext.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/ParserRuleContext.java @@ -733,6 +733,9 @@ public enum ParserRuleContext { OPTIONAL_TOP_LEVEL_SEMICOLON("optional-top-level-semicolon"), TUPLE_MEMBERS("tuple-members"), TUPLE_MEMBER("tuple-member"), + SINGLE_OR_ALTERNATE_WORKER("single-or-alternate-worker"), + SINGLE_OR_ALTERNATE_WORKER_SEPARATOR("single-or-alternate-worker-separator"), + SINGLE_OR_ALTERNATE_WORKER_END("single-or-alternate-worker-end"), ; private String value; diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STAlternateReceiveNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STAlternateReceiveNode.java new file mode 100644 index 000000000000..63f3f4c92977 --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STAlternateReceiveNode.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.compiler.internal.parser.tree; + +import io.ballerina.compiler.syntax.tree.AlternateReceiveNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; + +import java.util.Collection; +import java.util.Collections; + +/** + * This is a generated internal syntax tree node. + * + * @since 2201.9.0 + */ +public class STAlternateReceiveNode extends STNode { + public final STNode workers; + + STAlternateReceiveNode( + STNode workers) { + this( + workers, + Collections.emptyList()); + } + + STAlternateReceiveNode( + STNode workers, + Collection diagnostics) { + super(SyntaxKind.ALTERNATE_RECEIVE, diagnostics); + this.workers = workers; + + addChildren( + workers); + } + + public STNode modifyWith(Collection diagnostics) { + return new STAlternateReceiveNode( + this.workers, + diagnostics); + } + + public STAlternateReceiveNode modify( + STNode workers) { + if (checkForReferenceEquality( + workers)) { + return this; + } + + return new STAlternateReceiveNode( + workers, + diagnostics); + } + + public Node createFacade(int position, NonTerminalNode parent) { + return new AlternateReceiveNode(this, position, parent); + } + + @Override + public void accept(STNodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public T apply(STNodeTransformer transformer) { + return transformer.transform(this); + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNamedWorkerDeclarationNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNamedWorkerDeclarationNode.java index 4bbe811b30f5..396e6c690da5 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNamedWorkerDeclarationNode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNamedWorkerDeclarationNode.java @@ -37,6 +37,7 @@ public class STNamedWorkerDeclarationNode extends STNode { public final STNode workerName; public final STNode returnTypeDesc; public final STNode workerBody; + public final STNode onFailClause; STNamedWorkerDeclarationNode( STNode annotations, @@ -44,7 +45,8 @@ public class STNamedWorkerDeclarationNode extends STNode { STNode workerKeyword, STNode workerName, STNode returnTypeDesc, - STNode workerBody) { + STNode workerBody, + STNode onFailClause) { this( annotations, transactionalKeyword, @@ -52,6 +54,7 @@ public class STNamedWorkerDeclarationNode extends STNode { workerName, returnTypeDesc, workerBody, + onFailClause, Collections.emptyList()); } @@ -62,6 +65,7 @@ public class STNamedWorkerDeclarationNode extends STNode { STNode workerName, STNode returnTypeDesc, STNode workerBody, + STNode onFailClause, Collection diagnostics) { super(SyntaxKind.NAMED_WORKER_DECLARATION, diagnostics); this.annotations = annotations; @@ -70,6 +74,7 @@ public class STNamedWorkerDeclarationNode extends STNode { this.workerName = workerName; this.returnTypeDesc = returnTypeDesc; this.workerBody = workerBody; + this.onFailClause = onFailClause; addChildren( annotations, @@ -77,7 +82,8 @@ public class STNamedWorkerDeclarationNode extends STNode { workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } public STNode modifyWith(Collection diagnostics) { @@ -88,6 +94,7 @@ public STNode modifyWith(Collection diagnostics) { this.workerName, this.returnTypeDesc, this.workerBody, + this.onFailClause, diagnostics); } @@ -97,14 +104,16 @@ public STNamedWorkerDeclarationNode modify( STNode workerKeyword, STNode workerName, STNode returnTypeDesc, - STNode workerBody) { + STNode workerBody, + STNode onFailClause) { if (checkForReferenceEquality( annotations, transactionalKeyword, workerKeyword, workerName, returnTypeDesc, - workerBody)) { + workerBody, + onFailClause)) { return this; } @@ -115,6 +124,7 @@ public STNamedWorkerDeclarationNode modify( workerName, returnTypeDesc, workerBody, + onFailClause, diagnostics); } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeFactory.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeFactory.java index 7cf23088dc3f..8c058ad27a02 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeFactory.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeFactory.java @@ -979,7 +979,8 @@ public static STNode createNamedWorkerDeclarationNode( STNode workerKeyword, STNode workerName, STNode returnTypeDesc, - STNode workerBody) { + STNode workerBody, + STNode onFailClause) { return new STNamedWorkerDeclarationNode( annotations, @@ -987,7 +988,8 @@ public static STNode createNamedWorkerDeclarationNode( workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } public static STNode createNamedWorkerDeclarator( @@ -1824,6 +1826,13 @@ public static STNode createReceiveFieldsNode( closeBrace); } + public static STNode createAlternateReceiveNode( + STNode workers) { + + return new STAlternateReceiveNode( + workers); + } + public static STNode createRestDescriptorNode( STNode typeDescriptor, STNode ellipsisToken) { @@ -2710,5 +2719,16 @@ public static STNode createMemberTypeDescriptorNode( annotations, typeDescriptor); } + + public static STNode createReceiveFieldNode( + STNode fieldName, + STNode colon, + STNode peerWorker) { + + return new STReceiveFieldNode( + fieldName, + colon, + peerWorker); + } } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeTransformer.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeTransformer.java index 14b60714218b..47e6111e1b2e 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeTransformer.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeTransformer.java @@ -621,6 +621,10 @@ public T transform(STReceiveFieldsNode receiveFieldsNode) { return transformSyntaxNode(receiveFieldsNode); } + public T transform(STAlternateReceiveNode alternateReceiveNode) { + return transformSyntaxNode(alternateReceiveNode); + } + public T transform(STRestDescriptorNode restDescriptorNode) { return transformSyntaxNode(restDescriptorNode); } @@ -937,6 +941,10 @@ public T transform(STMemberTypeDescriptorNode memberTypeDescriptorNode) { return transformSyntaxNode(memberTypeDescriptorNode); } + public T transform(STReceiveFieldNode receiveFieldNode) { + return transformSyntaxNode(receiveFieldNode); + } + // Tokens public T transform(STToken token) { diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeVisitor.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeVisitor.java index e7b961a89e50..9f9794d78313 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeVisitor.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STNodeVisitor.java @@ -621,6 +621,10 @@ public void visit(STReceiveFieldsNode receiveFieldsNode) { visitSyntaxNode(receiveFieldsNode); } + public void visit(STAlternateReceiveNode alternateReceiveNode) { + visitSyntaxNode(alternateReceiveNode); + } + public void visit(STRestDescriptorNode restDescriptorNode) { visitSyntaxNode(restDescriptorNode); } @@ -937,6 +941,10 @@ public void visit(STMemberTypeDescriptorNode memberTypeDescriptorNode) { visitSyntaxNode(memberTypeDescriptorNode); } + public void visit(STReceiveFieldNode receiveFieldNode) { + visitSyntaxNode(receiveFieldNode); + } + // STNodeList public void visit(STNodeList nodeList) { visitChildren(nodeList); diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STReceiveFieldNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STReceiveFieldNode.java new file mode 100644 index 000000000000..5413a31dc8df --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STReceiveFieldNode.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.compiler.internal.parser.tree; + +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NonTerminalNode; +import io.ballerina.compiler.syntax.tree.ReceiveFieldNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; + +import java.util.Collection; +import java.util.Collections; + +/** + * This is a generated internal syntax tree node. + * + * @since 2201.9.0 + */ +public class STReceiveFieldNode extends STNode { + public final STNode fieldName; + public final STNode colon; + public final STNode peerWorker; + + STReceiveFieldNode( + STNode fieldName, + STNode colon, + STNode peerWorker) { + this( + fieldName, + colon, + peerWorker, + Collections.emptyList()); + } + + STReceiveFieldNode( + STNode fieldName, + STNode colon, + STNode peerWorker, + Collection diagnostics) { + super(SyntaxKind.RECEIVE_FIELD, diagnostics); + this.fieldName = fieldName; + this.colon = colon; + this.peerWorker = peerWorker; + + addChildren( + fieldName, + colon, + peerWorker); + } + + public STNode modifyWith(Collection diagnostics) { + return new STReceiveFieldNode( + this.fieldName, + this.colon, + this.peerWorker, + diagnostics); + } + + public STReceiveFieldNode modify( + STNode fieldName, + STNode colon, + STNode peerWorker) { + if (checkForReferenceEquality( + fieldName, + colon, + peerWorker)) { + return this; + } + + return new STReceiveFieldNode( + fieldName, + colon, + peerWorker, + diagnostics); + } + + public Node createFacade(int position, NonTerminalNode parent) { + return new ReceiveFieldNode(this, position, parent); + } + + @Override + public void accept(STNodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public T apply(STNodeTransformer transformer) { + return transformer.transform(this); + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STTreeModifier.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STTreeModifier.java index 270b93ed515b..b64f2180dd81 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STTreeModifier.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/internal/parser/tree/STTreeModifier.java @@ -1039,13 +1039,15 @@ public STNamedWorkerDeclarationNode transform( STNode workerName = modifyNode(namedWorkerDeclarationNode.workerName); STNode returnTypeDesc = modifyNode(namedWorkerDeclarationNode.returnTypeDesc); STNode workerBody = modifyNode(namedWorkerDeclarationNode.workerBody); + STNode onFailClause = modifyNode(namedWorkerDeclarationNode.onFailClause); return namedWorkerDeclarationNode.modify( annotations, transactionalKeyword, workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } @Override @@ -1953,6 +1955,14 @@ public STReceiveFieldsNode transform( closeBrace); } + @Override + public STAlternateReceiveNode transform( + STAlternateReceiveNode alternateReceiveNode) { + STNode workers = modifyNode(alternateReceiveNode.workers); + return alternateReceiveNode.modify( + workers); + } + @Override public STRestDescriptorNode transform( STRestDescriptorNode restDescriptorNode) { @@ -2915,6 +2925,18 @@ public STMemberTypeDescriptorNode transform( typeDescriptor); } + @Override + public STReceiveFieldNode transform( + STReceiveFieldNode receiveFieldNode) { + STNode fieldName = modifyNode(receiveFieldNode.fieldName); + STNode colon = modifyNode(receiveFieldNode.colon); + STNode peerWorker = modifyNode(receiveFieldNode.peerWorker); + return receiveFieldNode.modify( + fieldName, + colon, + peerWorker); + } + // Tokens public STToken transform(STToken token) { diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/AlternateReceiveNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/AlternateReceiveNode.java new file mode 100644 index 000000000000..c8f94dad42cd --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/AlternateReceiveNode.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.compiler.syntax.tree; + +import io.ballerina.compiler.internal.parser.tree.STNode; + +import java.util.Objects; + +/** + * This is a generated syntax tree node. + * + * @since 2201.9.0 + */ +public class AlternateReceiveNode extends NonTerminalNode { + + public AlternateReceiveNode(STNode internalNode, int position, NonTerminalNode parent) { + super(internalNode, position, parent); + } + + public SeparatedNodeList workers() { + return new SeparatedNodeList<>(childInBucket(0)); + } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public T apply(NodeTransformer visitor) { + return visitor.transform(this); + } + + @Override + protected String[] childNames() { + return new String[]{ + "workers"}; + } + + public AlternateReceiveNode modify( + SeparatedNodeList workers) { + if (checkForReferenceEquality( + workers.underlyingListNode())) { + return this; + } + + return NodeFactory.createAlternateReceiveNode( + workers); + } + + public AlternateReceiveNodeModifier modify() { + return new AlternateReceiveNodeModifier(this); + } + + /** + * This is a generated tree node modifier utility. + * + * @since 2201.9.0 + */ + public static class AlternateReceiveNodeModifier { + private final AlternateReceiveNode oldNode; + private SeparatedNodeList workers; + + public AlternateReceiveNodeModifier(AlternateReceiveNode oldNode) { + this.oldNode = oldNode; + this.workers = oldNode.workers(); + } + + public AlternateReceiveNodeModifier withWorkers( + SeparatedNodeList workers) { + Objects.requireNonNull(workers, "workers must not be null"); + this.workers = workers; + return this; + } + + public AlternateReceiveNode apply() { + return oldNode.modify( + workers); + } + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NamedWorkerDeclarationNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NamedWorkerDeclarationNode.java index ecf4af702eec..89e3dc9a03dc 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NamedWorkerDeclarationNode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NamedWorkerDeclarationNode.java @@ -57,6 +57,10 @@ public BlockStatementNode workerBody() { return childInBucket(5); } + public Optional onFailClause() { + return optionalChildInBucket(6); + } + @Override public void accept(NodeVisitor visitor) { visitor.visit(this); @@ -75,7 +79,8 @@ protected String[] childNames() { "workerKeyword", "workerName", "returnTypeDesc", - "workerBody"}; + "workerBody", + "onFailClause"}; } public NamedWorkerDeclarationNode modify( @@ -84,14 +89,16 @@ public NamedWorkerDeclarationNode modify( Token workerKeyword, IdentifierToken workerName, Node returnTypeDesc, - BlockStatementNode workerBody) { + BlockStatementNode workerBody, + OnFailClauseNode onFailClause) { if (checkForReferenceEquality( annotations.underlyingListNode(), transactionalKeyword, workerKeyword, workerName, returnTypeDesc, - workerBody)) { + workerBody, + onFailClause)) { return this; } @@ -101,7 +108,8 @@ public NamedWorkerDeclarationNode modify( workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } public NamedWorkerDeclarationNodeModifier modify() { @@ -121,6 +129,7 @@ public static class NamedWorkerDeclarationNodeModifier { private IdentifierToken workerName; private Node returnTypeDesc; private BlockStatementNode workerBody; + private OnFailClauseNode onFailClause; public NamedWorkerDeclarationNodeModifier(NamedWorkerDeclarationNode oldNode) { this.oldNode = oldNode; @@ -130,6 +139,7 @@ public NamedWorkerDeclarationNodeModifier(NamedWorkerDeclarationNode oldNode) { this.workerName = oldNode.workerName(); this.returnTypeDesc = oldNode.returnTypeDesc().orElse(null); this.workerBody = oldNode.workerBody(); + this.onFailClause = oldNode.onFailClause().orElse(null); } public NamedWorkerDeclarationNodeModifier withAnnotations( @@ -172,6 +182,12 @@ public NamedWorkerDeclarationNodeModifier withWorkerBody( return this; } + public NamedWorkerDeclarationNodeModifier withOnFailClause( + OnFailClauseNode onFailClause) { + this.onFailClause = onFailClause; + return this; + } + public NamedWorkerDeclarationNode apply() { return oldNode.modify( annotations, @@ -179,7 +195,8 @@ public NamedWorkerDeclarationNode apply() { workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } } } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeFactory.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeFactory.java index 60c1b9632f4a..33db78359e09 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeFactory.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeFactory.java @@ -1270,6 +1270,11 @@ public static FunctionBodyBlockNode createFunctionBodyBlockNode( return stFunctionBodyBlockNode.createUnlinkedFacade(); } + /** + * @deprecated Use {@link #createNamedWorkerDeclarationNode(NodeList, Token, Token, IdentifierToken, Node, + * BlockStatementNode, OnFailClauseNode)} instead. + */ + @Deprecated public static NamedWorkerDeclarationNode createNamedWorkerDeclarationNode( NodeList annotations, Token transactionalKeyword, @@ -1277,6 +1282,24 @@ public static NamedWorkerDeclarationNode createNamedWorkerDeclarationNode( IdentifierToken workerName, Node returnTypeDesc, BlockStatementNode workerBody) { + return createNamedWorkerDeclarationNode( + annotations, + transactionalKeyword, + workerKeyword, + workerName, + returnTypeDesc, + workerBody, + null); + } + + public static NamedWorkerDeclarationNode createNamedWorkerDeclarationNode( + NodeList annotations, + Token transactionalKeyword, + Token workerKeyword, + IdentifierToken workerName, + Node returnTypeDesc, + BlockStatementNode workerBody, + OnFailClauseNode onFailClause) { Objects.requireNonNull(annotations, "annotations must not be null"); Objects.requireNonNull(workerKeyword, "workerKeyword must not be null"); Objects.requireNonNull(workerName, "workerName must not be null"); @@ -1288,7 +1311,8 @@ public static NamedWorkerDeclarationNode createNamedWorkerDeclarationNode( workerKeyword.internalNode(), workerName.internalNode(), getOptionalSTNode(returnTypeDesc), - workerBody.internalNode()); + workerBody.internalNode(), + getOptionalSTNode(onFailClause)); return stNamedWorkerDeclarationNode.createUnlinkedFacade(); } @@ -2393,7 +2417,7 @@ public static ReceiveActionNode createReceiveActionNode( public static ReceiveFieldsNode createReceiveFieldsNode( Token openBrace, - SeparatedNodeList receiveFields, + SeparatedNodeList receiveFields, Token closeBrace) { Objects.requireNonNull(openBrace, "openBrace must not be null"); Objects.requireNonNull(receiveFields, "receiveFields must not be null"); @@ -2406,6 +2430,15 @@ public static ReceiveFieldsNode createReceiveFieldsNode( return stReceiveFieldsNode.createUnlinkedFacade(); } + public static AlternateReceiveNode createAlternateReceiveNode( + SeparatedNodeList workers) { + Objects.requireNonNull(workers, "workers must not be null"); + + STNode stAlternateReceiveNode = STNodeFactory.createAlternateReceiveNode( + workers.underlyingListNode().internalNode()); + return stAlternateReceiveNode.createUnlinkedFacade(); + } + public static RestDescriptorNode createRestDescriptorNode( TypeDescriptorNode typeDescriptor, Token ellipsisToken) { @@ -3589,5 +3622,20 @@ public static MemberTypeDescriptorNode createMemberTypeDescriptorNode( typeDescriptor.internalNode()); return stMemberTypeDescriptorNode.createUnlinkedFacade(); } + + public static ReceiveFieldNode createReceiveFieldNode( + SimpleNameReferenceNode fieldName, + Token colon, + SimpleNameReferenceNode peerWorker) { + Objects.requireNonNull(fieldName, "fieldName must not be null"); + Objects.requireNonNull(colon, "colon must not be null"); + Objects.requireNonNull(peerWorker, "peerWorker must not be null"); + + STNode stReceiveFieldNode = STNodeFactory.createReceiveFieldNode( + fieldName.internalNode(), + colon.internalNode(), + peerWorker.internalNode()); + return stReceiveFieldNode.createUnlinkedFacade(); + } } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java index ec21175b1833..6a07516d87e8 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java @@ -632,6 +632,10 @@ public T transform(ReceiveFieldsNode receiveFieldsNode) { return transformSyntaxNode(receiveFieldsNode); } + public T transform(AlternateReceiveNode alternateReceiveNode) { + return transformSyntaxNode(alternateReceiveNode); + } + public T transform(RestDescriptorNode restDescriptorNode) { return transformSyntaxNode(restDescriptorNode); } @@ -948,6 +952,10 @@ public T transform(MemberTypeDescriptorNode memberTypeDescriptorNode) { return transformSyntaxNode(memberTypeDescriptorNode); } + public T transform(ReceiveFieldNode receiveFieldNode) { + return transformSyntaxNode(receiveFieldNode); + } + // Tokens public T transform(Token token) { diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java index 13544361784d..1fe6ddfa596e 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java @@ -631,6 +631,10 @@ public void visit(ReceiveFieldsNode receiveFieldsNode) { visitSyntaxNode(receiveFieldsNode); } + public void visit(AlternateReceiveNode alternateReceiveNode) { + visitSyntaxNode(alternateReceiveNode); + } + public void visit(RestDescriptorNode restDescriptorNode) { visitSyntaxNode(restDescriptorNode); } @@ -947,6 +951,10 @@ public void visit(MemberTypeDescriptorNode memberTypeDescriptorNode) { visitSyntaxNode(memberTypeDescriptorNode); } + public void visit(ReceiveFieldNode receiveFieldNode) { + visitSyntaxNode(receiveFieldNode); + } + // Tokens public void visit(Token token) { diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldNode.java new file mode 100644 index 000000000000..88b77bf999dd --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldNode.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.compiler.syntax.tree; + +import io.ballerina.compiler.internal.parser.tree.STNode; + +import java.util.Objects; + +/** + * This is a generated syntax tree node. + * + * @since 2201.9.0 + */ +public class ReceiveFieldNode extends NonTerminalNode { + + public ReceiveFieldNode(STNode internalNode, int position, NonTerminalNode parent) { + super(internalNode, position, parent); + } + + public SimpleNameReferenceNode fieldName() { + return childInBucket(0); + } + + public Token colon() { + return childInBucket(1); + } + + public SimpleNameReferenceNode peerWorker() { + return childInBucket(2); + } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public T apply(NodeTransformer visitor) { + return visitor.transform(this); + } + + @Override + protected String[] childNames() { + return new String[]{ + "fieldName", + "colon", + "peerWorker"}; + } + + public ReceiveFieldNode modify( + SimpleNameReferenceNode fieldName, + Token colon, + SimpleNameReferenceNode peerWorker) { + if (checkForReferenceEquality( + fieldName, + colon, + peerWorker)) { + return this; + } + + return NodeFactory.createReceiveFieldNode( + fieldName, + colon, + peerWorker); + } + + public ReceiveFieldNodeModifier modify() { + return new ReceiveFieldNodeModifier(this); + } + + /** + * This is a generated tree node modifier utility. + * + * @since 2201.9.0 + */ + public static class ReceiveFieldNodeModifier { + private final ReceiveFieldNode oldNode; + private SimpleNameReferenceNode fieldName; + private Token colon; + private SimpleNameReferenceNode peerWorker; + + public ReceiveFieldNodeModifier(ReceiveFieldNode oldNode) { + this.oldNode = oldNode; + this.fieldName = oldNode.fieldName(); + this.colon = oldNode.colon(); + this.peerWorker = oldNode.peerWorker(); + } + + public ReceiveFieldNodeModifier withFieldName( + SimpleNameReferenceNode fieldName) { + Objects.requireNonNull(fieldName, "fieldName must not be null"); + this.fieldName = fieldName; + return this; + } + + public ReceiveFieldNodeModifier withColon( + Token colon) { + Objects.requireNonNull(colon, "colon must not be null"); + this.colon = colon; + return this; + } + + public ReceiveFieldNodeModifier withPeerWorker( + SimpleNameReferenceNode peerWorker) { + Objects.requireNonNull(peerWorker, "peerWorker must not be null"); + this.peerWorker = peerWorker; + return this; + } + + public ReceiveFieldNode apply() { + return oldNode.modify( + fieldName, + colon, + peerWorker); + } + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldsNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldsNode.java index 7e2c646968f9..885d8ecfa569 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldsNode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/ReceiveFieldsNode.java @@ -36,7 +36,7 @@ public Token openBrace() { return childInBucket(0); } - public SeparatedNodeList receiveFields() { + public SeparatedNodeList receiveFields() { return new SeparatedNodeList<>(childInBucket(1)); } @@ -64,7 +64,7 @@ protected String[] childNames() { public ReceiveFieldsNode modify( Token openBrace, - SeparatedNodeList receiveFields, + SeparatedNodeList receiveFields, Token closeBrace) { if (checkForReferenceEquality( openBrace, @@ -91,7 +91,7 @@ public ReceiveFieldsNodeModifier modify() { public static class ReceiveFieldsNodeModifier { private final ReceiveFieldsNode oldNode; private Token openBrace; - private SeparatedNodeList receiveFields; + private SeparatedNodeList receiveFields; private Token closeBrace; public ReceiveFieldsNodeModifier(ReceiveFieldsNode oldNode) { @@ -109,7 +109,7 @@ public ReceiveFieldsNodeModifier withOpenBrace( } public ReceiveFieldsNodeModifier withReceiveFields( - SeparatedNodeList receiveFields) { + SeparatedNodeList receiveFields) { Objects.requireNonNull(receiveFields, "receiveFields must not be null"); this.receiveFields = receiveFields; return this; diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/SyntaxKind.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/SyntaxKind.java index a800be0930c3..f74b79a91bb3 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/SyntaxKind.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/SyntaxKind.java @@ -484,7 +484,8 @@ public enum SyntaxKind { GROUPING_KEY_VAR_NAME(3092), GROUP_BY_CLAUSE(3093), COLLECT_CLAUSE(3094), - + ALTERNATE_RECEIVE(3095), + RECEIVE_FIELD(3096), // XML XML_ELEMENT(4000), diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/TreeModifier.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/TreeModifier.java index ea5ea39ca3d0..865d3bdb6aea 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/TreeModifier.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/TreeModifier.java @@ -1335,13 +1335,16 @@ public NamedWorkerDeclarationNode transform( modifyNode(namedWorkerDeclarationNode.returnTypeDesc().orElse(null)); BlockStatementNode workerBody = modifyNode(namedWorkerDeclarationNode.workerBody()); + OnFailClauseNode onFailClause = + modifyNode(namedWorkerDeclarationNode.onFailClause().orElse(null)); return namedWorkerDeclarationNode.modify( annotations, transactionalKeyword, workerKeyword, workerName, returnTypeDesc, - workerBody); + workerBody, + onFailClause); } @Override @@ -2461,7 +2464,7 @@ public ReceiveFieldsNode transform( ReceiveFieldsNode receiveFieldsNode) { Token openBrace = modifyToken(receiveFieldsNode.openBrace()); - SeparatedNodeList receiveFields = + SeparatedNodeList receiveFields = modifySeparatedNodeList(receiveFieldsNode.receiveFields()); Token closeBrace = modifyToken(receiveFieldsNode.closeBrace()); @@ -2471,6 +2474,15 @@ public ReceiveFieldsNode transform( closeBrace); } + @Override + public AlternateReceiveNode transform( + AlternateReceiveNode alternateReceiveNode) { + SeparatedNodeList workers = + modifySeparatedNodeList(alternateReceiveNode.workers()); + return alternateReceiveNode.modify( + workers); + } + @Override public RestDescriptorNode transform( RestDescriptorNode restDescriptorNode) { @@ -3675,6 +3687,21 @@ public MemberTypeDescriptorNode transform( typeDescriptor); } + @Override + public ReceiveFieldNode transform( + ReceiveFieldNode receiveFieldNode) { + SimpleNameReferenceNode fieldName = + modifyNode(receiveFieldNode.fieldName()); + Token colon = + modifyToken(receiveFieldNode.colon()); + SimpleNameReferenceNode peerWorker = + modifyNode(receiveFieldNode.peerWorker()); + return receiveFieldNode.modify( + fieldName, + colon, + peerWorker); + } + // Tokens @Override diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java index 611a9b071e3c..6dc3df46bfd5 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java @@ -633,6 +633,10 @@ private static SyntaxKind getNodeKind(String kind) { return SyntaxKind.COMPUTED_RESOURCE_ACCESS_SEGMENT; case "RESOURCE_ACCESS_REST_SEGMENT": return SyntaxKind.RESOURCE_ACCESS_REST_SEGMENT; + case "ALTERNATE_RECEIVE": + return SyntaxKind.ALTERNATE_RECEIVE; + case "RECEIVE_FIELD": + return SyntaxKind.RECEIVE_FIELD; // Trivia case "EOF_TOKEN": diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/ReceiveActionTest.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/ReceiveActionTest.java index d13102ea25e2..446896beb7c9 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/ReceiveActionTest.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/actions/ReceiveActionTest.java @@ -33,6 +33,11 @@ public void testBasicReceiveAction() { testFile("receive-action/receive_action_source_01.bal", "receive-action/receive_action_assert_01.json"); } + @Test + public void testAlternateReceiveAction() { + testFile("receive-action/receive_action_source_04.bal", "receive-action/receive_action_assert_04.json"); + } + // Recover tests @Test @@ -44,4 +49,9 @@ public void testRecoveryInReceiveAction() { public void testInvalidNodeInReceiveAction() { testFile("receive-action/receive_action_source_03.bal", "receive-action/receive_action_assert_03.json"); } + + @Test + public void testAlternateReceiveActionRecovery() { + testFile("receive-action/receive_action_source_05.bal", "receive-action/receive_action_assert_05.json"); + } } diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/misc/WorkerDeclTest.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/misc/WorkerDeclTest.java index 0be245411909..30f63c6b6db0 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/misc/WorkerDeclTest.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/syntax/misc/WorkerDeclTest.java @@ -31,6 +31,11 @@ public void testBasicWorkerDecl() { testFile("worker-decl/worker_decl_source_01.bal", "worker-decl/worker_decl_assert_01.json"); } + @Test + public void testWorkerDeclWithOnFailClause() { + testFile("worker-decl/worker_decl_source_06.bal", "worker-decl/worker_decl_assert_06.json"); + } + // Recovery tests @Test @@ -53,5 +58,9 @@ public void testMissingReturnTypeDesc() { public void testInvalidUsageOfNamedWorkers() { testFile("worker-decl/worker_decl_source_05.bal", "worker-decl/worker_decl_assert_05.json"); } - + + @Test + public void testWorkerWithOnFailClauseRecovery() { + testFile("worker-decl/worker_decl_source_07.bal", "worker-decl/worker_decl_assert_07.json"); + } } diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_01.json b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_01.json index e3df09a6bc30..0614217f73ce 100644 --- a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_01.json +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_01.json @@ -265,8 +265,13 @@ "kind": "LIST", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a" + } + ] } ] }, @@ -343,8 +348,13 @@ "kind": "LIST", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a" + } + ] }, { "kind": "COMMA_TOKEN", @@ -356,11 +366,16 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "b" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b" + } + ] }, { "kind": "COLON_TOKEN" @@ -386,8 +401,13 @@ ] }, { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] } ] }, @@ -457,11 +477,16 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "b" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b" + } + ] }, { "kind": "COLON_TOKEN" @@ -486,8 +511,13 @@ ] }, { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] } ] }, diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_02.json b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_02.json index e5e65cac1548..d3fa057f8f2f 100644 --- a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_02.json +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_02.json @@ -312,12 +312,17 @@ "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a", - "trailingMinutiae": [ + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ { - "kind": "WHITESPACE_MINUTIAE", - "value": " " + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] } ] }, @@ -330,11 +335,16 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "b" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b" + } + ] }, { "kind": "COLON_TOKEN" @@ -360,8 +370,13 @@ ] }, { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] } ] }, @@ -442,8 +457,13 @@ "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a" + } + ] }, { "kind": "COMMA_TOKEN", @@ -455,15 +475,21 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "isMissing": true, + "kind": "SIMPLE_NAME_REFERENCE", "hasDiagnostics": true, - "diagnostics": [ - "ERROR_MISSING_IDENTIFIER" + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_IDENTIFIER" + ] + } ] }, { @@ -490,12 +516,17 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] }, { "kind": "COLON_TOKEN" @@ -587,11 +618,16 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "b" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b" + } + ] }, { "kind": "COLON_TOKEN" @@ -616,8 +652,13 @@ ] }, { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] } ] }, diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_03.json b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_03.json index 730063a807c6..9c8c50d7b9a7 100644 --- a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_03.json +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_03.json @@ -120,12 +120,17 @@ "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a", - "trailingMinutiae": [ + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ { - "kind": "WHITESPACE_MINUTIAE", - "value": " " + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] } ] }, @@ -158,11 +163,16 @@ ] }, { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "b" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b" + } + ] }, { "kind": "COLON_TOKEN" @@ -188,8 +198,13 @@ ] }, { - "kind": "IDENTIFIER_TOKEN", - "value": "d" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d" + } + ] } ] }, diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_04.json b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_04.json new file mode 100644 index 000000000000..9743ea8f002f --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_04.json @@ -0,0 +1,992 @@ +{ + "kind": "FUNCTION_DEFINITION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATOR", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "E", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "FOREACH_STATEMENT", + "children": [ + { + "kind": "FOREACH_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "err", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "IN_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "B", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "ACTION_STATEMENT", + "children": [ + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "FUNCTION_KEYWORD" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ACTION_STATEMENT", + "children": [ + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "B", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "C", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "D" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ACTION_STATEMENT", + "children": [ + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "D" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ASSIGNMENT_STATEMENT", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ASSIGNMENT_STATEMENT", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "B", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "C" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "B", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "C" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] +} diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_05.json b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_05.json new file mode 100644 index 000000000000..4f7122368510 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_assert_05.json @@ -0,0 +1,508 @@ +{ + "kind": "FUNCTION_DEFINITION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "hasDiagnostics": true, + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "ACTION_STATEMENT", + "hasDiagnostics": true, + "children": [ + { + "kind": "RECEIVE_ACTION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "hasDiagnostics": true, + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_IDENTIFIER" + ], + "leadingMinutiae": [ + { + "kind": "INVALID_NODE_MINUTIAE", + "invalidNode": { + "kind": "INVALID_TOKEN_MINUTIAE_NODE", + "hasDiagnostics": true, + "children": [ + { + "kind": "IMPORT_KEYWORD", + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_INVALID_TOKEN" + ] + } + ] + } + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "C", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "D", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_SEMICOLON_TOKEN" + ] + } + ] + }, + { + "kind": "ASSIGNMENT_STATEMENT", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "B", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "hasDiagnostics": true, + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_IDENTIFIER" + ], + "leadingMinutiae": [ + { + "kind": "INVALID_NODE_MINUTIAE", + "invalidNode": { + "kind": "INVALID_TOKEN_MINUTIAE_NODE", + "hasDiagnostics": true, + "children": [ + { + "kind": "IMPORT_KEYWORD", + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_INVALID_TOKEN" + ] + } + ] + } + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "D" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "ASSIGNMENT_STATEMENT", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RECEIVE_ACTION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LEFT_ARROW_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ALTERNATE_RECEIVE", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "A", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "PIPE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "hasDiagnostics": true, + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_IDENTIFIER" + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_SEMICOLON_TOKEN" + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] +} diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_04.bal b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_04.bal new file mode 100644 index 000000000000..88bd3fd56d20 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_04.bal @@ -0,0 +1,13 @@ +function foo() { + worker E { + foreach var err in <- A | B { + } + } + + <- function | function; + <- A | B | C | D; + <- A | function | function | D; + a = <- function | A; + a = <- A | B | C; + int b = <- B | C; +} diff --git a/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_05.bal b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_05.bal new file mode 100644 index 000000000000..c4aa9317c6d0 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/actions/receive-action/receive_action_source_05.bal @@ -0,0 +1,5 @@ +function foo() { + <- A | import| C | D + a = <- A | B | import| D; + a = <- A | +} diff --git a/compiler/ballerina-parser/src/test/resources/misc/completion/completion_assert_08.json b/compiler/ballerina-parser/src/test/resources/misc/completion/completion_assert_08.json index c9b798d8f24e..d92545238d4f 100644 --- a/compiler/ballerina-parser/src/test/resources/misc/completion/completion_assert_08.json +++ b/compiler/ballerina-parser/src/test/resources/misc/completion/completion_assert_08.json @@ -1189,15 +1189,32 @@ "hasDiagnostics": true, "children": [ { - "kind": "QUALIFIED_NAME_REFERENCE", + "kind": "RECEIVE_FIELD", "hasDiagnostics": true, "children": [ { - "kind": "IDENTIFIER_TOKEN", - "value": "a" + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "a", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] }, { - "kind": "COLON_TOKEN" + "kind": "COLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] }, { "kind": "SIMPLE_NAME_REFERENCE", diff --git a/compiler/ballerina-parser/src/test/resources/misc/completion/completion_source_08.bal b/compiler/ballerina-parser/src/test/resources/misc/completion/completion_source_08.bal index acf6362f3bd0..f738f85855ef 100644 --- a/compiler/ballerina-parser/src/test/resources/misc/completion/completion_source_08.bal +++ b/compiler/ballerina-parser/src/test/resources/misc/completion/completion_source_08.bal @@ -26,7 +26,7 @@ function foo6() { function foo7() { <- { -a:} + a: } } function foo8() { diff --git a/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_06.json b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_06.json new file mode 100644 index 000000000000..a489150893d5 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_06.json @@ -0,0 +1,1332 @@ +{ + "kind": "MODULE_PART", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "FUNCTION_DEFINITION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATOR", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w1", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "children": [ + { + "kind": "ON_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "c", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w2", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w3", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "c", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "children": [ + { + "kind": "ON_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "VAR_TYPE_DESC", + "children": [ + { + "kind": "VAR_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "e", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w4", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "c", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "children": [ + { + "kind": "ON_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "ERROR_TYPE_DESC", + "children": [ + { + "kind": "ERROR_KEYWORD" + }, + { + "kind": "TYPE_PARAMETER", + "children": [ + { + "kind": "LT_TOKEN" + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "E" + } + ] + }, + { + "kind": "GT_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "e", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "RETURN_STATEMENT", + "children": [ + { + "kind": "RETURN_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "ERROR_CONSTRUCTOR", + "children": [ + { + "kind": "ERROR_KEYWORD" + }, + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [ + { + "kind": "POSITIONAL_ARG", + "children": [ + { + "kind": "STRING_LITERAL", + "children": [ + { + "kind": "STRING_LITERAL_TOKEN", + "value": "error!" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_PAREN_TOKEN" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATION", + "children": [ + { + "kind": "LIST", + "children": [ + { + "kind": "ANNOTATION", + "children": [ + { + "kind": "AT_TOKEN", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "someAnnot", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "MAPPING_CONSTRUCTOR", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w5", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "RETURN_TYPE_DESCRIPTOR", + "children": [ + { + "kind": "RETURNS_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "STRING_TYPE_DESC", + "children": [ + { + "kind": "STRING_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "c", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "3" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "d", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "4" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "EOF_TOKEN" + } + ] +} diff --git a/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_07.json b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_07.json new file mode 100644 index 000000000000..8f6984a58f42 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_assert_07.json @@ -0,0 +1,1130 @@ +{ + "kind": "MODULE_PART", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "FUNCTION_DEFINITION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "foo" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "hasDiagnostics": true, + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATOR", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "NAMED_WORKER_DECLARATION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w1", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "hasDiagnostics": true, + "children": [ + { + "kind": "ON_KEYWORD", + "hasDiagnostics": true, + "leadingMinutiae": [ + { + "kind": "INVALID_NODE_MINUTIAE", + "invalidNode": { + "kind": "INVALID_TOKEN_MINUTIAE_NODE", + "hasDiagnostics": true, + "children": [ + { + "kind": "PERCENT_TOKEN", + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_INVALID_TOKEN" + ] + } + ] + } + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "hasDiagnostics": true, + "leadingMinutiae": [ + { + "kind": "INVALID_NODE_MINUTIAE", + "invalidNode": { + "kind": "INVALID_TOKEN_MINUTIAE_NODE", + "hasDiagnostics": true, + "children": [ + { + "kind": "PERCENT_TOKEN", + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_INVALID_TOKEN" + ] + } + ] + } + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "FUNCTION_DEFINITION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "bar" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "hasDiagnostics": true, + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATOR", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "NAMED_WORKER_DECLARATION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w2", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "hasDiagnostics": true, + "children": [ + { + "kind": "ON_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_FAIL_KEYWORD" + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "f", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "hasDiagnostics": true, + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_IDENTIFIER" + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "hasDiagnostics": true, + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_OPEN_BRACE_TOKEN" + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_CLOSE_BRACE_TOKEN" + ] + } + ] + } + ] + }, + { + "kind": "FUNCTION_DEFINITION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_KEYWORD", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "baz" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "FUNCTION_SIGNATURE", + "children": [ + { + "kind": "OPEN_PAREN_TOKEN" + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_PAREN_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "FUNCTION_BODY_BLOCK", + "hasDiagnostics": true, + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "NAMED_WORKER_DECLARATOR", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "LIST", + "hasDiagnostics": true, + "children": [ + { + "kind": "NAMED_WORKER_DECLARATION", + "hasDiagnostics": true, + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "WORKER_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "IDENTIFIER_TOKEN", + "value": "w3", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "ON_FAIL_CLAUSE", + "hasDiagnostics": true, + "children": [ + { + "kind": "ON_KEYWORD", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "FAIL_KEYWORD", + "isMissing": true, + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_MISSING_FAIL_KEYWORD" + ] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "hasDiagnostics": true, + "children": [ + { + "kind": "SIMPLE_NAME_REFERENCE", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "f", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "hasDiagnostics": true, + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "hasDiagnostics": true, + "value": "w4", + "leadingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + }, + { + "kind": "INVALID_NODE_MINUTIAE", + "invalidNode": { + "kind": "INVALID_TOKEN_MINUTIAE_NODE", + "hasDiagnostics": true, + "children": [ + { + "kind": "WORKER_KEYWORD", + "hasDiagnostics": true, + "diagnostics": [ + "ERROR_INVALID_TOKEN" + ] + } + ] + } + }, + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "BLOCK_STATEMENT", + "children": [ + { + "kind": "OPEN_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + }, + { + "kind": "LIST", + "children": [ + { + "kind": "LOCAL_VAR_DECL", + "children": [ + { + "kind": "LIST", + "children": [] + }, + { + "kind": "TYPED_BINDING_PATTERN", + "children": [ + { + "kind": "INT_TYPE_DESC", + "children": [ + { + "kind": "INT_KEYWORD", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + }, + { + "kind": "CAPTURE_BINDING_PATTERN", + "children": [ + { + "kind": "IDENTIFIER_TOKEN", + "value": "b", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + } + ] + } + ] + }, + { + "kind": "EQUAL_TOKEN", + "trailingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ] + }, + { + "kind": "NUMERIC_LITERAL", + "children": [ + { + "kind": "DECIMAL_INTEGER_LITERAL_TOKEN", + "value": "2" + } + ] + }, + { + "kind": "SEMICOLON_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "leadingMinutiae": [ + { + "kind": "WHITESPACE_MINUTIAE", + "value": " " + } + ], + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "LIST", + "children": [] + }, + { + "kind": "CLOSE_BRACE_TOKEN", + "trailingMinutiae": [ + { + "kind": "END_OF_LINE_MINUTIAE", + "value": "\n" + } + ] + } + ] + } + ] + } + ] + }, + { + "kind": "EOF_TOKEN" + } + ] +} diff --git a/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_06.bal b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_06.bal new file mode 100644 index 000000000000..0bea360ca99f --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_06.bal @@ -0,0 +1,30 @@ +function foo() { + worker w1 { + int b = 2; + } on fail { + int c = 3; + } + + worker w2 { + + } + + worker w3 { + int c = 3; + } on fail var e { + + } + + worker w4 { + int c = 3; + } on fail error e { + return error("error!"); + } + + @someAnnot {} + worker w5 returns string { + int c = 3; + } + + int d = 4; +} diff --git a/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_07.bal b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_07.bal new file mode 100644 index 000000000000..6e4abdad6630 --- /dev/null +++ b/compiler/ballerina-parser/src/test/resources/misc/worker-decl/worker_decl_source_07.bal @@ -0,0 +1,22 @@ +function foo() { + worker w1 { + int b = 2; + } % on % fail { + } +} + +function bar() { + worker w2 { + int b = 2; + } on f +} + +function baz() { + worker w3 { + int b = 2; + } on f + + worker w4 { + int b = 2; + } +} diff --git a/compiler/ballerina-treegen/src/main/resources/syntax_node_metadata.json b/compiler/ballerina-treegen/src/main/resources/syntax_node_metadata.json index f62a209a1bcb..ef3b01f225aa 100644 --- a/compiler/ballerina-treegen/src/main/resources/syntax_node_metadata.json +++ b/compiler/ballerina-treegen/src/main/resources/syntax_node_metadata.json @@ -1,4 +1,12 @@ { + "AlternateReceiveNode": { + "createdYear": "2024", + "since": "2201.9.0" + }, + "ReceiveFieldNode": { + "createdYear": "2024", + "since": "2201.9.0" + }, "ClientResourceActionNode": { "createdYear": "2022", "since": "2201.2.0" diff --git a/compiler/ballerina-treegen/src/main/resources/syntax_tree_descriptor.json b/compiler/ballerina-treegen/src/main/resources/syntax_tree_descriptor.json index 478d9541c253..69079a74ca75 100644 --- a/compiler/ballerina-treegen/src/main/resources/syntax_tree_descriptor.json +++ b/compiler/ballerina-treegen/src/main/resources/syntax_tree_descriptor.json @@ -1808,6 +1808,11 @@ { "name": "workerBody", "type": "BlockStatementNode" + }, + { + "name": "onFailClause", + "type": "OnFailClauseNode", + "isOptional": true } ] }, @@ -3327,7 +3332,7 @@ }, { "name": "receiveFields", - "type": "NameReferenceNode", + "type": "Node", "occurrences": "MULTIPLE_SEPARATED" }, { @@ -3336,6 +3341,18 @@ } ] }, + { + "name": "AlternateReceiveNode", + "base": "Node", + "kind": "ALTERNATE_RECEIVE", + "attributes": [ + { + "name": "workers", + "type": "SimpleNameReferenceNode", + "occurrences": "MULTIPLE_SEPARATED" + } + ] + }, { "name": "RestDescriptorNode", "base": "Node", @@ -4940,6 +4957,25 @@ "type": "TypeDescriptorNode" } ] + }, + { + "name": "ReceiveFieldNode", + "base": "Node", + "kind": "RECEIVE_FIELD", + "attributes": [ + { + "name": "fieldName", + "type": "SimpleNameReferenceNode" + }, + { + "name": "colon", + "type": "Token" + }, + { + "name": "peerWorker", + "type": "SimpleNameReferenceNode" + } + ] } ] } diff --git a/docs/bir-spec/src/main/resources/kaitai/bir.ksy b/docs/bir-spec/src/main/resources/kaitai/bir.ksy index a2994bb058fa..8ee90dd30058 100644 --- a/docs/bir-spec/src/main/resources/kaitai/bir.ksy +++ b/docs/bir-spec/src/main/resources/kaitai/bir.ksy @@ -1216,6 +1216,8 @@ types: 'instruction_kind_enum::instruction_kind_new_re_flag_on_off': instruction_new_re_flag_on_off 'instruction_kind_enum::instruction_kind_new_re_quantifier': instruction_new_re_quantifier 'instruction_kind_enum::instruction_kind_record_default_fp_load': instruction_record_default_fp_load + 'instruction_kind_enum::instruction_kind_wk_alt_receive': instruction_wk_alt_receive + 'instruction_kind_enum::instruction_kind_wk_mul_receive': instruction_wk_mul_receive enums: instruction_kind_enum: 1: instruction_kind_goto @@ -1309,6 +1311,8 @@ types: 100: instruction_kind_new_re_flag_on_off 101: instruction_kind_new_re_quantifier 102: instruction_kind_record_default_fp_load + 103: instruction_kind_wk_alt_receive + 104: instruction_kind_wk_mul_receive 128: instruction_kind_platform instruction_const_load: seq: @@ -1505,6 +1509,36 @@ types: type: u1 - id: then_bb_id_name_cp_index type: s4 + instruction_wk_alt_receive: + seq: + - id: channel_name_count + type: s4 + - id: channel_name_cp_index + type: s4 + repeat: expr + repeat-expr: channel_name_count + - id: lhs_operand + type: operand + - id: is_same_strand + type: u1 + - id: then_bb_id_name_cp_index + type: s4 + instruction_wk_mul_receive: + seq: + - id: channel_field_count + type: s4 + - id: channel_field_cp_index + type: receive_field + repeat: expr + repeat-expr: channel_field_count + - id: type_cp_index + type: s4 + - id: lhs_operand + type: operand + - id: is_same_strand + type: u1 + - id: then_bb_id_name_cp_index + type: s4 instruction_wk_send: seq: - id: channel_name_cp_index @@ -1916,6 +1950,12 @@ types: - id: variable type: variable if: ignored_variable == 0 + receive_field: + seq: + - id: field_name + type: s4 + - id: channel_name + type: s4 variable: seq: - id: kind diff --git a/langlib/lang.__internal/src/main/ballerina/auto-close-channels.bal b/langlib/lang.__internal/src/main/ballerina/auto-close-channels.bal new file mode 100644 index 000000000000..9d7beafd4489 --- /dev/null +++ b/langlib/lang.__internal/src/main/ballerina/auto-close-channels.bal @@ -0,0 +1,25 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/jballerina.java; + +# Auto-closes the specified worker channels if they exist; otherwise, closes them upon creation. +# +# + channelIds - channel IDs of the channels to be closed +public function autoClose(string ... channelIds) = @java:Method { + 'class: "org.ballerinalang.langlib.internal.WorkerChannels", + name: "autoClose" +} external; diff --git a/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/WorkerChannels.java b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/WorkerChannels.java new file mode 100644 index 000000000000..f42dced134ac --- /dev/null +++ b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/WorkerChannels.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langlib.internal; + +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.scheduling.Scheduler; +import io.ballerina.runtime.internal.scheduling.Strand; +import io.ballerina.runtime.internal.scheduling.WorkerDataChannel; + +/** + * Native implementation of lang.internal:WorkerChannels. + * + * @since 2201.9.0 + */ +public class WorkerChannels { + + /** + * Auto-closes the specified worker channels if they exist; otherwise, closes them upon creation. + * + * @param channelIds channel IDs of the channels to be closed + */ + public static void autoClose(BString[] channelIds) { + Strand parent = Scheduler.getStrand().parent; + for (BString channelId : channelIds) { + String channelName = channelId.getValue() + ":" + (parent.functionInvocation - 1); + WorkerDataChannel workerDataChannel = parent.wdChannels.getWorkerDataChannel(channelName); + workerDataChannel.autoClose(); + } + } +} diff --git a/langlib/lang.error/src/main/ballerina/error.bal b/langlib/lang.error/src/main/ballerina/error.bal index fc9b87cdac92..3d6dc9740585 100644 --- a/langlib/lang.error/src/main/ballerina/error.bal +++ b/langlib/lang.error/src/main/ballerina/error.bal @@ -16,6 +16,9 @@ import ballerina/jballerina.java; +# Error type representing the no message error in worker interactions. +public type NoMessage distinct error; + # Type for value that can be cloned. # This is the same as in lang.value, but is copied here to avoid a dependency. public type Cloneable readonly|xml|Cloneable[]|map|table>; diff --git a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java index 1dbb9f71852c..bb494638ec54 100644 --- a/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java +++ b/misc/formatter/modules/formatter-core/src/main/java/org/ballerinalang/formatter/core/FormattingTreeModifier.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.formatter.core; +import io.ballerina.compiler.syntax.tree.AlternateReceiveNode; import io.ballerina.compiler.syntax.tree.AnnotAccessExpressionNode; import io.ballerina.compiler.syntax.tree.AnnotationAttachPointNode; import io.ballerina.compiler.syntax.tree.AnnotationDeclarationNode; @@ -164,6 +165,7 @@ import io.ballerina.compiler.syntax.tree.QueryExpressionNode; import io.ballerina.compiler.syntax.tree.QueryPipelineNode; import io.ballerina.compiler.syntax.tree.ReceiveActionNode; +import io.ballerina.compiler.syntax.tree.ReceiveFieldNode; import io.ballerina.compiler.syntax.tree.ReceiveFieldsNode; import io.ballerina.compiler.syntax.tree.RecordFieldNode; import io.ballerina.compiler.syntax.tree.RecordFieldWithDefaultValueNode; @@ -3010,12 +3012,19 @@ public ReceiveActionNode transform(ReceiveActionNode receiveActionNode) { @Override public ReceiveFieldsNode transform(ReceiveFieldsNode receiveFieldsNode) { - Token openBrace = formatToken(receiveFieldsNode.openBrace(), 0, 1); + boolean preserveIndent = env.preserveIndentation; + preserveIndentation(false); + boolean isMultiline = shouldExpand(receiveFieldsNode); + int trailingNL = isMultiline ? 1 : 0; + int trailingWS = isMultiline ? 0 : 1; + Token openBrace = formatToken(receiveFieldsNode.openBrace(), 0, trailingNL); indent(); - SeparatedNodeList receiveFields = formatSeparatedNodeList(receiveFieldsNode.receiveFields(), - 0, 1, 0, 1); - Token closeBrace = formatToken(receiveFieldsNode.closeBrace(), 0, 1); + SeparatedNodeList receiveFields = + formatSeparatedNodeList(receiveFieldsNode.receiveFields(), 0, 0, trailingWS, trailingNL, 0, + trailingNL); unindent(); + Token closeBrace = formatToken(receiveFieldsNode.closeBrace(), env.trailingWS, env.trailingNL); + preserveIndentation(preserveIndent); return receiveFieldsNode.modify() .withOpenBrace(openBrace) .withReceiveFields(receiveFields) @@ -3023,6 +3032,28 @@ public ReceiveFieldsNode transform(ReceiveFieldsNode receiveFieldsNode) { .apply(); } + @Override + public AlternateReceiveNode transform(AlternateReceiveNode alternateReceiveNode) { + SeparatedNodeList workers = + formatSeparatedNodeList(alternateReceiveNode.workers(), 1, 0, env.trailingWS, env.trailingNL); + return alternateReceiveNode.modify() + .withWorkers(workers) + .apply(); + } + + @Override + public ReceiveFieldNode transform(ReceiveFieldNode receiveFieldNode) { + SimpleNameReferenceNode fieldName = formatNode(receiveFieldNode.fieldName(), 0, 0); + Token colon = formatToken(receiveFieldNode.colon(), 1, 0); + SimpleNameReferenceNode peerWorker = formatNode(receiveFieldNode.peerWorker(), env.trailingWS, env.trailingNL); + + return receiveFieldNode.modify() + .withFieldName(fieldName) + .withColon(colon) + .withPeerWorker(peerWorker) + .apply(); + } + @Override public RestDescriptorNode transform(RestDescriptorNode restDescriptorNode) { TypeDescriptorNode typeDescriptor = formatNode(restDescriptorNode.typeDescriptor(), 0, 0); @@ -3505,8 +3536,17 @@ public NamedWorkerDeclarationNode transform(NamedWorkerDeclarationNode namedWork Token workerKeyword = formatToken(namedWorkerDeclarationNode.workerKeyword(), 1, 0); IdentifierToken workerName = formatToken(namedWorkerDeclarationNode.workerName(), 1, 0); Node returnTypeDesc = formatNode(namedWorkerDeclarationNode.returnTypeDesc().orElse(null), 1, 0); - BlockStatementNode workerBody = formatNode(namedWorkerDeclarationNode.workerBody(), env.trailingWS, - env.trailingNL); + + BlockStatementNode workerBody; + OnFailClauseNode onFailClause; + Optional onFailClauseNode = namedWorkerDeclarationNode.onFailClause(); + if (onFailClauseNode.isPresent()) { + workerBody = formatNode(namedWorkerDeclarationNode.workerBody(), 1, 0); + onFailClause = formatNode(onFailClauseNode.get(), env.trailingWS, env.trailingNL); + } else { + workerBody = formatNode(namedWorkerDeclarationNode.workerBody(), env.trailingWS, env.trailingNL); + onFailClause = null; + } return namedWorkerDeclarationNode.modify() .withAnnotations(annotations) @@ -3515,6 +3555,7 @@ public NamedWorkerDeclarationNode transform(NamedWorkerDeclarationNode namedWork .withWorkerName(workerName) .withReturnTypeDesc(returnTypeDesc) .withWorkerBody(workerBody) + .withOnFailClause(onFailClause) .apply(); } @@ -4656,6 +4697,9 @@ private boolean shouldExpand(Node node) { case LIST_CONSTRUCTOR: ListConstructorExpressionNode listConstructorExpressionNode = (ListConstructorExpressionNode) node; return listConstructorExpressionNode.toSourceCode().trim().contains(System.lineSeparator()); + case RECEIVE_FIELDS: + ReceiveFieldsNode receiveFieldsNode = (ReceiveFieldsNode) node; + return receiveFieldsNode.toSourceCode().trim().contains(System.lineSeparator()); default: return false; } diff --git a/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java b/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java index 54d2f79ccaea..d7c39d844c5f 100644 --- a/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java +++ b/misc/formatter/modules/formatter-core/src/test/java/org/ballerinalang/formatter/core/ParserTestFormatter.java @@ -91,6 +91,9 @@ public List skipList() { "separated_node_list_import_decl.bal", "node_location_test_03.bal", + // formatter keeps adding whitespaces #41698 + "worker_decl_source_07.bal", + // parser tests with syntax errors that cannot be handled by the formatter "worker_decl_source_03.bal", "worker_decl_source_05.bal", "invalid_identifier_source_01.bal", "ambiguity_source_23.bal", "ambiguity_source_09.bal", "ambiguity_source_18.bal", diff --git a/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/assert/send_receive_action_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/assert/send_receive_action_4.bal new file mode 100644 index 000000000000..22a414bfd67b --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/assert/send_receive_action_4.bal @@ -0,0 +1,67 @@ +function foo() { + worker w1 { + 1 -> function; + } + worker w2 { + 2 -> function; + } + map x = <- {a: w1, b: w2}; +} + +function bar() { + worker w1 { + 1 -> function; + } + worker w2 { + 2 -> function; + } + map x = <- {w1, w2}; +} + +function baz() { + worker w1 { + 1 -> function; + } + worker w2 { + 2 -> function; + } + map x = <- { + a: w1, + b: w2 + }; + +} + +function foz() { + worker lengthyWorkerName { + 1 -> resultWorker; + } + worker tooLengthyWorkerName { + 2 -> resultWorker; + } + worker short1 { + true -> resultWorker; + } + worker short2 { + "s" -> resultWorker; + } + + worker resultWorker { + map y = <- { + short1: short1, + short2, + lengthyWorkerName, + tooLengthyWorkerName + }; + } +} + +function foooz() { + do { + _ = <- + { + a: w1, + b: w2 + }; + } +} diff --git a/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/source/send_receive_action_4.bal b/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/source/send_receive_action_4.bal new file mode 100644 index 000000000000..e93955324332 --- /dev/null +++ b/misc/formatter/modules/formatter-core/src/test/resources/actions/send-receive/source/send_receive_action_4.bal @@ -0,0 +1,55 @@ +function foo() { + worker w1 { + 1 -> function; + } + worker w2 { + 2 -> function; + } + map x = <- {a:w1,b:w2}; +} + +function bar() { + worker w1 { + 1 -> function; + } + worker w2 { + 2 -> function; + } + map x = <- {w1,w2}; +} + +function baz() { + worker w1 {1->function;} + worker w2 { + 2 -> function;} + map x = <- { + a: w1, + b: w2}; + +} + +function foz() { + worker lengthyWorkerName {1->resultWorker;} + worker tooLengthyWorkerName { + 2 -> resultWorker;} + worker short1 { + true -> resultWorker;} + worker short2 { + "s" -> resultWorker; + } + + worker resultWorker { + map y = <- {short1: short1, short2, + lengthyWorkerName, + tooLengthyWorkerName}; + } +} + +function foooz() { + do { + _ = <- +{ +a: w1,b: w2 +}; + } +} diff --git a/misc/syntax-api-calls-gen/src/main/resources/api_gen_syntax_tree_descriptor.json b/misc/syntax-api-calls-gen/src/main/resources/api_gen_syntax_tree_descriptor.json index 5b402cb200ea..c2eeb0024b7e 100644 --- a/misc/syntax-api-calls-gen/src/main/resources/api_gen_syntax_tree_descriptor.json +++ b/misc/syntax-api-calls-gen/src/main/resources/api_gen_syntax_tree_descriptor.json @@ -1808,6 +1808,11 @@ { "name": "workerBody", "type": "BlockStatementNode" + }, + { + "name": "onFailClause", + "type": "OnFailClauseNode", + "isOptional": true } ] }, @@ -3327,7 +3332,7 @@ }, { "name": "receiveFields", - "type": "NameReferenceNode", + "type": "Node", "occurrences": "MULTIPLE_SEPARATED" }, { @@ -3336,6 +3341,18 @@ } ] }, + { + "name": "AlternateReceiveNode", + "base": "Node", + "kind": "ALTERNATE_RECEIVE", + "attributes": [ + { + "name": "workers", + "type": "SimpleNameReferenceNode", + "occurrences": "MULTIPLE_SEPARATED" + } + ] + }, { "name": "RestDescriptorNode", "base": "Node", @@ -4940,6 +4957,25 @@ "type": "TypeDescriptorNode" } ] + }, + { + "name": "ReceiveFieldNode", + "base": "Node", + "kind": "RECEIVE_FIELD", + "attributes": [ + { + "name": "fieldName", + "type": "SimpleNameReferenceNode" + }, + { + "name": "colon", + "type": "Token" + }, + { + "name": "peerWorker", + "type": "SimpleNameReferenceNode" + } + ] } ] } diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/DiagnosticsTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/DiagnosticsTest.java index 81cbc55dc6ac..af01102fbe23 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/DiagnosticsTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/DiagnosticsTest.java @@ -104,7 +104,7 @@ private Object[][] getExpectedErrors() { {"too many arguments in call to 'map()'", 27, 18}, {"missing close parenthesis token", 27, 39}, - {"invalid usage of receive expression, var not allowed", 37, 8}, + {"receive action not supported wth 'var' type", 37, 8}, {"variable assignment is required", 37, 8}, {"missing identifier", 38, 0}, {"missing semicolon token", 38, 0}, diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/SymbolAtCursorTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/SymbolAtCursorTest.java index 8dafe220b20a..bbeb41c6a182 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/SymbolAtCursorTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/SymbolAtCursorTest.java @@ -134,7 +134,11 @@ public Object[][] getPositionsForExactLookup() { {204, 11, "y2"}, {204, 20, "x2"}, {205, 10, "y3"}, - {205, 19, "y1"} + {205, 19, "y1"}, + {229, 20, "w1"}, + {229, 23, "w2"}, + {243, 20, "w1"}, + {243, 27, "w2"} }; } diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/allreferences/FindRefsInExprsTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/allreferences/FindRefsInExprsTest.java index f2937f300469..84f855a5a14f 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/allreferences/FindRefsInExprsTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/allreferences/FindRefsInExprsTest.java @@ -217,6 +217,16 @@ public Object[][] getLookupPositions() { {212, 16, location(223, 22, 24), List.of(location(212, 16, 18), location(223, 22, 24)) + }, + // Alternate receive + {228, 11, location(228, 11, 13), + List.of(location(228, 11, 13), + location(237, 19, 21)) + }, + // Multiple receive + {246, 11, location(246, 11, 13), + List.of(location(246, 11, 13), + location(251, 26, 28)) } }; } diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/find-all-ref/find_var_ref_in_exprs.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/find-all-ref/find_var_ref_in_exprs.bal index 29791fcd579e..ca2391ae5e60 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/test-src/find-all-ref/find_var_ref_in_exprs.bal +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/find-all-ref/find_var_ref_in_exprs.bal @@ -224,3 +224,31 @@ function (int) returns int func4 = a => a + a; function func3(string s1) returns function (int) returns int { return func4; } + +function testAlternateReceive() { + worker w1 { + 3 -> w3; + } + + worker w2 { + 4 -> w3; + } + + worker w3 { + int _ = <- w1|w2; + } +} + +function testMultipleReceive() { + worker w1 { + 5 -> w3; + } + + worker w2 { + 6 -> w3; + } + + worker w3 { + _ = <- {a: w1, b: w2}; + } +} diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbol_at_cursor_basic_test.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbol_at_cursor_basic_test.bal index 007d0223b31f..b2d8ae93bb86 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbol_at_cursor_basic_test.bal +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/symbol_at_cursor_basic_test.bal @@ -215,4 +215,32 @@ public type ReturnValue02 readonly & string; ReturnValue02 stringVar01 = "ballerina"; -public type ReadOnlyPrimitiveUnion readonly & string|int; \ No newline at end of file +public type ReadOnlyPrimitiveUnion readonly & string|int; + +function testAlternateReceive() { + worker w1 { + 3 -> w3; + } + + worker w2 { + 4 -> w3; + } + + worker w2 { + int _ = <- w1|w2; + } +} + +function testMultipleReceive() { + worker w1 { + 5 -> w3; + } + + worker w2 { + 6 -> w3; + } + + worker w2 { + _ = <- {a: w1, b: w2}; + } +} diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java index 078b4267e901..fe8ed238035a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java @@ -113,20 +113,8 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 93, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 133, 27); validateError(negativeResult, i++, "incompatible types: 'other' is not an iterable collection", 151, 27); - validateError(negativeResult, i++, "invalid usage of receive expression, var not allowed", 151, 27); - validateError(negativeResult, i++, "incompatible types: 'other' is not an iterable collection", 168, 27); - validateError(negativeResult, i++, "invalid usage of receive expression, var not allowed", 168, 27); - validateError(negativeResult, i++, "multiple receive action not yet supported", 168, 30); - validateError(negativeResult, i++, "multiple receive action not yet supported", 169, 28); - validateError(negativeResult, i++, "multiple receive action not yet supported", 170, 23); - validateError(negativeResult, i++, "multiple receive action not yet supported", 179, 28); - validateError(negativeResult, i++, "multiple receive action not yet supported", 180, 23); - validateError(negativeResult, i++, "multiple receive action not yet supported", 189, 28); - validateError(negativeResult, i++, "multiple receive action not yet supported", 191, 23); - validateError(negativeResult, i++, "multiple receive action not yet supported", 200, 28); - validateError(negativeResult, i++, "multiple receive action not yet supported", 202, 23); - validateError(negativeResult, i++, "multiple receive action not yet supported", 211, 28); - validateError(negativeResult, i++, "multiple receive action not yet supported", 213, 23); + validateError(negativeResult, i++, "receive action not supported wth 'var' type", 151, 27); + validateError(negativeResult, i++, "receive action not supported wth 'var' type", 168, 27); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 279, 15); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 291, 18); validateError(negativeResult, i++, "order by not supported for complex type fields, order key should " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/BasicWorkerActionsNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/BasicWorkerActionsNegativeTest.java index 6eaf9b90c43a..0684adfc0508 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/BasicWorkerActionsNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/BasicWorkerActionsNegativeTest.java @@ -43,7 +43,7 @@ public void testWorkerActionsSemanticsNegative() { BAssertUtil.validateError(resultSemanticsNegative, index++, "action invocation as an expression not allowed here", 78, 15); BAssertUtil.validateError(resultSemanticsNegative, index++, - "invalid usage of receive expression, var not allowed", 112, 21); + "receive action not supported wth 'var' type", 112, 21); BAssertUtil.validateError(resultSemanticsNegative, index++, "missing identifier", 139, 13); BAssertUtil.validateError(resultSemanticsNegative, index++, @@ -63,67 +63,61 @@ public void testNegativeWorkerActions() { BAssertUtil.validateError(resultNegative, index++, "invalid worker flush expression for 'w1', there are no " + "worker send statements to 'w1' from 'w3'", 62, 17); BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'val'", 63, 9); - BAssertUtil.validateError(resultNegative, index++, "worker send statement position not supported yet, " + - "must be a top level statement in a worker", 76, 13); - BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'msg'", 81, 9); + BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'msg'", 78, 9); BAssertUtil.validateError(resultNegative, index++, "invalid worker receive statement position, must be a " + - "top level statement in a worker", 83, 19); - BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'x1'", 91, 9); - BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'x2'", 92, 9); - BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'result'", 93, 9); + "top level statement in a worker", 80, 19); + BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'x1'", 88, 9); + BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'x2'", 89, 9); + BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'result'", 90, 9); BAssertUtil.validateError(resultNegative, index++, "invalid worker flush expression for 'w2', there are no " + - "worker send statements to 'w2' from 'w1'", 93, 25); - BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'j'", 100, 9); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 144, 26); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 161, 26); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 162, 26); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 166, 25); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 167, 22); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 168, 25); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 169, 21); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 190, 17); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 198, 30); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 200, 26); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 218, 30); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 219, 30); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 220, 75); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 226, 29); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 227, 26); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 228, 29); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 229, 25); - BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 231, 21); + "worker send statements to 'w2' from 'w1'", 90, 25); + BAssertUtil.validateWarning(resultNegative, index++, "unused variable 'j'", 97, 9); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 141, 26); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 158, 26); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 159, 26); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 163, 25); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 164, 22); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 165, 25); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 166, 21); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 187, 17); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 195, 30); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 197, 26); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 215, 30); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 216, 30); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 217, 75); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 223, 29); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wix"), 224, 26); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 225, 29); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wx"), 226, 25); + BAssertUtil.validateError(resultNegative, index++, formatMessage("wy"), 228, 21); String notSupportedMsg = "worker send statement position not supported yet, " + "must be a top level statement in a worker"; - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 244, 13); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 246, 17); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 252, 13); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 253, 13); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 257, 13); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 258, 13); - BAssertUtil.validateError(resultNegative, index++, "unreachable code", 261, 9); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 249, 13); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 250, 13); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 254, 13); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 255, 13); + BAssertUtil.validateError(resultNegative, index++, "unreachable code", 258, 9); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 260, 17); BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 263, 17); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 266, 17); - BAssertUtil.validateError(resultNegative, index++, "undefined worker 'w1'", 271, 26); + BAssertUtil.validateError(resultNegative, index++, "undefined worker 'w1'", 268, 26); BAssertUtil.validateError(resultNegative, index++, - "invalid worker receive statement position, must be a top level statement in a worker", 278, 21); + "invalid worker receive statement position, must be a top level statement in a worker", 275, 21); BAssertUtil.validateError(resultNegative, index++, - "invalid worker receive statement position, must be a top level statement in a worker", 283, 17); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 290, 26); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 292, 30); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 298, 26); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 299, 26); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 303, 26); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 304, 26); - BAssertUtil.validateError(resultNegative, index++, "unreachable code", 307, 22); + "invalid worker receive statement position, must be a top level statement in a worker", 280, 17); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 295, 26); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 296, 26); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 300, 26); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 301, 26); + BAssertUtil.validateError(resultNegative, index++, "unreachable code", 304, 22); + BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 306, 30); BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 309, 30); - BAssertUtil.validateError(resultNegative, index++, notSupportedMsg, 312, 30); - BAssertUtil.validateError(resultNegative, index++, "undefined worker 'w1'", 317, 39); + BAssertUtil.validateError(resultNegative, index++, "undefined worker 'w1'", 314, 39); BAssertUtil.validateError(resultNegative, index++, - "invalid worker receive statement position, must be a top level statement in a worker", 324, 34); + "invalid worker receive statement position, must be a top level statement in a worker", 321, 34); BAssertUtil.validateError(resultNegative, index++, - "invalid worker receive statement position, must be a top level statement in a worker", 329, 30); + "invalid worker receive statement position, must be a top level statement in a worker", 326, 30); Assert.assertEquals(resultNegative.getDiagnostics().length, index, "Worker actions negative test diagnostic count"); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java index 559420eaa2a2..c56b0e5d6c61 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java @@ -197,12 +197,13 @@ public void testWaitCausingADeadlockNegative() { String msg = "worker send/receive interactions are invalid; worker(s) cannot move onwards from the state: '%s'"; BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, FINISHED]"), 19, 14); BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, wait x, FINISHED]"), 29, 14); - BAssertUtil.validateError(result, index++, String.format(msg, "[wait w2, <- w, wait w1, FINISHED]"), 43, 14); + BAssertUtil.validateError(result, index++, "invalid worker send, no matching worker receive", 46, 9); + BAssertUtil.validateError(result, index++, "invalid worker receive, no matching worker send", 50, 17); BAssertUtil.validateError(result, index++, String.format(msg, "[wait v, wait w, wait x, FINISHED]"), 61, 18); BAssertUtil.validateError(result, index++, String.format(msg, "[wait vi, wait wi, wait xi, FINISHED, FINISHED]"), 78, 23); - BAssertUtil.validateError(result, index++, - String.format(msg, "[11 -> w3, FINISHED, FINISHED, wait {w1,w2,w3}]"), 94, 15); + BAssertUtil.validateError(result, index++, "invalid worker send, no matching worker receive", 95, 9); + BAssertUtil.validateError(result, index++, "invalid worker receive, no matching worker send", 96, 20); Assert.assertEquals(result.getErrorCount(), index); } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerAlternateReceiveTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerAlternateReceiveTest.java new file mode 100644 index 000000000000..67738badcdc0 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerAlternateReceiveTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.test.worker; + +import org.ballerinalang.test.BAssertUtil; +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Worker alternative receive related tests. + * + * @since 2201.9.0 + */ +public class WorkerAlternateReceiveTest { + + private CompileResult result; + + @BeforeClass + public void setup() { + result = BCompileUtil.compile("test-src/workers/workers_alt_receive.bal"); + Assert.assertEquals(result.getErrorCount(), 0); + } + + @Test(dataProvider = "functionProvider") + public void workerAlternateReceiveTest(String funcName) { + BRunUtil.invoke(result, funcName, new Object[0]); + } + + @DataProvider + public static String[] functionProvider() { + return new String[] { + "workerAlternateReceiveTest", + "workerAlternateReceiveTest2", + "alternateReceiveWithSenderPanic", + "alternateReceiveWithMultiplePanic", + "alternateReceiveWithSenderError", + "alternateReceiveWithMultipleError", + "alternateReceiveWithPanicAndError", + "alternateReceiveWithReceiverPanic", + "alternateReceiveWithReceiverError", + "alternateReceiveWithSameWorkerSend", + "alternateReceiveWithSameWorkerSendError1", + "alternateReceiveWithSameWorkerSendError2", + "alternateReceiveWithSameWorkerSendPanic", + "multilpleAlternateReceive1", + "multilpleAlternateReceive2", + "workerAlternateReceiveWithConditionalSend" + }; + } + + @Test(description = "Test alternate receive type checking") + public void testAltWorkerReceiveTypeChecking() { + CompileResult negativeResult = BCompileUtil.compile("test-src/workers/alternate_receive_type_checking.bal"); + int index = 0; + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 69, + 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found '" + + "(int|ballerina/lang.error:0.0.0:NoMessage)'", 70, 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 71, + 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 72, + 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 73, + 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found '" + + "(decimal|string|int|boolean)'", 74, 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found '" + + "(decimal|string|int|boolean|ballerina/lang.error:0.0.0:NoMessage)'", 75, 20); + Assert.assertEquals(negativeResult.getErrorCount(), index); + } + + @AfterClass + public void tearDown() { + result = null; + } +} diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerConditionalSendTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerConditionalSendTest.java new file mode 100644 index 000000000000..720441d1454d --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerConditionalSendTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.ballerinalang.test.worker; + +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Worker conditional send related tests. + * + * @since 2201.9.0 + */ +public class WorkerConditionalSendTest { + + private CompileResult result; + + @BeforeClass + public void setup() { + this.result = BCompileUtil.compile("test-src/workers/workers_conditional_send.bal"); + Assert.assertEquals(result.getErrorCount(), 0); + } + + @Test(dataProvider = "functionProvider") + public void workerConditionalSendTest(String funcName) { + BRunUtil.invoke(result, funcName, new Object[0]); + } + + @DataProvider + public static String[] functionProvider() { + return new String[] { + "workerConditionalSendTest", + "sameWorkerSendTest", + "sameWorkerSendEitherOnePath", + "sameWorkerSendAltReceiveSendError", + "sameWorkerSendAltReceiveReceiverError", + "sameWorkerSendElse", + "sameWorkerSendSenderPanic", + "sameWorkerSendReceiverPanic", + "sameWorkerSendMultiplePath1", + "sameWorkerSendMultiplePath2", + "sameWorkerSendMultiplePathError1", + "sameWorkerSendMultiplePathError2", + "sameWorkerSendMultiplePathError3", + "sameWorkerSendMultiplePathError4", + "multipleReceiveConditional", + "multipleReceiveWithNonConditionalSend", + "testNonTopLevelSend", + "testSendWithEarlyReturnError" + }; + } + + @AfterClass + public void tearDown() { + result = null; + } +} diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java index 7c9d4c241d00..990243a46c7f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerFailTest.java @@ -32,11 +32,50 @@ public class WorkerFailTest { @Test - public void invalidWorkerSendReceive() { - CompileResult result = BCompileUtil.compile("test-src/workers/invalid-worker-send-receive.bal"); - String message = Arrays.toString(result.getDiagnostics()); - Assert.assertEquals(result.getErrorCount(), 1); - Assert.assertTrue(message.contains(" interactions are invalid"), message); + public void testSendReceiveAllowedSyntacticPositions() { + CompileResult result = BCompileUtil.compile("test-src/workers/send-receive-allowed-positions.bal"); + String sendNotAllowedError = "worker send statement position not supported yet, " + + "must be a top level statement in a worker"; + String receiveNotAllowedError = "invalid worker receive statement position, " + + "must be a top level statement in a worker"; + int index = 0; + validateError(result, index++, sendNotAllowedError, 20, 13); + validateError(result, index++, sendNotAllowedError, 24, 13); + validateError(result, index++, "using send action within the lock statement is not allowed to prevent " + + "possible deadlocks", 28, 13); + validateError(result, index++, receiveNotAllowedError, 66, 17); + validateError(result, index++, receiveNotAllowedError, 70, 17); + validateError(result, index++, "using receive action within the lock statement is not allowed to prevent " + + "possible deadlocks", 74, 17); + validateError(result, index++, receiveNotAllowedError, 79, 15); + validateError(result, index++, receiveNotAllowedError, 81, 21); + Assert.assertEquals(result.getErrorCount(), index); + } + + @Test + public void testMismatchInSendReceivePairing() { + CompileResult result = BCompileUtil.compile("test-src/workers/send-receive-mismatch.bal"); + String invalidSendErrMsg = "invalid worker send, no matching worker receive"; + String invalidReceiveErrMsg = "invalid worker receive, no matching worker send"; + int index = 0; + validateError(result, index++, invalidSendErrMsg, 19, 9); + validateError(result, index++, invalidSendErrMsg, 23, 9); + validateError(result, index++, invalidReceiveErrMsg, 29, 17); + validateError(result, index++, invalidReceiveErrMsg, 33, 20); + validateError(result, index++, invalidSendErrMsg, 45, 9); + validateError(result, index++, invalidReceiveErrMsg, 60, 21); + validateError(result, index++, invalidReceiveErrMsg, 70, 34); + validateError(result, index++, invalidReceiveErrMsg, 83, 20); + validateError(result, index++, invalidReceiveErrMsg, 83, 27); + validateError(result, index++, invalidReceiveErrMsg, 83, 34); + validateError(result, index++, invalidSendErrMsg, 87, 9); + validateWarning(result, index++, "unused variable 'b'", 95, 13); + validateError(result, index++, invalidReceiveErrMsg, 97, 17); + validateWarning(result, index++, "unused variable 'b'", 101, 13); + validateWarning(result, index++, "unused variable 'a'", 106, 13); + validateWarning(result, index++, "unused variable 'b'", 107, 13); + Assert.assertEquals(result.getErrorCount(), index - 4); + Assert.assertEquals(result.getWarnCount(), 4); } @Test @@ -77,11 +116,21 @@ public void invalidSendInLambda() { } @Test - public void invalidSendWithReturnTest() { - CompileResult result = BCompileUtil.compile("test-src/workers/invalid-send-with-return.bal"); - Assert.assertEquals(result.getErrorCount(), 1); - String message = result.getDiagnostics()[0].message(); - Assert.assertTrue(message.contains("can not be used after a non-error return"), message); + public void testSendReceiveFailureType() { + CompileResult result = BCompileUtil.compile("test-src/workers/send-receive-failure-type.bal"); + int index = 0; + validateError(result, index++, "incompatible types: expected 'int', " + + "found '(int|ballerina/lang.error:0.0.0:NoMessage)'", 49, 15); + validateError(result, index++, "incompatible types: expected 'int', " + + "found '(ErrorA|int|ballerina/lang.error:0.0.0:NoMessage)'", 50, 15); + validateError(result, index++, "incompatible types: expected 'int', " + + "found '(ErrorA|ErrorB|string|ballerina/lang.error:0.0.0:NoMessage)'", 51, 15); + validateError(result, index++, "incompatible types: expected 'int', found '(ErrorA|ErrorB|int)'", 71, 15); + validateError(result, index++, "incompatible types: expected 'int', " + + "found '(ErrorA|ErrorB|int|ballerina/lang.error:0.0.0:NoMessage)'", 86, 15); + validateError(result, index++, "incompatible types: expected '()', found 'ErrorA?'", 119, 14); + validateError(result, index++, "incompatible types: expected '()', found '(ErrorA|ErrorB)?'", 120, 14); + Assert.assertEquals(result.getErrorCount(), index); } @Test @@ -139,24 +188,6 @@ public void testSyncSendReceiveMismatch() { validateWarning(result, 3, "unused variable 'err'", 47, 5); } - @Test - public void invalidSendInIf() { - CompileResult result = BCompileUtil.compile("test-src/workers/invalid-send-in-if.bal"); - String message = Arrays.toString(result.getDiagnostics()); - Assert.assertEquals(result.getErrorCount(), 1, message); - Assert.assertTrue(message.contains("worker send statement position not supported yet, " + - "must be a top level statement in a worker"), message); - } - - @Test - public void invalidSyncSendInIf() { - CompileResult result = BCompileUtil.compile("test-src/workers/invalid-sync-send-in-if.bal"); - String message = Arrays.toString(result.getDiagnostics()); - Assert.assertEquals(result.getErrorCount(), 1, message); - Assert.assertTrue(message.contains("worker send statement position not supported yet, " + - "must be a top level statement in a worker"), message); - } - @Test public void invalidReceiveInIf() { CompileResult result = BCompileUtil.compile("test-src/workers/invalid-receive-in-if.bal"); @@ -238,15 +269,6 @@ public void invalidAsyncSendInFork() { "must be a top level statement in a worker"), message); } - @Test - public void invalidSyncSendInFork() { - CompileResult result = BCompileUtil.compile("test-src/workers/invalid-sync-send-in-fork.bal"); - String message = Arrays.toString(result.getDiagnostics()); - Assert.assertEquals(result.getErrorCount(), 1, message); - Assert.assertTrue(message.contains("worker send statement position not supported yet, " + - "must be a top level statement in a worker"), message); - } - @Test public void invalidReceiveInFork() { CompileResult result = BCompileUtil.compile("test-src/workers/invalid-receive-in-fork.bal"); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerMultipleReceiveTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerMultipleReceiveTest.java new file mode 100644 index 000000000000..04374251d96c --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerMultipleReceiveTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com) + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.ballerinalang.test.worker; + +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Worker alternative receive related tests. + * + * @since 2201.9.0 + */ +public class WorkerMultipleReceiveTest { + + private CompileResult result; + + @BeforeClass + public void setup() { + this.result = BCompileUtil.compile("test-src/workers/workers_multiple_receive.bal"); + Assert.assertEquals(result.getErrorCount(), 0); + } + + @Test(dataProvider = "functionProvider") + public void workerMultipleReceiveTest(String funcName) { + BRunUtil.invoke(result, funcName, new Object[0]); + } + + @DataProvider + public static String[] functionProvider() { + return new String[] { + "workerMultipleReceiveTest1", + "workerMultipleReceiveTest2", + "workerMultipleReceiveTest3", + "workerMultipleReceiveWithUserDefinedRecord", + "workerMultipleReceiveWithErrorReturn", + "workerMultipleReceiveWithAllErrorReturn", + "workerMultipleReceiveWithPanic", + "workerMultipleReceiveWithAllPanic", + "workerMultipleReceiveWithErrorReturnRec", + "workerMultipleReceiveWithConditionalSend1", + "workerMultipleReceiveWithConditionalSend2", + "workerMultipleReceiveWithConditionalSend3" + }; + } + + @AfterClass + public void tearDown() { + result = null; + } +} diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java new file mode 100644 index 000000000000..487c2272ab99 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerOnFailTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.ballerinalang.test.worker; + +import org.ballerinalang.test.BCompileUtil; +import org.ballerinalang.test.BRunUtil; +import org.ballerinalang.test.CompileResult; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.util.Arrays; + +/** + * Tests the worker on fail clause. + * + * @since 2021.9.0 + */ +public class WorkerOnFailTest { + + private CompileResult result; + + @BeforeClass + public void setup() { + this.result = BCompileUtil.compile("test-src/workers/worker-on-fail.bal"); + Assert.assertEquals(result.getErrorCount(), 0, Arrays.asList(result.getDiagnostics()).toString()); + } + + @Test + public void simpleOnFailTest() { + Object returns = BRunUtil.invoke(result, "testOnFailInWorker"); + long ret = (long) returns; + Assert.assertEquals(ret, -1); + } + + @Test + public void doOnFailInsideWorker() { + Object returns = BRunUtil.invoke(result, "testDoOnFailInsideWorker"); + long ret = (long) returns; + Assert.assertEquals(ret, 3); + } + + @Test + public void returnWithinOnFail() { + Object returns = BRunUtil.invoke(result, "testReturnWithinOnFail"); + long ret = (long) returns; + Assert.assertEquals(ret, -1); + } + + @Test + public void onFailWorkerWithVariable() { + Object returns = BRunUtil.invoke(result, "testOnFailWorkerWithVariable"); + long ret = (long) returns; + Assert.assertEquals(ret, 0); + } + + @Test + public void workerOnFailWithSend() { + Object returns = BRunUtil.invoke(result, "testWorkerOnFailWithSend"); + long ret = (long) returns; + Assert.assertEquals(ret, 1); + } + +} diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerTest.java index c47d6264c89f..ad54efc92529 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WorkerTest.java @@ -346,8 +346,36 @@ public void testWorkerInsideLock() { public void testMultipleReceiveAction() { // Multiple receive action is not yet supported. This is to test the error message. CompileResult result = BCompileUtil.compile("test-src/workers/multiple-receive-action.bal"); - Assert.assertEquals(result.getErrorCount(), 1); - BAssertUtil.validateError(result, 0, "multiple receive action not yet supported", 23, 25); + Assert.assertEquals(result.getErrorCount(), 0); + BRunUtil.invoke(result, "testMultipleReceiveAction"); + } + + @Test(description = "Test multiple receive type checking") + public void testMultipleWorkerReceiveTypeChecking() { + CompileResult negativeResult = BCompileUtil.compile("test-src/workers/multiple-receive-type-checking.bal"); + int index = 0; + BAssertUtil.validateError(negativeResult, index++, "invalid multiple receive: duplicate key 'a'", 27, 24); + BAssertUtil.validateError(negativeResult, index++, "invalid multiple receive: duplicate key 'a'", 38, 24); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in " + + "type 'int'", 55, 17); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " 'record {| int c; int a; int b; |}'", 57, 45); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " 'record {| |} & readonly'", 62, 25); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " 'ABCRecord'", 67, 23); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " '(string|record {| int a; int b; int c; anydata...; |}|int)'", 72, 54); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " '(string|boolean|int)'", 73, 32); + BAssertUtil.validateError(negativeResult, index++, "ambiguous type '(map|record {| int a; int b; anydata" + + "...; |})'", 74, 45); + BAssertUtil.validateError(negativeResult, index++, "ambiguous type '(map|record {| int...; |})'", 75, 42); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " '(int|record {| int a; int c; anydata...; |})'", 77, 40); + BAssertUtil.validateError(negativeResult, index++, "a type compatible with multiple receive not found in type" + + " 'record {| readonly int a; readonly int c; |} & readonly'", 85, 49); + Assert.assertEquals(negativeResult.getErrorCount(), index); } @Test diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/actions-negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/actions-negative.bal index 5641b8b7e4b1..145c6a733eb6 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/actions-negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/actions-negative.bal @@ -71,11 +71,8 @@ function workerActionSecTest() { string msg = "hello"; msg -> w2; - - if (true) { - i -> w2; - } } + worker w2 { print("w1"); string msg = "default"; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/alternate_receive_type_checking.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/alternate_receive_type_checking.bal new file mode 100644 index 000000000000..beadd1836343 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/alternate_receive_type_checking.bal @@ -0,0 +1,79 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +function testAltReceiveNoMessageErrorType(boolean b) { + worker w1 { + // case 1 + 1 -> w2; + 2 -> w2; + + // case 2 + if b { + 3 -> w2; + 4 -> w2; + } + + // case 3 + if b { + 5 -> w2; + } + 6 -> w2; + + // case 4 + 7 -> w2; + if b { + 8 -> w2; + } + + // case 5 + 9 -> w2; + 10 -> w2; + if b { + 11 -> w2; + } + 12 -> w2; + + // case 5 + 13.3d -> w2; + if b { + "xx" -> w2; + 14 -> w2; + true -> w2; + } + + // case 6 + if !b { + 13.3d -> w2; + } + if b { + "xx" -> w2; + 14 -> w2; + true -> w2; + } + } + + worker w2 { + string _ = <- w1|w1; // found 'int' + string _ = <- w1|w1; // found 'int|error:NoMessage' + string _ = <- w1|w1; // found 'int' + string _ = <- w1|w1; // found 'int' + string _ = <- w1|w1|w1|w1; // found 'int' + string _ = <- w1|w1|w1|w1; // found 'decimal|string|int|boolean' + string _ = <- w1|w1|w1|w1; // found 'decimal|string|int|boolean|error:NoMessage' + } + + _ = wait {a: w1, b: w2}; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-in-if.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-in-if.bal deleted file mode 100644 index e281c959d004..000000000000 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-in-if.bal +++ /dev/null @@ -1,16 +0,0 @@ - - -public function main() { - worker w1{ - int i = 20; - if (0 > 1) { - i -> w2; - } - } - - worker w2 { - int j = 25; - j = <- w1; - } -} - diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-with-return.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-with-return.bal deleted file mode 100644 index 21707110cb97..000000000000 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-send-with-return.bal +++ /dev/null @@ -1,23 +0,0 @@ -import ballerina/jballerina.java; - -public function main() { - worker w1 returns boolean|error{ - int i = 2; - if (0 > 1) { - return true; - } - i -> w2; - println("w1"); - return false; - } - - worker w2 { - int j = 25; - j = <- w1; - println(j); - } -} - -public function println(any|error... values) = @java:Method { - 'class: "org.ballerinalang.test.utils.interop.Utils" -} external; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-fork.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-fork.bal deleted file mode 100644 index 672a1e065efa..000000000000 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-fork.bal +++ /dev/null @@ -1,25 +0,0 @@ -public function main() { - fork { - worker w1{ - int i = 20; - } - - worker w2 { - int j = 25; - int sum = 0; - fork { - worker w3 { - foreach var i in 1... 10 { - sum = sum + i; - if (true) { - var xx = i ->> w4; - } - } - } - worker w4 { - int k = <- w3; - } - } - } - } -} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-if.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-if.bal deleted file mode 100644 index 194ae6479259..000000000000 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-sync-send-in-if.bal +++ /dev/null @@ -1,16 +0,0 @@ - - -public function main() { - worker w1{ - int i = 20; - if(true){ - var ans = i ->> w2; - } - } - - worker w2 { - int j = 25; - j = <- w1; - } -} - diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal deleted file mode 100644 index b1d3899311dd..000000000000 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/invalid-worker-send-receive.bal +++ /dev/null @@ -1,22 +0,0 @@ - -function invalidWorkerSendReceive() { - fork { - worker w1 { - int a = 5; - int b = 0; - a -> w2; - b = <- w3; - } - worker w2 { - int a = 0; - int b = 15; - a = <- w1; - a -> w3; - } - worker w3 { - int a = 0; - int b = 15; - a = <- w2; - } - } -} \ No newline at end of file diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-action.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-action.bal index 2b9d30d2662c..2420a968742b 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-action.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-action.bal @@ -14,14 +14,18 @@ // specific language governing permissions and limitations // under the License. -public function main() { +import ballerina/test; + +public function testMultipleReceiveAction() { worker w1 { 100 -> w2; } - worker w2 { + worker w2 returns map { map m = <- { w1 }; + return m; } - wait w2; + map m = wait w2; + test:assertEquals(m, { w1: 100 }, "Invalid map returned"); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-type-checking.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-type-checking.bal new file mode 100644 index 000000000000..856692e77a15 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/multiple-receive-type-checking.bal @@ -0,0 +1,92 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +public function testMultipleReceiveDuplicateKey1() { + worker w1 { + 50 -> w3; + } + + worker w2 { + 100 -> w3; + } + + worker w3 { + _ = <- {a: w1, a: w2}; // error: duplicate key + } +} + +public function testMultipleReceiveDuplicateKey2() { + worker w1 { + 50 -> w2; + 100 -> w2; + } + + worker w2 { + _ = <- {a: w1, a: w1}; // error: duplicate key + } +} + +type MapOfInt map; + +type IntType int; + +type MapOfInt2 map; + +type ABRecord record {|int a; int b;|}; + +type ABCRecord record {|int a; int b; string c;|}; + +public function testMultipleReceiveTypeChecking1() { + worker w2 { + // Non-union cases + int _ = <- {a: w1, b: w1}; // error: incompatible type + map _ = <- {a: w1, b: w1}; // OK + record {|int c; int a; int b;|} _ = <- {a: w1, b: w1}; // error: incompatible type + record {|int c?; int a; int b;|} _ = <- {a: w1, b: w1}; // OK + record {|int a; int b;|} _ = <- {a: w1, b: w1}; // OK + record {|int a; int b; int...;|} _ = <- {a: w1, b: w1}; // OK + record {|int...;|} _ = <- {a: w1, b: w1}; // OK + record {||} _ = <- {a: w1, b: w1}; // error: incompatible type + record {} _ = <- {a: w1, b: w1}; // OK + MapOfInt _ = <- {a: w1, b: w1}; // OK + MapOfInt2 _ = <- {a: w1, b: w1}; // OK + ABRecord _ = <- {a: w1, b: w1}; // OK + ABCRecord _ = <- {a: w1, b: w1}; // error: incompatible type + + // Union cases + string|int|map _ = <- {a: w1, b: w1}; // OK + string|record {int a; int b;}|int _ = <- {a: w1, b: w1}; // OK + string|record {int a; int b; int c;}|int _ = <- {a: w1, b: w1}; // error: incompatible type + string|boolean|int _ = <- {a: w1, b: w1}; // error: incompatible type + map|record {int a; int b;} _ = <- {a: w1, b: w1}; // error: ambiguous type + map|record {|int ...;|} _ = <- {a: w1, b: w1}; // error: ambiguous type + map|record {int a; int c;} _ = <- {a: w1, b: w1}; // OK + int|record {int a; int c;} _ = <- {a: w1, b: w1}; // error: incompatible type + + // Special cases + readonly r = <- {a: w1, b: w1}; // OK + json _ = <- {a: w1, b: w1}; // OK + anydata _ = <- {a: w1, b: w1}; // OK + any _ = <- {a: w1, b: w1}; // OK + record {|int a; int b;|} & readonly _ = <- {a: w1, b: w1}; // OK + record {|int a; int c;|} & readonly _ = <- {a: w1, b: w1}; // error: incompatible type + } + + worker w1 { + 1 ->> w2; + 2 ->> w2; + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-allowed-positions.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-allowed-positions.bal new file mode 100644 index 000000000000..c7806fe03161 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-allowed-positions.bal @@ -0,0 +1,107 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +public function testSendAllowedLocations(boolean b) returns error? { + worker w1 { + foreach var i in [1, 2, 3] { + i -> function; // error: position not allowed + } + + while b { + 119 -> function; // error: position not allowed + } + + lock { + 120 -> function; // error: position not allowed + } + + 1 -> function; // OK + if b { + 2 -> function; // OK + if b { + 3 -> function; // OK + } + } + () _ = 4 -> function; // OK + () _ = (4 -> function); // OK + do { + 5 -> function; // OK + } + { + 5 -> function; // OK + } + } on fail { + 6 -> function; // OK + } + + _ = <- w1; + _ = check <- w1; + _ = check <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; + _ = <- w1; +} + +public function testReceiveAllowedLocations(boolean b) returns error? { + worker w1 { + foreach int _ in [1, 2, 3] { + _ = <- function; // error: position not allowed + } + + while b { + _ = <- function; // error: position not allowed + } + + lock { + _ = <- function; // error: position not allowed + } + + _ = <- function; // OK + if b { + _ = <- function; // error: position not allowed + if b { + _ = <- function; // error: position not allowed + } + } + _ = <- function; // OK + _ = (<- function); // OK + do { + _ = <- function; // OK + } + { + _ = <- function; // OK + } + } on fail { + _ = <- function; // OK + } + + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; + 1 -> w1; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-failure-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-failure-type.bal new file mode 100644 index 000000000000..8ad304087044 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-failure-type.bal @@ -0,0 +1,133 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +public type ErrorA distinct error; + +public type ErrorB distinct error; + +public function testSendFailureType1(boolean b, boolean c) { + worker w1 returns boolean|ErrorA|ErrorB { + int i = 2; + + i -> w2; + if (0 > 1) { + return true; + } + i = i + 1; + i -> w2; + + if b { + return error ErrorA("error A"); + } + 44 -> w2; + + if c { + return error ErrorB("error B"); + } + "xxx" -> w2; + "yyy" -> w2; + "zzz" -> w2; + + return true; + } + + worker w2 { + int _ = <- w1; // OK + int _ = <- w1; // error: found 'int|error:NoMessage' + int _ = <- w1; // error: found 'int|ErrorA|error:NoMessage' + int _ = <- w1; // error: found 'string|ErrorA|ErrorB|error:NoMessage' + string|ErrorA|ErrorB|error:NoMessage m = <- w1; // OK + string|error n = <- w1; // OK + + println(m); + println(n); + } +} + +public function testSendFailureType2(boolean b) { + worker w1 returns boolean|ErrorA|ErrorB { + ErrorA|ErrorB e = error ErrorA("error A"); + if b { + return e; + } + 11 -> w2; + return true; + } + + worker w2 { + int _ = <- w1; // error: found '(ErrorA|ErrorB|int)' + } +} + +public function testSendFailureType3(boolean b) { + worker w1 returns boolean|ErrorA|ErrorB { + ErrorA|ErrorB|boolean e = error ErrorA("error A"); + if b { + return e; + } + 22 -> w2; + return true; + } + + worker w2 { + int _ = <- w1; // error: found '(ErrorA|ErrorB|int|error:NoMessage) + } +} + +public function testReceiveFailureType(boolean b, boolean c) { + worker w1 returns boolean|ErrorA|ErrorB { + int i = 2; + + _ = <- w2; + if (0 > 1) { + return true; + } + i = i + 1; + _ = <- w2; + + if b { + return error ErrorA("error A"); + } + _ = <- w2; + + if c { + return error ErrorB("error B"); + } + _ = <- w2; + _ = <- w2; + _ = <- w2; + + return true; + } + + worker w2 { + () _ = 1 ->> w1; // OK + () _ = 2 ->> w1; // OK + () p = 3 ->> w1; // error: found 'ErrorA?' + () q = 4 ->> w1; // error: found '(ErrorA|ErrorB)?' + ErrorA|ErrorB? r = 5 ->> w1; // OK + error? s = 6 ->> w1; // OK + + println(p); + println(q); + println(r); + println(s); + } +} + +public function println(any|error value) { + return; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-mismatch.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-mismatch.bal new file mode 100644 index 000000000000..fd7f379f3c56 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/send-receive-mismatch.bal @@ -0,0 +1,111 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +function case1() { + worker w1 { + 20 -> w2; + } + + worker w2 { + "xxx" -> w1; + } +} + +function case2() { + worker w1 { + int _ = <- w2; + } + + worker w2 { + string _ = <- w1; + } +} + +function case3() { + boolean foo = true; + worker w1 { + if foo { + 2 -> w2; + } else { + 3 -> w2; + } + 4 -> w2; + } + + worker w2 returns error? { + _ = check <- w1; + _ = check <- w1; + } +} + +function case4() { + worker w1 { + "xxx" -> w2; + } + + worker w2 { + _ = <- w1 | w1; + } +} + +function case5() { + worker w1 { + "xxx" -> w2; + } + + worker w2 { + _ = <- {a: w1, b: w3, c: w3}; + } + + worker w3 { + "yyy" -> w2; + } +} + +function case6() { + worker w1 { + } + + worker w2 { + _ = <- {a: w1, b: w3, c: w3}; + } + + worker w3 { + "yyy" -> w2; + } +} + +function case7() { + fork { + worker w1 { + int a = 5; + int b = 0; + a -> w2; + b = <- w3; + } + worker w2 { + int a = 0; + int b = 15; + a = <- w1; + a -> w3; + } + worker w3 { + int a = 0; + int b = 15; + a = <- w2; + } + } +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal new file mode 100644 index 000000000000..6029a48eda52 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/worker-on-fail.bal @@ -0,0 +1,105 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +function testOnFailInWorker() returns int { + int[] vals = returnIntArr(); + int key = returnOne(); + int val = 0; + + worker A { + int? index = vals.indexOf(key); + if index != () { + val = vals[index]; + } else { + check error("value not found"); + } + } on fail { + val = -1; + } + wait A; + return val; +} + +function testDoOnFailInsideWorker() returns int { + int val = 0; + worker A { + do { + val += 1; + fail error("error in do"); + } on fail { + val += 1; + } + fail error("error for worker"); + } on fail { + val += 1; + } + wait A; + return val; +} + +function testReturnWithinOnFail() returns int { + int x = returnOne(); + worker A returns string { + if (x == 1) { + check error("one"); + } + return "not one"; + } on fail error e { + return e.message(); + } + string str = wait A; + return str == "one" ? -1 : 0; +} + +function testOnFailWorkerWithVariable() returns int { + int x = 0; + worker A { + do { + x += 1; + fail error("error in do"); + } on fail { + x += 1; + } + fail error("error in worker"); + } on fail error e { + if e.message() == "error in worker" { + x -= 2; + } else { + x -= -1; + } + } + wait A; + return x; +} + +function testWorkerOnFailWithSend() returns int { + worker A { + int x = 1; + x -> B; + check error("testWorkerOnFailWithSend"); + } on fail var err { + _ = err.message(); + } + + worker B returns int { + return <- A; + } + return wait B; +} + +function returnOne() returns int => 1; + +function returnIntArr() returns int[] => [2, 3, 4, 5]; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_alt_receive.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_alt_receive.bal new file mode 100644 index 000000000000..41afac26944a --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_alt_receive.bal @@ -0,0 +1,401 @@ +// Copyright (c) 2024 WSO2 Inc. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/lang.runtime; + +function workerAlternateReceiveTest() { + worker w1 { + 2 -> w2; + } + + worker w2 returns int { + int result = <- w1 | w3; + return result; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + worker w4 { + runtime:sleep(2); + 2 -> w5; + } + + worker w5 returns int { + int result = <- w4 | w6; + return result; + } + + worker w6 { + 3 -> w5; + } + + map results = wait {a: w2, b: w5}; + test:assertEquals(results["a"], 2, "Invalid int result"); + test:assertEquals(results["b"], 3, "Invalid int result"); +} + +function workerAlternateReceiveTest2() { + worker w1 { + runtime:sleep(2); + 1 -> w5; + } + + worker w2 { + runtime:sleep(3); + 2 -> w5; + } + + worker w3 { + 3 -> w5; + } + + worker w4 { + runtime:sleep(4); + 4 -> w5; + } + + worker w5 returns int { + int result = <- w1 | w2 | w3 | w4; + return result; + } + + int result = wait w5; + test:assertEquals(result, 3, "Invalid int result"); +} + +function alternateReceiveWithSenderPanic() { + worker w1 { + int value = 10; + if value == 10 { + panic error("Error in worker 1"); + } + value -> w2; + } + + worker w2 returns int { + int a = <- w1 | w3; + return a; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + int|error unionResult = trap wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function alternateReceiveWithMultiplePanic() { + worker w1 { + int value = 10; + if value == 10 { + panic error("Error in worker 1"); + } + value -> w2; + } + + worker w2 returns int { + int a = <- w1 | w3; + return a; + } + + worker w3 { + int value = 10; + if value == 10 { + panic error("Error in worker 3"); + } + 3 -> w2; + } + + int|error unionResult = trap wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + boolean validError = e.message() == "Error in worker 1" || e.message() == "Error in worker 3"; + test:assertTrue(validError, "Invalid error message"); +} + +function alternateReceiveWithSenderError() { + worker w1 returns error? { + int value = 10; + if value == 10 { + return error("Error in worker 1"); + } + value -> w2; + } + + worker w2 returns int|error { + int|error a = <- w1 | w3; + return a; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + int|error unionResult = wait w2; + test:assertTrue(unionResult is int, "Expected int result"); + test:assertEquals(unionResult, 3, "Invalid int result"); +} + +function alternateReceiveWithMultipleError() { + worker w1 returns error? { + int value = 10; + if value == 10 { + return error("Error in worker 1"); + } + value -> w2; + } + + worker w2 returns int|error { + int|error a = <- w1 | w3; + return a; + } + + worker w3 returns error? { + int value = 10; + runtime:sleep(2); + if value == 10 { + return error("Error in worker 3"); + } + value -> w2; + } + + int|error unionResult = wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 3", "Invalid error message"); +} + +function alternateReceiveWithPanicAndError() { + worker w1 { + int value = 10; + if value == 10 { + panic error("Error in worker 1"); + } + value -> w2; + } + + worker w2 returns int|error { + int|error a = <- w1 | w3; + return a; + } + + worker w3 returns error? { + int value = 10; + if value == 10 { + return error("Error in worker 3"); + } + value -> w2; + } + + int|error unionResult = trap wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function alternateReceiveWithReceiverPanic() { + worker w1 { + 10 -> w2; + } + + worker w2 returns int { + int value = 10; + if value == 10 { + panic error("Error in worker 2"); + } + int a = <- w1 | w3; + return a; + } + + worker w3 { + 3 -> w2; + } + + int|error unionResult = trap wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 2", "Invalid error message"); +} + +function alternateReceiveWithReceiverError() { + worker w1 { + 10 -> w2; + } + + worker w2 returns int|error { + int value = 10; + if value == 10 { + return error("Error in worker 2"); + } + int a = <- w1 | w3; + return a; + } + + worker w3 { + 3 -> w2; + } + + int|error unionResult = wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 2", "Invalid error message"); +} + +function alternateReceiveWithSameWorkerSend() { + worker w1 { + 1 -> w2; + 2 -> w2; + } + + worker w2 returns int { + int x = <- w1 | w1; + return x; + } + + int intResult = wait w2; + test:assertEquals(intResult, 1, "Invalid int result"); +} + +function alternateReceiveWithSameWorkerSendError1() { + worker w1 { + int|error x = int:fromString("invalid"); + x -> w2; + 2 -> w2; + } + + worker w2 returns int|error { + int|error x = <- w1 | w1; + return x; + } + + int|error intResult = wait w2; + test:assertEquals(intResult, 2, "Invalid int result"); +} + + +function alternateReceiveWithSameWorkerSendError2() { + worker w1 { + int|error x = int:fromString("invalid"); + x -> w2; + x -> w2; + } + + worker w2 returns int|error { + int|error x = <- w1 | w1; + return x; + } + + int|error intResult = wait w2; + test:assertTrue(intResult is error, "Expected error result"); + error e = intResult; + test:assertEquals(e.message(), "{ballerina/lang.int}NumberParsingError", "Invalid error result"); + test:assertEquals(e.detail().toString(), "{\"message\":\"'string' value 'invalid' cannot be converted to 'int'\"}", + "Invalid error result"); +} + +function alternateReceiveWithSameWorkerSendPanic() { + worker w1 { + int x = checkpanic int:fromString("invalid"); + x -> w2; + 2 -> w2; + } + + worker w2 returns int { + int x = <- w1 | w1; + return x; + } + + int|error intResult = trap wait w2; + test:assertTrue(intResult is error, "Expected error result"); + error e = intResult; + test:assertEquals(e.message(), "{ballerina/lang.int}NumberParsingError", "Invalid error result"); + test:assertEquals(e.detail().toString(), "{\"message\":\"'string' value 'invalid' cannot be converted to 'int'\"}", + "Invalid error result"); + } + + function multilpleAlternateReceive1() { + worker w1 { + 1 -> w2; + 2 -> w2; + } + + worker w2 returns [int, int] { + int x = <- w1 | w3; + int y = <- w1; + return [x, y]; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + [int, int] [x, y] = wait w2; + test:assertEquals(x, 1, "Invalid int result"); + test:assertEquals(y, 2, "Invalid int result"); + } + + function multilpleAlternateReceive2() { + worker w1 { + runtime:sleep(2); + 1 -> w2; + 2 -> w2; + } + + worker w2 returns [int, int] { + int x = <- w1 | w3; + int y = <- w1; + return [x, y]; + } + + worker w3 { + 3 -> w2; + } + + [int, int] [x, y] = wait w2; + test:assertEquals(x, 3, "Invalid int result"); + test:assertEquals(y, 2, "Invalid int result"); + } + +function workerAlternateReceiveWithConditionalSend() returns error? { + worker w1 { + boolean b1 = true; + boolean b2 = true; + boolean b3 = false; + + if b1 { + 1 -> function; + } else if b2 { + 2 -> function; + } else if b3 { + 3 -> function; + } else { + 4 -> function; + } + } + + int n = check <- w1|w1|w1|w1; + test:assertEquals(n, 1); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_conditional_send.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_conditional_send.bal new file mode 100644 index 000000000000..08051b73b216 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_conditional_send.bal @@ -0,0 +1,479 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/lang.'error as errorLib; + +function workerConditionalSendTest() { + boolean foo = true; + + worker w1 { + if foo { + 1 -> w2; + } else { + 2 -> w3; + } + } + + worker w2 returns int|errorLib:NoMessage { + int|errorLib:NoMessage a = <- w1; + return a; + } + + worker w3 { + int|errorLib:NoMessage b = <- w1; + int|errorLib:NoMessage c = <- w4; + } + + worker w4 { + if !foo { + 1 -> w3; + } else { + 2 -> w5; + } + } + + worker w5 returns int|errorLib:NoMessage { + int|errorLib:NoMessage d = <- w4; + return d; + } + + map results = wait {a: w2, b: w5}; + test:assertEquals(results["a"], 1, "Invalid int result"); + test:assertEquals(results["b"], 2, "Invalid int result"); +} + +function sameWorkerSendTest() { + boolean foo = true; + + worker w1 { + if foo { + 10 -> w2; + } else { + 20 -> w2; + } + } + + worker w2 { + int|errorLib:NoMessage a = <- w1; + int|errorLib:NoMessage b = <- w1; + test:assertEquals(a, 10, "Invalid int result"); + test:assertTrue(b is error); + error e = b; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'w2'\"}", "Invalid error detail"); + } + + _ = wait {a: w1, b: w2}; +} + +function sameWorkerSendEitherOnePath() { + boolean foo = true; + + worker w1 { + if foo { + 10 -> w2; + } else { + 20 -> w2; + } + } + + worker w2 { + int|errorLib:NoMessage a = <- w1 | w1; + test:assertEquals(a, 10, "Invalid int result"); + } + + _ = wait {a: w1, b: w2}; +} + +function sameWorkerSendAltReceiveSendError() { + boolean foo = true; + + worker w1 returns error? { + if foo { + if foo && true { + return error("Error in worker 1"); + } else { + 10 -> w2; + } + } else { + 20 -> w2; + } + } + + worker w2 { + int|error a = <- w1 | w1; + test:assertTrue(a is error, "Invalid error result"); + error e = a; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no worker message received for channel 'w1->w2'\"}", "Invalid error detail"); + } + error? unionResult = wait w1; + test:assertTrue(unionResult is error, "Invalid error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function sameWorkerSendAltReceiveReceiverError() { + boolean foo = true; + + worker w1 returns error? { + if foo { + if foo && true { + return error("Error in worker 1"); + } else { + 10 -> w2; + } + } else { + 20 -> w2; + } + } + + worker w2 returns int|error { + int value = 10; + if value == 10 { + return error("Error in worker 2"); + } + int|error a = <- w1 | w1; + return a; + } + + int|error unionResult = wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 2", "Invalid error message"); +} + +function sameWorkerSendElse() { + boolean foo = false; + + worker w1 returns error? { + if foo { + if foo && true { + return error("Error in worker 1"); + } else { + 10 -> w2; + } + } else { + 20 -> w2; + } + } + + worker w2 { + int|error a = <- w1 | w1; + test:assertTrue(a is int, "Invalid int result"); + test:assertEquals(a, 20, "Wrong int value received"); + } + + error? unionResult = wait w1; + test:assertTrue(unionResult is (), "Invalid result"); +} + +function sameWorkerSendSenderPanic() { + + boolean foo = true; + + worker w1 { + if foo { + if foo && true { + panic error("Error in worker 1"); + } else { + 10 -> w2; + } + } else { + 20 -> w2; + } + } + + worker w2 { + int|errorLib:NoMessage a = <- w1 | w1; + } + + error? unionResult = trap wait w1; + test:assertTrue(unionResult is error, "Invalid error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function sameWorkerSendReceiverPanic() { + boolean foo = true; + + worker w1 { + if foo { + 10 -> w2; + } else { + 20 -> w2; + } + } + + worker w2 returns int|errorLib:NoMessage { + int value = 10; + if value == 10 { + panic error("Error in worker 2"); + } + int|errorLib:NoMessage a = <- w1 | w1; + return a; + } + + int|error unionResult = trap wait w2; + test:assertTrue(unionResult is error, "Expected error result"); + error e = unionResult; + test:assertEquals(e.message(), "Error in worker 2", "Invalid error message"); +} + +function sameWorkerSendMultiplePath1() { + boolean foo = true; + + worker w1 { + if foo { + 1 -> w2; + } + 2 -> w2; + } + + worker w2 returns int|errorLib:NoMessage { + int|errorLib:NoMessage a = <- w1 | w1; + return a; + } + + int|errorLib:NoMessage intResult = wait w2; + test:assertEquals(intResult, 1, "Invalid int result"); +} + +function sameWorkerSendMultiplePath2() { + boolean foo = false; + + worker w1 { + if foo { + 1 -> w2; + } + 2 -> w2; + } + + worker w2 returns int|errorLib:NoMessage { + int|errorLib:NoMessage a = <- w1 | w1; + return a; + } + + int|errorLib:NoMessage intResult = wait w2; + test:assertEquals(intResult, 2, "Invalid int result"); +} + +function sameWorkerSendMultiplePathError1() { + boolean foo = false; + + worker w1 returns error? { + int value = 10; + if foo { + if value == 10 { + return error("Error in worker 1"); + } + 1 -> w2; + } + 2 -> w2; + } + + worker w2 returns int|error? { + int|error a = <- w1 | w1; + return a; + } + + map mapResult = wait { a : w1, b: w2}; + test:assertTrue(mapResult["a"] is (), "Invalid nil result"); + test:assertEquals(mapResult["b"], 2, "Invalid int result"); +} + +function sameWorkerSendMultiplePathError2() { + boolean foo = false; + + worker w1 returns error? { + int value = 10; + if foo { + 1 -> w2; + } + if value == 10 { + return error("Error in worker 1"); + } + 2 -> w2; + } + + worker w2 returns int|error { + int|error a = <- w1 | w1; + return a; + } + + map mapResult = wait { a : w1, b: w2}; + test:assertTrue(mapResult["a"] is error, "Invalid error result"); + test:assertTrue(mapResult["b"] is error, "Invalid error result"); + error e = mapResult["a"]; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); + e = mapResult["b"]; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function sameWorkerSendMultiplePathError3() { + boolean foo = true; + + worker w1 returns error? { + int value = 10; + if foo { + if value == 10 { + return error("Error in worker 1"); + } + 1 -> w2; + } + 2 -> w2; + } + + worker w2 returns int|error? { + int|error a = <- w1 | w1; + return a; + } + + map mapResult = wait { a : w1, b: w2}; + test:assertTrue(mapResult["a"] is error, "Invalid error result"); + test:assertTrue(mapResult["b"] is error, "Invalid error result"); + error e = mapResult["a"]; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); + e = mapResult["b"]; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); +} + +function sameWorkerSendMultiplePathError4() { + boolean foo = true; + + worker w1 returns error? { + int value = 10; + if foo { + 1 -> w2; + } + if value == 10 { + return error("Error in worker 1"); + } + 2 -> w2; + } + + worker w2 returns int|error { + int|error a = <- w1 | w1; + return a; + } + + map mapResult = wait { a : w1, b: w2}; + error e = mapResult["a"]; + test:assertEquals(e.message(), "Error in worker 1", "Invalid error message"); + test:assertEquals(mapResult["b"], 1, "Invalid int result"); +} + +function multipleReceiveConditional() { + boolean foo = true; + worker w1 returns int { + if (foo) { + int x = 2; + x -> w2; + return x; + } else { + 2-> w3; + } + int y = <- w3; + return y; + } + + worker w2 returns int|errorLib:NoMessage { + int|errorLib:NoMessage y = <- w1; + return y; + } + + worker w3 returns int|errorLib:NoMessage { + int|errorLib:NoMessage y = <- w1; + 3 -> w1; + return y; + } + + map mapResult = wait {a: w1, b: w2, c: w3}; + test:assertEquals(mapResult["a"], 2, "Invalid int result"); + test:assertEquals(mapResult["b"], 2, "Invalid int result"); + test:assertTrue(mapResult["c"] is error, "Expected error result"); + error e = mapResult["c"]; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'w3'\"}", "Invalid error detail"); +} + +function multipleReceiveWithNonConditionalSend() { + boolean foo = true; + worker w3 { + int|errorLib:NoMessage x = <- w1; + int|errorLib:NoMessage y = <- w1; + int z = <- w1; + test:assertEquals(x, 2, "Invalid int result"); + test:assertTrue(y is error, "Invalid error result"); + error e = y; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'w3'\"}", "Invalid error detail"); + test:assertEquals(z, 4, "Invalid int result"); + } + + worker w1 { + if foo { + 2 -> w3; + } else { + 3 -> w3; + } + 4 -> w3; + } + wait w3; +} + +function testNonTopLevelSend() { + worker w1 returns boolean { + int i = 2; + if (0 > 1) { + return true; + } + i -> w2; + return false; + } + + worker w2 returns int|errorLib:NoMessage { + int|errorLib:NoMessage j = 25; + j = <- w1; + return j; + } + + map mapResult = wait {a: w1, b: w2}; + test:assertEquals(mapResult["a"], false, "Invalid boolean result"); + test:assertEquals(mapResult["b"], 2, "Invalid int result"); +} + +function testSendWithEarlyReturnError() { + worker w1 returns boolean|error { + int i = 2; + if (0 > 1) { + return error("Error in worker 1"); + } + i -> w2; + return false; + } + + worker w2 returns int|error { + int|error j = 25; + j = <- w1; + return j; + } + + map mapResult = wait {a: w1, b: w2}; + test:assertEquals(mapResult["a"], false, "Invalid boolean result"); + test:assertEquals(mapResult["b"], 2, "Invalid int result"); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_multiple_receive.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_multiple_receive.bal new file mode 100644 index 000000000000..b882eda09607 --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/workers_multiple_receive.bal @@ -0,0 +1,344 @@ +// Copyright (c) 2024 WSO2 Inc. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +import ballerina/test; +import ballerina/lang.runtime; +import ballerina/lang.'error as errorLib; + +function workerMultipleReceiveTest1() { + worker w1 { + 2 -> w2; + } + + worker w2 returns map { + map result = <- { w1, w3}; + return result; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + map results = wait w2; + test:assertEquals(results["w1"], 2, "Invalid int result"); + test:assertEquals(results["w3"], 3, "Invalid int result"); +} + +function workerMultipleReceiveTest2() { + worker w1 { + 2 -> w2; + } + + worker w2 returns map { + map result = <- { a:w1, b:w3}; + return result; + } + + worker w3 { + runtime:sleep(2); + 3 -> w2; + } + + map results = wait w2; + test:assertEquals(results["a"], 2, "Invalid int result"); + test:assertEquals(results["b"], 3, "Invalid int result"); +} + +function workerMultipleReceiveTest3() { + worker w1 { + 100 -> w2; + 20 -> w2; + } + + worker w2 returns map { + map result = {} ; + map m = <- { a:w1, b:w3 }; + result["first"] = m; + m = <- { aa:w1, bb:w3 }; + result["second"] = m; + return result; + } + + worker w3 { + 6 -> w2; + 45 -> w2; + } + + map mapResult = wait w2; + test:assertEquals(mapResult["first"], { "a":100, "b":6}, "Invalid map result"); + test:assertEquals(mapResult["second"], { "aa":20, "bb":45}, "Invalid map result"); +} + +type MyRec record {| + int firstValue; + int secondValue; +|}; + +function workerMultipleReceiveWithUserDefinedRecord() { + worker w1 { + 100 -> w2; + 20 -> w2; + } + + worker w2 returns map { + map result = {} ; + MyRec m = <- { firstValue:w1, secondValue:w3 }; + result["first"] = m; + m = <- { firstValue:w1, secondValue:w3 }; + result["second"] = m; + return result; + } + + worker w3 { + 6 -> w2; + 45 -> w2; + } + + map mapResult = wait w2; + test:assertEquals(mapResult["first"], {"firstValue":100,"secondValue":6}, "Invalid map result"); + test:assertEquals(mapResult["second"], {"firstValue":20,"secondValue":45}, "Invalid map result"); +} + +function workerMultipleReceiveWithErrorReturn() { + worker w1 returns error?{ + int v = 10; + if v == 10 { + return error("Error in worker w1"); + } + 100 -> w2; + } + + worker w2 returns map { + map m = <- { firstValue:w1, secondValue:w3 }; + return m; + } + + worker w3 { + 6 -> w2; + } + + map mapResult = wait w2; + test:assertTrue(mapResult["firstValue"] is error, "Invalid map result"); + error e = mapResult["firstValue"]; + test:assertEquals(e.message(), "Error in worker w1", "Invalid error message"); + test:assertEquals(mapResult["secondValue"], 6, "Invalid int result"); +} + +function workerMultipleReceiveWithAllErrorReturn() { + worker w1 returns error?{ + int v = 10; + if v == 10 { + return error("Error in worker w1"); + } + 100 -> w2; + } + + worker w2 returns map { + map m = <- { firstValue:w1, secondValue:w3 }; + return m; + } + + worker w3 returns error?{ + int v = 10; + if v == 10 { + return error("Error in worker w3"); + } + 6 -> w2; + } + + map mapResult = wait w2; + test:assertTrue(mapResult["firstValue"] is error, "Invalid map result"); + error e = mapResult["firstValue"]; + test:assertEquals(e.message(), "Error in worker w1", "Invalid error message"); + test:assertTrue(mapResult["secondValue"] is error, "Invalid map result"); + e = mapResult["secondValue"]; + test:assertEquals(e.message(), "Error in worker w3", "Invalid error message"); +} + +function workerMultipleReceiveWithPanic() { + worker w1 { + int v = 10; + if v == 10 { + panic error("Error in worker w1"); + } + 100 -> w2; + } + + worker w2 returns map { + map m = <- { firstValue:w1, secondValue:w3 }; + return m; + } + + worker w3 { + 6 -> w2; + } + + map|error result = trap wait w2; + test:assertTrue(result is error, "Invalid map result"); + error e = result; + test:assertEquals(e.message(), "Error in worker w1", "Invalid error message"); +} + +function workerMultipleReceiveWithAllPanic() { + worker w1 { + runtime:sleep(2); + int v = 10; + if v == 10 { + panic error("Error in worker w1"); + } + 100 -> w2; + } + + worker w2 returns map { + map m = <- { firstValue:w1, secondValue:w3 }; + return m; + } + + worker w3 { + int v = 10; + if v == 10 { + panic error("Error in worker w3"); + } + 6 -> w2; + } + + map|error result = trap wait w2; + test:assertTrue(result is error, "Invalid map result"); + error e = result; + test:assertEquals(e.message(), "Error in worker w3", "Invalid error message"); +} + +function workerMultipleReceiveWithErrorReturnRec() { + worker w1 returns error?{ + int v = 10; + if v == 10 { + return error("Error in worker w1"); + } + 100 -> w2; + } + + worker w2 returns ErrRec { + ErrRec m = <- { firstValue:w1, secondValue:w3 }; + return m; + } + + worker w3 { + 6 -> w2; + } + + ErrRec mapResult = wait w2; + test:assertTrue(mapResult.firstValue is error, "Invalid map result"); + error e = mapResult.firstValue; + test:assertEquals(e.message(), "Error in worker w1", "Invalid error message"); + test:assertEquals(mapResult.secondValue, 6, "Invalid int result"); +} + +type ErrRec record {| + int|error firstValue; + int secondValue; +|}; + +function workerMultipleReceiveWithConditionalSend1() { + boolean foo = true; + + worker w1 { + if foo { + 1 -> w2; + } else { + 2 -> w3; + } + } + + worker w2 returns map { + map m = <- {w1, w3}; + return m; + } + + worker w3 { + int|errorLib:NoMessage a = <- w1; + test:assertTrue(a is error, "Invalid result"); + error e = a; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'w3'\"}", "Invalid error detail"); + 3 -> w2; + } + + map mapResult = wait w2; + test:assertEquals(mapResult["w1"], 1, "Invalid map result"); + test:assertEquals(mapResult["w3"], 3, "Invalid map result"); +} + +function workerMultipleReceiveWithConditionalSend2() { + boolean foo = true; + + worker w1 { + if foo { + 1 -> w3; + } else { + 2 -> w2; + } + } + + worker w2 returns map { + map m = <- {w1, w3}; + return m; + } + + worker w3 { + int|errorLib:NoMessage a = <- w1; + test:assertTrue(a is int, "Invalid result"); + test:assertEquals(a, 1, "Invalid int result"); + 3 -> w2; + } + + map mapResult = wait w2; + test:assertTrue(mapResult["w1"] is errorLib:NoMessage, "Invalid map result"); + errorLib:NoMessage e = mapResult["w1"]; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'w2'\"}", "Invalid error detail"); + test:assertEquals(mapResult["w3"], 3, "Invalid map result"); +} + +function workerMultipleReceiveWithConditionalSend3() { + worker w1 { + boolean b1 = true; + boolean b2 = true; + boolean b3 = false; + + if b1 { + 1 -> function; + } else if b2 { + 2 -> function; + } else if b3 { + 3 -> function; + } else { + 4 -> function; + } + } + + map mapResult = <- {a:w1, b:w1, c:w1, d:w1}; + test:assertEquals(mapResult["a"], 1); + + int|errorLib:NoMessage? bResult = mapResult["b"]; + test:assertTrue(bResult is errorLib:NoMessage); + errorLib:NoMessage e = bResult; + test:assertEquals(e.message(), "NoMessage", "Invalid error message"); + test:assertEquals(e.detail().toString(), "{\"message\":\"no message received from worker 'w1' to worker 'function'\"}", "Invalid error detail"); + + test:assertTrue(mapResult["c"] is errorLib:NoMessage); + test:assertTrue(mapResult["d"] is errorLib:NoMessage); +}