From d2c4cb30de26f04aece7757bddc11c4bba1c5ad8 Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Fri, 7 Jun 2024 14:20:43 +0530 Subject: [PATCH 1/8] Fix blocking issue with invokeAsync API --- .../runtime/internal/BalRuntime.java | 143 +++++++++++++++--- .../runtime/internal/errors/ErrorCodes.java | 4 +- .../main/resources/MessagesBundle.properties | 4 +- 3 files changed, 125 insertions(+), 26 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java index 48031fde0fed..dd1ccf6af5a5 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java @@ -38,6 +38,7 @@ import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.launch.LaunchUtils; import io.ballerina.runtime.internal.scheduling.AsyncUtils; +import io.ballerina.runtime.internal.scheduling.RuntimeRegistry; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; import io.ballerina.runtime.internal.util.RuntimeUtils; @@ -50,12 +51,14 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.function.Function; import static io.ballerina.identifier.Utils.encodeNonFunctionIdentifier; import static io.ballerina.runtime.api.constants.RuntimeConstants.ANON_ORG; import static io.ballerina.runtime.api.constants.RuntimeConstants.CONFIGURATION_CLASS_NAME; import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; +import static io.ballerina.runtime.api.constants.RuntimeConstants.MODULE_INIT_CLASS_NAME; /** * Internal implementation of the API used by the interop users to control Ballerina runtime behavior. @@ -67,6 +70,9 @@ public class BalRuntime extends Runtime { private final Scheduler scheduler; private final Module module; private boolean moduleInitialized = false; + private boolean moduleStarted = false; + private boolean moduleStopped = false; + private Thread schedulerThread = null; public BalRuntime(Scheduler scheduler, Module module) { this.scheduler = scheduler; @@ -74,38 +80,49 @@ public BalRuntime(Scheduler scheduler, Module module) { } public BalRuntime(Module module) { - this.scheduler = new Scheduler(false); + this.scheduler = new Scheduler(true); this.module = module; } @Override public void init() { + if (moduleInitialized) { + throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "init"); + } invokeConfigInit(); - invokeMethodAsync("$moduleInit", null, PredefinedTypes.TYPE_NULL, "init", new Object[1]); + schedulerThread = new Thread(scheduler::start); + schedulerThread.start(); + invokeMethodSync("$moduleInit"); moduleInitialized = true; } @Override public void start() { if (!moduleInitialized) { - throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_METHOD_CALL, "start"); + throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_FUNCTION_INVOCATION_BEFORE_MODULE_INIT, "start"); + } + if (moduleStarted) { + throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "start"); } - invokeMethodAsync("$moduleStart", null, PredefinedTypes.TYPE_NULL, "start", new Object[1]); + invokeMethodSync("$moduleStart"); + moduleStarted = true; } @Override public void invokeMethodAsync(String functionName, Callback callback, Object... args) { if (!moduleInitialized) { - throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_FUNCTION_INVOCATION, functionName); + throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_FUNCTION_INVOCATION_BEFORE_MODULE_INIT, + functionName); } - invokeMethodAsync(functionName, callback, PredefinedTypes.TYPE_ANY, functionName, args); + invokeMethod(functionName, callback, PredefinedTypes.TYPE_ANY, functionName, args); } @Override public void stop() { if (!moduleInitialized) { - throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_METHOD_CALL, "stop"); + throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_FUNCTION_INVOCATION_BEFORE_MODULE_INIT, "stop"); } +<<<<<<< HEAD invokeMethodAsync("$moduleStop", null, PredefinedTypes.TYPE_NULL, "stop", new Scheduler(false), null); } @@ -120,6 +137,20 @@ private void invokeMethodAsync(String functionName, Callback callback, Type retu System.arraycopy(args, 0, argsWithStrand, 1, args.length); scheduler.schedule(argsWithStrand, func, future); scheduler.start(); +======= + if (moduleStopped) { + throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "stop"); + } + scheduler.poison(); + try { + schedulerThread.join(); + } catch (InterruptedException e) { + throw ErrorCreator.createError(StringUtils.fromString("ballerina: error occurred in while waiting for " + + "scheduler thread to finish"), e); + } + invokeModuleStop(); + moduleStopped = true; +>>>>>>> 6f23b0a1586 (Fix blocking issue with invokeAsync API) } /** @@ -339,19 +370,12 @@ public void registerStopHandler(BFunctionPointer stopHandler) { } private void invokeConfigInit() { - String configClassName = getConfigClassName(this.module); - Class configClazz; - try { - configClazz = Class.forName(configClassName); - } catch (Throwable e) { - throw ErrorCreator.createError(StringUtils.fromString("failed to load configuration class :" + - configClassName)); - } + Class configClass = loadClass(CONFIGURATION_CLASS_NAME); ConfigDetails configDetails = LaunchUtils.getConfigurationDetails(); String funcName = Utils.encodeFunctionIdentifier("$configureInit"); try { final Method method = - configClazz.getDeclaredMethod(funcName, Map.class, String[].class, Path[].class, String.class); + configClass.getDeclaredMethod(funcName, Map.class, String[].class, Path[].class, String.class); method.invoke(null, new HashMap<>(), new String[]{}, configDetails.paths, configDetails.configContent); } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw ErrorCreator.createError(StringUtils.fromString("configurable initialization failed due to " + @@ -359,17 +383,92 @@ private void invokeConfigInit() { } } - private static String getConfigClassName(Module module) { - String configClassName = CONFIGURATION_CLASS_NAME; + private void invokeModuleStop() { + Class initClass = loadClass(MODULE_INIT_CLASS_NAME); + String funcName = Utils.encodeFunctionIdentifier("$moduleStop"); + try { + final Method method = initClass.getDeclaredMethod(funcName, RuntimeRegistry.class); + method.invoke(null, scheduler.getRuntimeRegistry()); + + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { + throw ErrorCreator.createError(StringUtils.fromString("calling module stop failed due to " + + RuntimeUtils.formatErrorMessage(e)), e); + } + } + + private Class loadClass(String moduleInitClassName) { + String initClassName = getFullQualifiedClassName(this.module, moduleInitClassName); + Class initClazz; + try { + initClazz = Class.forName(initClassName); + } catch (Throwable e) { + throw ErrorCreator.createError(StringUtils.fromString("failed to load configuration class :" + + initClassName), e); + } + return initClazz; + } + + private static String getFullQualifiedClassName(Module module, String className) { String orgName = module.getOrg(); String packageName = module.getName(); if (!DOT.equals(packageName)) { - configClassName = encodeNonFunctionIdentifier(packageName) + "." + module.getMajorVersion() + "." + - configClassName; + className = encodeNonFunctionIdentifier(packageName) + "." + module.getMajorVersion() + "." + className; } if (!ANON_ORG.equals(orgName)) { - configClassName = encodeNonFunctionIdentifier(orgName) + "." + configClassName; + className = encodeNonFunctionIdentifier(orgName) + "." + className; + } + return className; + } + + private void invokeMethodSync(String functionName) { + final CountDownLatch latch = new CountDownLatch(1); + SyncCallback callback = new SyncCallback(latch); + invokeMethod(functionName, callback, PredefinedTypes.TYPE_NULL, functionName, new Object[1]); + try { + latch.await(); + } catch (InterruptedException e) { + throw ErrorCreator.createError(e); + } + if (callback.initError != null) { + throw callback.initError; + } + } + + private void invokeMethod(String functionName, Callback callback, Type returnType, + String strandName, Object... args) { + ValueCreator valueCreator = ValueCreator.getValueCreator(ValueCreator.getLookupKey(module.getOrg(), + module.getName(), module.getMajorVersion(), module.isTestPkg())); + Function func = o -> valueCreator.call((Strand) (((Object[]) o)[0]), functionName, args); + FutureValue future = scheduler.createFuture(null, callback, null, returnType, strandName, null); + Object[] argsWithStrand = new Object[args.length + 1]; + argsWithStrand[0] = future.strand; + System.arraycopy(args, 0, argsWithStrand, 1, args.length); + scheduler.schedule(argsWithStrand, func, future); + } + + /** + * This class used to handle ballerina function invocation synchronously. + * + * @since 2201.9.1 + */ + static class SyncCallback implements Callback { + + CountDownLatch latch; + BError initError; + + public SyncCallback(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void notifySuccess(Object result) { + latch.countDown(); + } + + @Override + public void notifyFailure(BError error) { + latch.countDown(); + initError = error; } - return configClassName; } } 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 125c7e3fb47e..1a1d8e966fd8 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 @@ -156,8 +156,8 @@ public enum ErrorCodes implements DiagnosticCode { CONFIG_UNUSED_ENV_VARS("config.env.unused.vars", "RUNTIME_0126"), CONFIG_ENV_VAR_NAME_AMBIGUITY("config.env.variable.name.ambiguity", "RUNTIME_0127"), NO_MESSAGE_ERROR("no.worker.message.received", "RUNTIME_0128"), - INVALID_METHOD_CALL("invalid.method.call", "RUNTIME_0129"), - INVALID_FUNCTION_INVOCATION("invalid.function.invocation.call", "RUNTIME_0130"), + FUNCTION_ALREADY_CALLED("function.already.called", "RUNTIME_0129"), + INVALID_FUNCTION_INVOCATION_BEFORE_MODULE_INIT("invalid.function.call.before.module.init", "RUNTIME_0130"), INVALID_TUPLE_MEMBER_SIZE("invalid.tuple.member.size", "RUNTIME_0131"); private final String errorMsgKey; diff --git a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties index 78ae3bef5376..86bea8f8fad4 100644 --- a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties +++ b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties @@ -263,8 +263,8 @@ 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 -invalid.method.call = ''{0}'' method is called before module initialization -invalid.function.invocation.call = function ''{0}'' is called before module initialization +function.already.called = function ''{0}'' is already called +invalid.function.call.before.module.init = function ''{0}'' is called before module initialization config.env.vars.ambiguity = configurable value for variable ''{0}'' clashes with multiple environment variables {1} config.env.variable.ambiguity = configurable value for variable ''{0}'' clashes with variable ''{1}''. Please \ provide the env variable as ''[{2}]'' From f0de161bc5a3c7f4c136c1ef484f491cf0755a3d Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Fri, 7 Jun 2024 14:22:38 +0530 Subject: [PATCH 2/8] Refactor runtime API tests --- .../runtime/api/ModuleStartCallNegative.java | 34 -------- .../test/runtime/api/RuntimeAPICall.java | 48 ++--------- .../runtime/api/RuntimeAPICallNegative.java | 51 +++++++++--- .../test/runtime/api/RuntimeAPITest.java | 60 ++++---------- .../test/runtime/api/RuntimeAPITestUtils.java | 82 +++++++++++++++++++ .../modules/moduleA/main.bal | 2 +- 6 files changed, 144 insertions(+), 133 deletions(-) delete mode 100644 tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/ModuleStartCallNegative.java create mode 100644 tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITestUtils.java diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/ModuleStartCallNegative.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/ModuleStartCallNegative.java deleted file mode 100644 index 07420535e470..000000000000 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/ModuleStartCallNegative.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2024, WSO2 LLC. (http://wso2.com). - * - * Licensed 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.runtime.api; - -import io.ballerina.runtime.api.Module; -import io.ballerina.runtime.api.Runtime; - -/** - * Source class to test the functionality of Ballerina runtime APIs for invoking functions. - * - * @since 2201.9.0 - */ -public class ModuleStartCallNegative { - - public static void main(String[] args) { - Module module = new Module("testorg", "function_invocation_negative", "1"); - Runtime balRuntime = Runtime.from(module); - balRuntime.start(); - } -} diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICall.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICall.java index 658025a8a8b1..a4bb7147658a 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICall.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICall.java @@ -19,13 +19,12 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.PredefinedTypes; import io.ballerina.runtime.api.Runtime; -import io.ballerina.runtime.api.async.Callback; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; -import java.io.PrintStream; +import static org.ballerinalang.test.runtime.api.RuntimeAPITestUtils.blockAndInvokeMethodAsync; +import static org.ballerinalang.test.runtime.api.RuntimeAPITestUtils.blockAndInvokeMethodAsyncSequentially; /** * Source class to test the functionality of Ballerina runtime APIs for invoking functions. @@ -34,57 +33,24 @@ */ public class RuntimeAPICall { - private static final PrintStream out = System.out; - public static void main(String[] args) { Module module = new Module("testorg", "function_invocation", "1"); Runtime balRuntime = Runtime.from(module); balRuntime.init(); balRuntime.start(); - balRuntime.invokeMethodAsync("add", - new Callback() { - @Override - public void notifySuccess(Object result) { - out.println(result); - } - - @Override - public void notifyFailure(BError error) { - out.println("Error: " + error); - } - }, 5L, 7L); + blockAndInvokeMethodAsync(balRuntime, "add", 5L, 7L); BObject person = ValueCreator.createObjectValue(module, "Person", 1001, StringUtils.fromString("John Doe")); - balRuntime.invokeMethodAsyncSequentially(person, "getNameWithTitle", null, null, new Callback() { - @Override - public void notifySuccess(Object result) { - out.println(result); - } - - @Override - public void notifyFailure(BError error) { - out.println("Error: " + error); - } - }, null, PredefinedTypes.TYPE_STRING, StringUtils.fromString("Dr. "), false); - + blockAndInvokeMethodAsyncSequentially(balRuntime, person, "getNameWithTitle", PredefinedTypes.TYPE_STRING, + StringUtils.fromString("Dr. "), false); balRuntime.stop(); balRuntime = Runtime.from(new Module("testorg", "function_invocation.moduleA", "1")); balRuntime.init(); balRuntime.start(); - balRuntime.invokeMethodAsync("getPerson", - new Callback() { - @Override - public void notifySuccess(Object result) { - out.println(result); - } - - @Override - public void notifyFailure(BError error) { - out.println("Error: " + error); - } - }, 1001L, StringUtils.fromString("John"), StringUtils.fromString("100m")); + blockAndInvokeMethodAsync(balRuntime, "getPerson", 1001L, StringUtils.fromString("John"), + StringUtils.fromString("100m")); balRuntime.stop(); } } diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java index a871563d9c98..4d7cd6866672 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java @@ -18,11 +18,12 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.Runtime; -import io.ballerina.runtime.api.async.Callback; import io.ballerina.runtime.api.values.BError; import java.io.PrintStream; +import static org.ballerinalang.test.runtime.api.RuntimeAPITestUtils.blockAndInvokeMethodAsync; + /** * Source class to test the functionality of Ballerina runtime APIs for invoking functions. * @@ -35,16 +36,42 @@ public class RuntimeAPICallNegative { public static void main(String[] args) { Module module = new Module("testorg", "function_invocation", "1"); Runtime balRuntime = Runtime.from(module); - balRuntime.invokeMethodAsync("add", new Callback() { - @Override - public void notifySuccess(Object result) { - out.println(result); - } - - @Override - public void notifyFailure(BError error) { - out.println("Error: " + error); - } - }); + + // Test function called before module initialization error for add, start and stop functions + try { + blockAndInvokeMethodAsync(balRuntime, "add"); + } catch (BError e) { + out.println(e.getMessage()); + } + try { + balRuntime.start(); + } catch (BError e) { + out.println(e.getMessage()); + } + try { + balRuntime.stop(); + } catch (BError e) { + out.println(e.getMessage()); + } + + // Test already called error for init, start and stop functions + balRuntime.init(); + try { + balRuntime.init(); + } catch (BError e) { + out.println(e.getMessage()); + } + balRuntime.start(); + try { + balRuntime.start(); + } catch (BError e) { + out.println(e.getMessage()); + } + balRuntime.stop(); + try { + balRuntime.stop(); + } catch (BError e) { + out.println(e.getMessage()); + } } } diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java index ab3f12fe4c4c..e18b613fce36 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java @@ -60,7 +60,7 @@ public void setup() throws BallerinaTestException { public void testRuntimeAPIsForBalFunctionInvocation() throws BallerinaTestException { Path jarPath = Paths.get(testFileLocation, "function_invocation", "target", "bin", "function_invocation.jar").toAbsolutePath(); - compileJavaSource(jarPath, "RuntimeAPICall.java", "targetDir"); + compileJavaSource(jarPath, "targetDir", "RuntimeAPICall.java", "RuntimeAPITestUtils.java"); unzipJarFile(jarPath, "targetDir"); createExecutableJar("targetDir", "org.ballerinalang.test.runtime.api.RuntimeAPICall"); Map envProperties = new HashMap<>(); @@ -101,7 +101,7 @@ public void testRuntimeAPIsForBalFunctionInvocation() throws BallerinaTestExcept public void testBalFunctionInvocationAPINegative() throws BallerinaTestException { Path jarPath = Paths.get(testFileLocation, "function_invocation", "target", "bin", "function_invocation.jar").toAbsolutePath(); - compileJavaSource(jarPath, "RuntimeAPICallNegative.java", "target-dir-negative"); + compileJavaSource(jarPath, "target-dir-negative", "RuntimeAPICallNegative.java", "RuntimeAPITestUtils.java"); unzipJarFile(jarPath, "target-dir-negative"); createExecutableJar("target-dir-negative", "org.ballerinalang.test.runtime.api.RuntimeAPICallNegative"); @@ -128,47 +128,15 @@ public void testBalFunctionInvocationAPINegative() throws BallerinaTestException List leechers = new ArrayList<>(); leechers.add(new LogLeecher("function 'add' is called before module initialization", LogLeecher.LeecherType.ERROR)); - addToServerInfoLogReader(serverInfoLogReader, leechers); - serverInfoLogReader.start(); - bMainInstance.waitForLeechers(leechers, 5000); - runProcess.waitFor(); - serverInfoLogReader.stop(); - serverInfoLogReader.removeAllLeechers(); - } catch (IOException | InterruptedException e) { - throw new BallerinaTestException("Error occurred while running the java file"); - } - } - - @Test - public void testModuleStartCallNegative() throws BallerinaTestException { - Path jarPath = Paths.get(testFileLocation, "function_invocation", "target", "bin", - "function_invocation.jar").toAbsolutePath(); - compileJavaSource(jarPath, "ModuleStartCallNegative.java", "start-call-negative"); - unzipJarFile(jarPath, "start-call-negative"); - createExecutableJar("start-call-negative", - "org.ballerinalang.test.runtime.api.ModuleStartCallNegative"); - Map envProperties = new HashMap<>(); - bMainInstance.addJavaAgents(envProperties); - - // Run the executable jar and assert the output - Path execJarPath = Paths.get(javaSrcLocation.toString(), "start-call-negative", - "test-exec.jar").toAbsolutePath(); - List runCmdSet = new ArrayList<>(); - runCmdSet.add("java"); - if (envProperties.containsKey(JAVA_OPTS)) { - runCmdSet.add(envProperties.get(JAVA_OPTS).trim()); - } - runCmdSet.add("-jar"); - runCmdSet.add(execJarPath.toString()); - ProcessBuilder runProcessBuilder = new ProcessBuilder(runCmdSet); - Map env = runProcessBuilder.environment(); - env.putAll(envProperties); - runProcessBuilder.redirectErrorStream(true); - try { - Process runProcess = runProcessBuilder.start(); - ServerLogReader serverInfoLogReader = new ServerLogReader("inputStream", runProcess.getInputStream()); - List leechers = new ArrayList<>(); - leechers.add(new LogLeecher("'start' method is called before module initialization", + leechers.add(new LogLeecher("function 'start' is called before module initialization", + LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'stop' is called before module initialization", + LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'init' is already called", + LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'start' is already called", + LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'stop' is already called", LogLeecher.LeecherType.ERROR)); addToServerInfoLogReader(serverInfoLogReader, leechers); serverInfoLogReader.start(); @@ -189,7 +157,7 @@ public void tearDown() { BFileUtil.deleteDirectory(Paths.get(javaSrcLocation.toString(), "start-call-negative").toFile()); } - private static void compileJavaSource(Path jarPath, String srcFile, String targetDir) + private static void compileJavaSource(Path jarPath, String targetDir, String... srcFiles) throws BallerinaTestException { List compileCmdSet = new ArrayList<>(); compileCmdSet.add("javac"); @@ -197,7 +165,9 @@ private static void compileJavaSource(Path jarPath, String srcFile, String targe compileCmdSet.add(jarPath.toString()); compileCmdSet.add("-d"); compileCmdSet.add(targetDir); - compileCmdSet.add(Paths.get(javaSrcLocation.toString(), srcFile).toString()); + for (String srcFile : srcFiles) { + compileCmdSet.add(Paths.get(javaSrcLocation.toString(), srcFile).toString()); + } ProcessBuilder compile = new ProcessBuilder(compileCmdSet).directory(javaSrcLocation.toFile()); try { Process process = compile.start(); diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITestUtils.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITestUtils.java new file mode 100644 index 000000000000..f8b27a622607 --- /dev/null +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITestUtils.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com). + * + * Licensed 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.runtime.api; + +import io.ballerina.runtime.api.Runtime; +import io.ballerina.runtime.api.async.Callback; +import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.api.values.BObject; + +import java.io.PrintStream; +import java.util.concurrent.CountDownLatch; + +/** + * Contains utils methods required for test Ballerina runtime APIs for invoking functions. + * + * @since 2201.9.1 + */ +public class RuntimeAPITestUtils { + + private static final PrintStream out = System.out; + + public static void blockAndInvokeMethodAsync(Runtime balRuntime, String functionName, Object... args) { + final CountDownLatch latch = new CountDownLatch(1); + balRuntime.invokeMethodAsync(functionName, new Callback() { + @Override + public void notifySuccess(Object result) { + out.println(result); + latch.countDown(); + } + + @Override + public void notifyFailure(BError error) { + out.println("Error: " + error); + latch.countDown(); + } + }, args); + try { + latch.await(); + } catch (InterruptedException e) { + throw ErrorCreator.createError(e); + } + } + + public static void blockAndInvokeMethodAsyncSequentially(Runtime balRuntime, BObject object, String functionName, + Type returnType, Object... args) { + final CountDownLatch latch = new CountDownLatch(1); + balRuntime.invokeMethodAsyncSequentially(object, functionName, null, null, new Callback() { + @Override + public void notifySuccess(Object result) { + latch.countDown(); + out.println(result); + } + + @Override + public void notifyFailure(BError error) { + latch.countDown(); + out.println("Error: " + error); + } + }, null, returnType, args); + try { + latch.await(); + } catch (InterruptedException e) { + throw ErrorCreator.createError(e); + } + } +} diff --git a/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/modules/moduleA/main.bal b/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/modules/moduleA/main.bal index 514154e42835..583c462ef840 100644 --- a/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/modules/moduleA/main.bal +++ b/tests/jballerina-integration-test/src/test/resources/runtime.api/function_invocation/modules/moduleA/main.bal @@ -14,7 +14,7 @@ // specific language governing permissions and limitations // under the License. -type Person record { +public type Person record { int id; string name; SportsActivity sportsActivity; From 5dc0eb3d193f7b280ac7beae8d0e4981363d2c44 Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Thu, 13 Jun 2024 11:41:34 +0530 Subject: [PATCH 3/8] Fix review suggestions --- .../java/io/ballerina/runtime/internal/BalRuntime.java | 7 +++---- .../src/main/resources/MessagesBundle.properties | 2 +- .../org/ballerinalang/test/runtime/api/RuntimeAPITest.java | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java index dd1ccf6af5a5..26ce32d5df2b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java @@ -145,8 +145,8 @@ private void invokeMethodAsync(String functionName, Callback callback, Type retu try { schedulerThread.join(); } catch (InterruptedException e) { - throw ErrorCreator.createError(StringUtils.fromString("ballerina: error occurred in while waiting for " + - "scheduler thread to finish"), e); + throw ErrorCreator.createError(StringUtils.fromString("error occurred while waiting for the scheduler " + + "thread to finish"), e); } invokeModuleStop(); moduleStopped = true; @@ -389,9 +389,8 @@ private void invokeModuleStop() { try { final Method method = initClass.getDeclaredMethod(funcName, RuntimeRegistry.class); method.invoke(null, scheduler.getRuntimeRegistry()); - } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - throw ErrorCreator.createError(StringUtils.fromString("calling module stop failed due to " + + throw ErrorCreator.createError(StringUtils.fromString("failed to stop the module due to " + RuntimeUtils.formatErrorMessage(e)), e); } } diff --git a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties index 86bea8f8fad4..a7caa9feeb3d 100644 --- a/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties +++ b/bvm/ballerina-runtime/src/main/resources/MessagesBundle.properties @@ -263,7 +263,7 @@ 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 -function.already.called = function ''{0}'' is already called +function.already.called = function ''{0}'' has already been called invalid.function.call.before.module.init = function ''{0}'' is called before module initialization config.env.vars.ambiguity = configurable value for variable ''{0}'' clashes with multiple environment variables {1} config.env.variable.ambiguity = configurable value for variable ''{0}'' clashes with variable ''{1}''. Please \ diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java index e18b613fce36..ee3609340ffc 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java @@ -132,11 +132,11 @@ public void testBalFunctionInvocationAPINegative() throws BallerinaTestException LogLeecher.LeecherType.ERROR)); leechers.add(new LogLeecher("function 'stop' is called before module initialization", LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'init' is already called", + leechers.add(new LogLeecher("function 'init' has already been called", LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'start' is already called", + leechers.add(new LogLeecher("function 'start' has already been called", LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'stop' is already called", + leechers.add(new LogLeecher("function 'stop' has already been called", LogLeecher.LeecherType.ERROR)); addToServerInfoLogReader(serverInfoLogReader, leechers); serverInfoLogReader.start(); From ba131f931788ad062e8f7727d2cc877fc4c4475e Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Mon, 1 Jul 2024 17:21:43 +0530 Subject: [PATCH 4/8] Refactor current module stop logic --- .../runtime/internal/BalRuntime.java | 65 ++------- .../internal/scheduling/SyncCallback.java | 48 +++++++ .../compiler/bir/codegen/JvmConstants.java | 1 + .../compiler/bir/codegen/JvmPackageGen.java | 3 +- .../compiler/bir/codegen/JvmSignatures.java | 1 + .../bir/codegen/ShutDownListenerGen.java | 131 +----------------- .../bir/codegen/methodgen/InitMethodGen.java | 126 ++++++++++++++++- 7 files changed, 190 insertions(+), 185 deletions(-) create mode 100644 bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/SyncCallback.java diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java index 26ce32d5df2b..9a2b17ba163a 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java @@ -41,6 +41,7 @@ import io.ballerina.runtime.internal.scheduling.RuntimeRegistry; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; +import io.ballerina.runtime.internal.scheduling.SyncCallback; import io.ballerina.runtime.internal.util.RuntimeUtils; import io.ballerina.runtime.internal.values.FutureValue; import io.ballerina.runtime.internal.values.ObjectValue; @@ -122,22 +123,6 @@ public void stop() { if (!moduleInitialized) { throw ErrorHelper.getRuntimeException(ErrorCodes.INVALID_FUNCTION_INVOCATION_BEFORE_MODULE_INIT, "stop"); } -<<<<<<< HEAD - invokeMethodAsync("$moduleStop", null, PredefinedTypes.TYPE_NULL, "stop", new Scheduler(false), null); - } - - private void invokeMethodAsync(String functionName, Callback callback, Type returnType, String strandName, - Object... args) { - ValueCreator valueCreator = ValueCreator.getValueCreator(ValueCreator.getLookupKey(module.getOrg(), - module.getName(), module.getMajorVersion(), module.isTestPkg())); - Function func = o -> valueCreator.call((Strand) (((Object[]) o)[0]), functionName, args); - FutureValue future = scheduler.createFuture(null, callback, null, returnType, strandName, null); - Object[] argsWithStrand = new Object[args.length + 1]; - argsWithStrand[0] = future.strand; - System.arraycopy(args, 0, argsWithStrand, 1, args.length); - scheduler.schedule(argsWithStrand, func, future); - scheduler.start(); -======= if (moduleStopped) { throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "stop"); } @@ -150,7 +135,6 @@ private void invokeMethodAsync(String functionName, Callback callback, Type retu } invokeModuleStop(); moduleStopped = true; ->>>>>>> 6f23b0a1586 (Fix blocking issue with invokeAsync API) } /** @@ -384,27 +368,26 @@ private void invokeConfigInit() { } private void invokeModuleStop() { - Class initClass = loadClass(MODULE_INIT_CLASS_NAME); - String funcName = Utils.encodeFunctionIdentifier("$moduleStop"); + Class configClass = loadClass(MODULE_INIT_CLASS_NAME); try { - final Method method = initClass.getDeclaredMethod(funcName, RuntimeRegistry.class); + final Method method = + configClass.getDeclaredMethod("$currentModuleStop", RuntimeRegistry.class); method.invoke(null, scheduler.getRuntimeRegistry()); } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - throw ErrorCreator.createError(StringUtils.fromString("failed to stop the module due to " + + throw ErrorCreator.createError(StringUtils.fromString("configurable initialization failed due to " + RuntimeUtils.formatErrorMessage(e)), e); } } - private Class loadClass(String moduleInitClassName) { - String initClassName = getFullQualifiedClassName(this.module, moduleInitClassName); - Class initClazz; + private Class loadClass(String className) { + String name = getFullQualifiedClassName(this.module, className); + Class clazz; try { - initClazz = Class.forName(initClassName); + clazz = Class.forName(name); } catch (Throwable e) { - throw ErrorCreator.createError(StringUtils.fromString("failed to load configuration class :" + - initClassName), e); + throw ErrorCreator.createError(StringUtils.fromString("failed to load configuration class :" + name), e); } - return initClazz; + return clazz; } private static String getFullQualifiedClassName(Module module, String className) { @@ -444,30 +427,4 @@ private void invokeMethod(String functionName, Callback callback, Type returnTyp System.arraycopy(args, 0, argsWithStrand, 1, args.length); scheduler.schedule(argsWithStrand, func, future); } - - /** - * This class used to handle ballerina function invocation synchronously. - * - * @since 2201.9.1 - */ - static class SyncCallback implements Callback { - - CountDownLatch latch; - BError initError; - - public SyncCallback(CountDownLatch latch) { - this.latch = latch; - } - - @Override - public void notifySuccess(Object result) { - latch.countDown(); - } - - @Override - public void notifyFailure(BError error) { - latch.countDown(); - initError = error; - } - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/SyncCallback.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/SyncCallback.java new file mode 100644 index 000000000000..34b6987c7c08 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/scheduling/SyncCallback.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com) + * + * Licensed 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.api.async.Callback; +import io.ballerina.runtime.api.values.BError; + +import java.util.concurrent.CountDownLatch; + +/** + * This class used to handle ballerina function invocation synchronously. + * + * @since 2201.9.1 + */ +public class SyncCallback implements Callback { + + public CountDownLatch latch; + public BError initError; + + public SyncCallback(CountDownLatch latch) { + this.latch = latch; + } + + @Override + public void notifySuccess(Object result) { + latch.countDown(); + } + + @Override + public void notifyFailure(BError error) { + latch.countDown(); + initError = error; + } +} 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 9b3903fb4043..a685a4eafa6d 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 @@ -322,6 +322,7 @@ public class JvmConstants { public static final String CONSTANT_INIT_METHOD_PREFIX = "$constant_init"; public static final String ANNOTATIONS_METHOD_PREFIX = "$process_annotations"; public static final String CURRENT_MODULE_INIT = "$currentModuleInit"; + public static final String CURRENT_MODULE_STOP = "$currentModuleStop"; public static final String MODULE_INIT_METHOD = "$moduleInit"; public static final String MODULE_START_METHOD = "$moduleStart"; public static final String MODULE_STOP_METHOD = "$moduleStop"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java index 4b83a7c881bc..f5f682f36b6d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java @@ -408,6 +408,7 @@ private void generateModuleClasses(BIRPackage module, Map jarEnt generateLockForVariable(cw); initMethodGen.generateModuleInitializer(cw, module, moduleInitClass, typesClass); + initMethodGen.generateModuleStop(cw, moduleInitClass, asyncDataCollector, jvmConstantsGen); ModuleStopMethodGen stopMethodGen = new ModuleStopMethodGen(jvmTypeGen, jvmConstantsGen); stopMethodGen.generateExecutionStopMethod(cw, moduleInitClass, module, asyncDataCollector, immediateImports); @@ -743,7 +744,7 @@ CompiledJarFile generate(BIRPackage module) { typeHashVisitor, jarEntries, symbolTable); // generate the shutdown listener class. - new ShutDownListenerGen(jvmConstantsGen).generateShutdownSignalListener(moduleInitClass, jarEntries, + new ShutDownListenerGen().generateShutdownSignalListener(moduleInitClass, jarEntries, asyncDataCollector); removeSourceAnnotationTypeDefs(module.typeDefs); 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 42f7b04cc9cb..905e422db221 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 @@ -192,6 +192,7 @@ public class JvmSignatures { public static final String CREATE_XML_PI = "(L" + B_STRING_VALUE + ";L" + B_STRING_VALUE + ";Z)L" + XML_VALUE + ";"; public static final String CREATE_XML_TEXT = "(L" + B_STRING_VALUE + ";)L" + XML_VALUE + ";"; public static final String CRETAE_XML_SEQUENCE = "()L" + XML_SEQUENCE + ";"; + public static final String CURRENT_MODULE_STOP = "(L" + RUNTIME_REGISTRY_CLASS + ";)V"; public static final String DECIMAL_NEGATE = "()L" + DECIMAL_VALUE + ";"; public static final String DECIMAL_TO_HANDLE = "(L" + OBJECT + ";)L" + HANDLE_VALUE + ";"; public static final String DECIMAL_VALUE_OF_BOOLEAN = "(B)L" + DECIMAL_VALUE + ";"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java index bb0d9c2d9927..aad198b2aaf6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java @@ -20,69 +20,29 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; -import org.objectweb.asm.Opcodes; import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; -import org.wso2.ballerinalang.compiler.bir.codegen.methodgen.MethodGenUtils; -import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import java.util.Map; import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; -import static org.objectweb.asm.Opcodes.AALOAD; -import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ACC_PRIVATE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SUPER; -import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; -import static org.objectweb.asm.Opcodes.ANEWARRAY; -import static org.objectweb.asm.Opcodes.ASTORE; -import static org.objectweb.asm.Opcodes.BIPUSH; -import static org.objectweb.asm.Opcodes.CHECKCAST; -import static org.objectweb.asm.Opcodes.DUP; import static org.objectweb.asm.Opcodes.GETFIELD; -import static org.objectweb.asm.Opcodes.GETSTATIC; -import static org.objectweb.asm.Opcodes.ICONST_0; -import static org.objectweb.asm.Opcodes.ICONST_1; -import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; -import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static org.objectweb.asm.Opcodes.NEW; import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.RETURN; import static org.objectweb.asm.Opcodes.V17; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CLASS_FILE_SUFFIX; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUTURE_VALUE; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.HANDLE_STOP_PANIC_METHOD; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CURRENT_MODULE_STOP; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.JAVA_THREAD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.JVM_INIT_METHOD; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.LAMBDA_PREFIX; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_STOP_METHOD; -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.RUNTIME_REGISTRY_CLASS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RUNTIME_REGISTRY_VARIABLE; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RUNTIME_UTILS; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER_START_METHOD; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STACK; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND_CLASS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_RUNTIME_REGISTRY; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_STRAND; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_THROWABLE; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.HANDLE_STOP_PANIC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_RUNTIME_REGISTRY; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LAMBDA_STOP_DYNAMIC; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_NULL_TYPE; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MODULE_STOP; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.SET_STRAND; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.STACK_FRAMES; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.VOID_METHOD_DESC; /** @@ -91,11 +51,6 @@ * @since 2.0.0 */ public class ShutDownListenerGen { - private final String strandMetadataClass; - - public ShutDownListenerGen(JvmConstantsGen jvmConstantsGen) { - this.strandMetadataClass = jvmConstantsGen.getStrandMetadataConstantsClass(); - } void generateShutdownSignalListener(String initClass, Map jarEntries, AsyncDataCollector asyncDataCollector) { @@ -123,8 +78,7 @@ private void genConstructor(String innerClassName, ClassWriter cw) { mv.visitMethodInsn(INVOKESPECIAL, JAVA_THREAD, JVM_INIT_METHOD, VOID_METHOD_DESC, false); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, innerClassName , RUNTIME_REGISTRY_VARIABLE, - GET_RUNTIME_REGISTRY); + mv.visitFieldInsn(PUTFIELD, innerClassName, RUNTIME_REGISTRY_VARIABLE, GET_RUNTIME_REGISTRY); mv.visitInsn(RETURN); JvmCodeGenUtil.visitMaxStackForMethod(mv, JVM_INIT_METHOD, innerClassName); mv.visitEnd(); @@ -136,88 +90,11 @@ private void genRunMethod(String initClass, String innerClassName, ClassWriter c mv.visitCode(); // Create a scheduler. A new scheduler is used here, to make the stop function to not // depend/wait on whatever is being running on the background. eg: a busy loop in the main. - mv.visitTypeInsn(NEW, SCHEDULER); - mv.visitInsn(DUP); - mv.visitInsn(ICONST_1); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESPECIAL, SCHEDULER, JVM_INIT_METHOD, "(IZ)V", false); - mv.visitVarInsn(ASTORE, 1); // Scheduler var1 - String lambdaName = generateStopDynamicLambdaBody(cw, initClass); - generateCallStopDynamicLambda(mv, lambdaName, initClass, asyncDataCollector); mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 3); - mv.visitMethodInsn(INVOKESTATIC, initClass, MODULE_STOP_METHOD, MODULE_STOP, false); + mv.visitFieldInsn(GETFIELD, innerClassName, RUNTIME_REGISTRY_VARIABLE, GET_RUNTIME_REGISTRY); + mv.visitMethodInsn(INVOKESTATIC, initClass, CURRENT_MODULE_STOP, JvmSignatures.CURRENT_MODULE_STOP, false); mv.visitInsn(RETURN); JvmCodeGenUtil.visitMaxStackForMethod(mv, "run", innerClassName); mv.visitEnd(); } - - private String generateStopDynamicLambdaBody(ClassWriter cw, String initClass) { - String lambdaName = LAMBDA_PREFIX + "stopdynamic"; - MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + ACC_STATIC, lambdaName, LAMBDA_STOP_DYNAMIC, null, null); - mv.visitCode(); - MethodGenUtils.callSetDaemonStrand(mv); - generateCallSchedulerStopDynamicListeners(mv, lambdaName, initClass); - return lambdaName; - } - - private void generateCallSchedulerStopDynamicListeners(MethodVisitor mv, String lambdaName, String initClass) { - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_1); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, RUNTIME_REGISTRY_CLASS); - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); - mv.visitTypeInsn(CHECKCAST, STRAND_CLASS); - mv.visitMethodInsn(INVOKEVIRTUAL, RUNTIME_REGISTRY_CLASS, "gracefulStop", SET_STRAND, false); - mv.visitInsn(ACONST_NULL); - MethodGenUtils.visitReturn(mv, lambdaName, initClass); - } - - private void generateCallStopDynamicLambda(MethodVisitor mv, String lambdaName, String moduleInitClass, - AsyncDataCollector asyncDataCollector) { - addRuntimeRegistryAsParameter(mv, moduleInitClass + "$SignalListener"); - generateMethodBody(mv, moduleInitClass, lambdaName, asyncDataCollector); - // handle any runtime errors - Label labelIf = new Label(); - mv.visitVarInsn(ALOAD, 3); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); - mv.visitJumpInsn(IFNULL, labelIf); - mv.visitVarInsn(ALOAD, 3); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); - mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, HANDLE_STOP_PANIC_METHOD, HANDLE_STOP_PANIC, false); - mv.visitLabel(labelIf); - } - - private void addRuntimeRegistryAsParameter(MethodVisitor mv, String innerClassName) { - mv.visitIntInsn(BIPUSH, 2); - mv.visitTypeInsn(ANEWARRAY, OBJECT); - mv.visitVarInsn(ASTORE, 2); - mv.visitVarInsn(ALOAD, 2); - mv.visitInsn(ICONST_1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, innerClassName, RUNTIME_REGISTRY_VARIABLE, GET_RUNTIME_REGISTRY); - mv.visitInsn(AASTORE); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - } - - private void generateMethodBody(MethodVisitor mv, String initClass, String stopFuncName, - AsyncDataCollector asyncDataCollector) { - JvmCodeGenUtil.createFunctionPointer(mv, initClass + "$SignalListener", stopFuncName); - mv.visitInsn(ACONST_NULL); - mv.visitFieldInsn(GETSTATIC, PREDEFINED_TYPES, "TYPE_NULL", LOAD_NULL_TYPE); - MethodGenUtils.submitToScheduler(mv, this.strandMetadataClass, "stop", asyncDataCollector); - mv.visitVarInsn(ASTORE, 3); - mv.visitVarInsn(ALOAD, 3); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, STRAND, GET_STRAND); - mv.visitTypeInsn(NEW, STACK); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, STACK, JVM_INIT_METHOD, VOID_METHOD_DESC, false); - mv.visitFieldInsn(PUTFIELD, STRAND_CLASS, MethodGenUtils.FRAMES, STACK_FRAMES); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, SCHEDULER, SCHEDULER_START_METHOD, VOID_METHOD_DESC, false); - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java index d8a7ab46017f..4393d2ae2c46 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java @@ -21,15 +21,19 @@ import io.ballerina.identifier.Utils; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCastGen; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil; import org.wso2.ballerinalang.compiler.bir.codegen.JvmPackageGen; import org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures; +import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; import org.wso2.ballerinalang.compiler.bir.codegen.internal.JavaClass; import org.wso2.ballerinalang.compiler.bir.codegen.model.BIRFunctionWrapper; import org.wso2.ballerinalang.compiler.bir.codegen.model.JIMethodCLICall; import org.wso2.ballerinalang.compiler.bir.codegen.model.JIMethodCall; +import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; import org.wso2.ballerinalang.compiler.bir.model.BIRNonTerminator; import org.wso2.ballerinalang.compiler.bir.model.BIROperand; @@ -55,10 +59,12 @@ import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.objectweb.asm.Opcodes.AALOAD; +import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACONST_NULL; import static org.objectweb.asm.Opcodes.ALOAD; +import static org.objectweb.asm.Opcodes.ANEWARRAY; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.BIPUSH; import static org.objectweb.asm.Opcodes.CHECKCAST; @@ -67,24 +73,38 @@ import static org.objectweb.asm.Opcodes.GETSTATIC; import static org.objectweb.asm.Opcodes.ICONST_0; import static org.objectweb.asm.Opcodes.ICONST_1; +import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.INVOKESPECIAL; import static org.objectweb.asm.Opcodes.INVOKESTATIC; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.LRETURN; import static org.objectweb.asm.Opcodes.NEW; +import static org.objectweb.asm.Opcodes.PUTFIELD; import static org.objectweb.asm.Opcodes.RETURN; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CLI_SPEC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_TYPES_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CURRENT_MODULE_INIT; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CURRENT_MODULE_STOP; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUTURE_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.GET_TEST_EXECUTION_STATE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.GRACEFUL_EXIT_METHOD_NAME; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.HANDLE_STOP_PANIC_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.JVM_INIT_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.LAMBDA_PREFIX; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MAIN_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_EXECUTE_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_INIT_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_START_METHOD; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_STOP_METHOD; +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.RUNTIME_REGISTRY_CLASS; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RUNTIME_UTILS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER_START_METHOD; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STACK; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.STRAND_CLASS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TEST_EXECUTE_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.TEST_EXECUTION_STATE; @@ -92,10 +112,16 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.ADD_VALUE_CREATOR; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_MAIN_ARGS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_SCHEDULER; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_STRAND; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_THROWABLE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GRACEFUL_EXIT_METHOD; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.HANDLE_STOP_PANIC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LAMBDA_STOP_DYNAMIC; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_NULL_TYPE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MODULE_STOP; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.RETURN_OBJECT; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.SET_STRAND; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.STACK_FRAMES; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.VOID_METHOD_DESC; import static org.wso2.ballerinalang.compiler.util.CompilerUtils.getMajorVersion; @@ -195,14 +221,13 @@ private void invokeStopFunction(String initClass, MethodVisitor mv, String metho } public void generateModuleInitializer(ClassWriter cw, BIRNode.BIRPackage module, String typeOwnerClass, - String moduleTypeClass) { + String moduleInitClass) { // Using object return type since this is similar to a ballerina function without a return. // A ballerina function with no returns is equivalent to a function with nil-return. MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, CURRENT_MODULE_INIT, RETURN_OBJECT, null, null); mv.visitCode(); - - mv.visitMethodInsn(INVOKESTATIC, moduleTypeClass, CREATE_TYPES_METHOD, VOID_METHOD_DESC, false); + mv.visitMethodInsn(INVOKESTATIC, moduleInitClass, CREATE_TYPES_METHOD, VOID_METHOD_DESC, false); mv.visitTypeInsn(NEW, typeOwnerClass); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, typeOwnerClass, JVM_INIT_METHOD, VOID_METHOD_DESC, false); @@ -223,6 +248,101 @@ public void generateModuleInitializer(ClassWriter cw, BIRNode.BIRPackage module, MethodGenUtils.visitReturn(mv, CURRENT_MODULE_INIT, typeOwnerClass); } + public void generateModuleStop(ClassWriter cw, String moduleInitClass, AsyncDataCollector asyncDataCollector, + JvmConstantsGen jvmConstantsGen) { + // Using object return type since this is similar to a ballerina function without a return. + // A ballerina function with no returns is equivalent to a function with nil-return. + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, CURRENT_MODULE_STOP, + JvmSignatures.CURRENT_MODULE_STOP, null, null); + mv.visitCode(); + + // Create a scheduler. A new scheduler is used here, to make the stop function to not + // depend/wait on whatever is being running on the background. eg: a busy loop in the main. + mv.visitTypeInsn(NEW, SCHEDULER); + mv.visitInsn(DUP); + mv.visitInsn(ICONST_1); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESPECIAL, SCHEDULER, JVM_INIT_METHOD, "(IZ)V", false); + mv.visitVarInsn(ASTORE, 1); // Scheduler var1 + String lambdaName = generateStopDynamicLambdaBody(cw, moduleInitClass); + generateCallStopDynamicLambda(mv, lambdaName, moduleInitClass, asyncDataCollector, jvmConstantsGen); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKESTATIC, moduleInitClass, MODULE_STOP_METHOD, MODULE_STOP, false); + mv.visitInsn(RETURN); + JvmCodeGenUtil.visitMaxStackForMethod(mv, CURRENT_MODULE_STOP, moduleInitClass); + mv.visitEnd(); + } + + private String generateStopDynamicLambdaBody(ClassWriter cw, String initClass) { + String lambdaName = LAMBDA_PREFIX + "stopdynamic"; + MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC + ACC_STATIC, lambdaName, LAMBDA_STOP_DYNAMIC, null, null); + mv.visitCode(); + MethodGenUtils.callSetDaemonStrand(mv); + generateCallSchedulerStopDynamicListeners(mv, lambdaName, initClass); + return lambdaName; + } + + private void generateCallSchedulerStopDynamicListeners(MethodVisitor mv, String lambdaName, String initClass) { + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_1); + mv.visitInsn(AALOAD); + mv.visitTypeInsn(CHECKCAST, RUNTIME_REGISTRY_CLASS); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_0); + mv.visitInsn(AALOAD); + mv.visitTypeInsn(CHECKCAST, STRAND_CLASS); + mv.visitMethodInsn(INVOKEVIRTUAL, RUNTIME_REGISTRY_CLASS, "gracefulStop", SET_STRAND, false); + mv.visitInsn(ACONST_NULL); + MethodGenUtils.visitReturn(mv, lambdaName, initClass); + } + + private void generateCallStopDynamicLambda(MethodVisitor mv, String lambdaName, String moduleInitClass, + AsyncDataCollector asyncDataCollector, JvmConstantsGen jvmConstantsGen) { + addRuntimeRegistryAsParameter(mv); + generateMethodBody(mv, moduleInitClass, lambdaName, asyncDataCollector, jvmConstantsGen); + // handle any runtime errors + Label labelIf = new Label(); + mv.visitVarInsn(ALOAD, 3); + mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); + mv.visitJumpInsn(IFNULL, labelIf); + mv.visitVarInsn(ALOAD, 3); + mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); + mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, HANDLE_STOP_PANIC_METHOD, HANDLE_STOP_PANIC, false); + mv.visitLabel(labelIf); + } + + private void addRuntimeRegistryAsParameter(MethodVisitor mv) { + mv.visitIntInsn(BIPUSH, 2); + mv.visitTypeInsn(ANEWARRAY, OBJECT); + mv.visitVarInsn(ASTORE, 2); + mv.visitVarInsn(ALOAD, 2); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(AASTORE); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + } + + private void generateMethodBody(MethodVisitor mv, String initClass, String stopFuncName, + AsyncDataCollector asyncDataCollector, JvmConstantsGen jvmConstantsGen) { + JvmCodeGenUtil.createFunctionPointer(mv, initClass, stopFuncName); + mv.visitInsn(ACONST_NULL); + mv.visitFieldInsn(GETSTATIC, PREDEFINED_TYPES, "TYPE_NULL", LOAD_NULL_TYPE); + MethodGenUtils.submitToScheduler(mv, jvmConstantsGen.getStrandMetadataConstantsClass(), "stop", + asyncDataCollector); + mv.visitVarInsn(ASTORE, 3); + mv.visitVarInsn(ALOAD, 3); + mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, STRAND, GET_STRAND); + mv.visitTypeInsn(NEW, STACK); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, STACK, JVM_INIT_METHOD, VOID_METHOD_DESC, false); + mv.visitFieldInsn(PUTFIELD, STRAND_CLASS, MethodGenUtils.FRAMES, STACK_FRAMES); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, SCHEDULER, SCHEDULER_START_METHOD, VOID_METHOD_DESC, false); + } + public void enrichPkgWithInitializers(Map birFunctionMap, Map jvmClassMap, String typeOwnerClass, BIRNode.BIRPackage pkg, Set moduleImports, From 8179645723f68fe82ab161b834d0f979e344c5f4 Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Mon, 1 Jul 2024 22:08:25 +0530 Subject: [PATCH 5/8] Refactor module stop for tests --- .../compiler/bir/codegen/JvmPackageGen.java | 4 +- .../bir/codegen/ShutDownListenerGen.java | 9 +-- .../bir/codegen/methodgen/MainMethodGen.java | 70 +------------------ 3 files changed, 8 insertions(+), 75 deletions(-) diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java index f5f682f36b6d..d82e37860490 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java @@ -744,8 +744,8 @@ CompiledJarFile generate(BIRPackage module) { typeHashVisitor, jarEntries, symbolTable); // generate the shutdown listener class. - new ShutDownListenerGen().generateShutdownSignalListener(moduleInitClass, jarEntries, - asyncDataCollector); + new ShutDownListenerGen().generateShutdownSignalListener(moduleInitClass, jarEntries + ); removeSourceAnnotationTypeDefs(module.typeDefs); // desugar the record init function diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java index aad198b2aaf6..4dfbf629fd3e 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/ShutDownListenerGen.java @@ -21,7 +21,6 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; -import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; import java.util.Map; @@ -52,8 +51,7 @@ */ public class ShutDownListenerGen { - void generateShutdownSignalListener(String initClass, Map jarEntries, - AsyncDataCollector asyncDataCollector) { + void generateShutdownSignalListener(String initClass, Map jarEntries) { String innerClassName = initClass + "$SignalListener"; ClassWriter cw = new BallerinaClassWriter(COMPUTE_FRAMES); cw.visit(V17, ACC_SUPER, innerClassName, null, JAVA_THREAD, null); @@ -65,7 +63,7 @@ void generateShutdownSignalListener(String initClass, Map jarEnt genConstructor(innerClassName, cw); // implement run() method - genRunMethod(initClass, innerClassName, cw, asyncDataCollector); + genRunMethod(initClass, innerClassName, cw); cw.visitEnd(); jarEntries.put(innerClassName + CLASS_FILE_SUFFIX, cw.toByteArray()); @@ -84,8 +82,7 @@ private void genConstructor(String innerClassName, ClassWriter cw) { mv.visitEnd(); } - private void genRunMethod(String initClass, String innerClassName, ClassWriter cw, - AsyncDataCollector asyncDataCollector) { + private void genRunMethod(String initClass, String innerClassName, ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "run", VOID_METHOD_DESC, null, null); mv.visitCode(); // Create a scheduler. A new scheduler is used here, to make the stop function to not diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java index 8d3c240960f0..cd27923ed8c3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MainMethodGen.java @@ -26,6 +26,7 @@ import org.objectweb.asm.Opcodes; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil; import org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants; +import org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures; import org.wso2.ballerinalang.compiler.bir.codegen.JvmTypeGen; import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; import org.wso2.ballerinalang.compiler.bir.codegen.internal.BIRVarToJVMIndexMap; @@ -68,6 +69,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CONFIGURATION_CLASS_NAME; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CONFIGURE_INIT; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CONFIG_DETAILS; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CURRENT_MODULE_STOP; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CURRENT_MODULE_VAR_NAME; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUTURE_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.HANDLE_ALL_THROWABLE_METHOD; @@ -82,12 +84,10 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MAIN_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_EXECUTE_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_INIT_CLASS_NAME; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.MODULE_STOP_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OBJECT; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OPERAND; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.OPTION; 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.REPOSITORY_IMPL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.RUNTIME_UTILS; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.SCHEDULER; @@ -115,7 +115,6 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_TEST_CONFIG_PATH; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.GET_THROWABLE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.HANDLE_ERROR_RETURN; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.HANDLE_STOP_PANIC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.HANDLE_THROWABLE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_CLI_SPEC; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_CONFIG; @@ -124,14 +123,10 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_OPTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_RUNTIME_REGISTRY; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_TEST_ARGS; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_NULL_TYPE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MAIN_METHOD_SIGNATURE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.METHOD_STRING_PARAM; -import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.MODULE_STOP; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.STACK_FRAMES; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.VOID_METHOD_DESC; -import static org.wso2.ballerinalang.compiler.bir.codegen.methodgen.ModuleStopMethodGen.ARR_VAR; -import static org.wso2.ballerinalang.compiler.bir.codegen.methodgen.ModuleStopMethodGen.FUTURE_VAR; /** * Generates Jvm byte code for the main method. @@ -240,68 +235,9 @@ private void generateExecuteFunctionCall(String initClass, MethodVisitor mv, BIR } private void generateModuleStopCall(String initClass, MethodVisitor mv) { - String lambdaName = LAMBDA_PREFIX + "stopdynamic"; - mv.visitTypeInsn(NEW, SCHEDULER); - mv.visitInsn(DUP); - mv.visitInsn(ICONST_1); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESPECIAL, SCHEDULER, JVM_INIT_METHOD, "(IZ)V", false); - mv.visitVarInsn(ASTORE, indexMap.addIfNotExists("newSchedulerVar", symbolTable.anyType)); - generateCallStopDynamicLambda(mv, lambdaName, initClass, asyncDataCollector); - mv.visitVarInsn(ALOAD, indexMap.get("newSchedulerVar")); - mv.visitVarInsn(ALOAD, indexMap.get(FUTURE_VAR)); - mv.visitMethodInsn(INVOKESTATIC, initClass, MODULE_STOP_METHOD, MODULE_STOP, false); - } - - private void generateCallStopDynamicLambda(MethodVisitor mv, String lambdaName, String moduleInitClass, - AsyncDataCollector asyncDataCollector) { - addRuntimeRegistryAsParameter(mv); - int futureIndex = indexMap.addIfNotExists(FUTURE_VAR, symbolTable.anyType); - generateMethodBody(mv, moduleInitClass, lambdaName, asyncDataCollector); - // handle any runtime errors - Label labelIf = new Label(); - mv.visitVarInsn(ALOAD, futureIndex); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); - mv.visitJumpInsn(IFNULL, labelIf); - mv.visitVarInsn(ALOAD, futureIndex); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, PANIC_FIELD, GET_THROWABLE); - mv.visitMethodInsn(INVOKESTATIC, RUNTIME_UTILS, HANDLE_STOP_PANIC_METHOD, HANDLE_STOP_PANIC, - false); - mv.visitLabel(labelIf); - } - - private void addRuntimeRegistryAsParameter(MethodVisitor mv) { - int arrIndex = indexMap.addIfNotExists(ARR_VAR, symbolTable.anyType); - mv.visitIntInsn(BIPUSH, 2); - mv.visitTypeInsn(ANEWARRAY, OBJECT); - mv.visitVarInsn(ASTORE, arrIndex); - mv.visitVarInsn(ALOAD, arrIndex); - mv.visitInsn(ICONST_1); mv.visitVarInsn(ALOAD, indexMap.get(SCHEDULER_VAR)); mv.visitMethodInsn(INVOKEVIRTUAL, SCHEDULER, "getRuntimeRegistry", GET_RUNTIME_REGISTRY_CLASS, false); - mv.visitInsn(AASTORE); - mv.visitVarInsn(ALOAD, indexMap.get("newSchedulerVar")); - mv.visitVarInsn(ALOAD, arrIndex); - } - - private void generateMethodBody(MethodVisitor mv, String initClass, String stopFuncName, - AsyncDataCollector asyncDataCollector) { - JvmCodeGenUtil.createFunctionPointer(mv, initClass + "$SignalListener", stopFuncName); - mv.visitInsn(ACONST_NULL); - mv.visitFieldInsn(GETSTATIC, PREDEFINED_TYPES, "TYPE_NULL", LOAD_NULL_TYPE); - MethodGenUtils.submitToScheduler(mv, this.strandMetadataClass, "stop", asyncDataCollector); - int futureIndex = indexMap.get(FUTURE_VAR); - mv.visitVarInsn(ASTORE, futureIndex); - mv.visitVarInsn(ALOAD, futureIndex); - mv.visitFieldInsn(GETFIELD, FUTURE_VALUE, STRAND, GET_STRAND); - mv.visitTypeInsn(NEW, STACK); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, STACK, JVM_INIT_METHOD, VOID_METHOD_DESC, false); - mv.visitFieldInsn(PUTFIELD, STRAND_CLASS, MethodGenUtils.FRAMES, STACK_FRAMES); - int schedulerIndex = indexMap.get("newSchedulerVar"); - mv.visitVarInsn(ALOAD, schedulerIndex); - mv.visitMethodInsn(INVOKEVIRTUAL, SCHEDULER, SCHEDULER_START_METHOD, VOID_METHOD_DESC, false); - + mv.visitMethodInsn(INVOKESTATIC, initClass, CURRENT_MODULE_STOP, JvmSignatures.CURRENT_MODULE_STOP, false); } private void startScheduler(int schedulerVarIndex, MethodVisitor mv) { From a6edbb8be89047d25169b2cb6512492edf841d5d Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Tue, 2 Jul 2024 13:47:33 +0530 Subject: [PATCH 6/8] Improve error handling in runtime api --- .../runtime/internal/BalRuntime.java | 75 +++++++++---------- 1 file changed, 34 insertions(+), 41 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java index 9a2b17ba163a..83b99a1cd180 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java @@ -42,7 +42,6 @@ import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; import io.ballerina.runtime.internal.scheduling.SyncCallback; -import io.ballerina.runtime.internal.util.RuntimeUtils; import io.ballerina.runtime.internal.values.FutureValue; import io.ballerina.runtime.internal.values.ObjectValue; import io.ballerina.runtime.internal.values.ValueCreator; @@ -90,11 +89,17 @@ public void init() { if (moduleInitialized) { throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "init"); } - invokeConfigInit(); - schedulerThread = new Thread(scheduler::start); - schedulerThread.start(); - invokeMethodSync("$moduleInit"); - moduleInitialized = true; + try { + invokeConfigInit(); + schedulerThread = new Thread(scheduler::start); + schedulerThread.start(); + invokeMethodSync("$moduleInit"); + moduleInitialized = true; + } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | + IllegalAccessException e) { + throw ErrorCreator.createError(StringUtils.fromString("error occurred while initializing the ballerina " + + "module "), e); + } } @Override @@ -126,15 +131,16 @@ public void stop() { if (moduleStopped) { throw ErrorHelper.getRuntimeException(ErrorCodes.FUNCTION_ALREADY_CALLED, "stop"); } - scheduler.poison(); try { + scheduler.poison(); schedulerThread.join(); - } catch (InterruptedException e) { - throw ErrorCreator.createError(StringUtils.fromString("error occurred while waiting for the scheduler " + - "thread to finish"), e); + invokeModuleStop(); + moduleStopped = true; + } catch (InterruptedException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | + IllegalAccessException e) { + throw ErrorCreator.createError(StringUtils.fromString("error occurred during module stop "), e); } - invokeModuleStop(); - moduleStopped = true; + } /** @@ -171,6 +177,7 @@ public void notifySuccess(Object result) { Function func = getFunction((Object[]) result, objectVal, methodName); scheduler.scheduleToObjectGroup(new Object[1], func, future); } + @Override public void notifyFailure(BError error) { callback.notifyFailure(error); @@ -219,6 +226,7 @@ public void notifySuccess(Object result) { Function func = getFunction((Object[]) result, objectVal, methodName); scheduler.schedule(new Object[1], func, future); } + @Override public void notifyFailure(BError error) { callback.notifyFailure(error); @@ -279,6 +287,7 @@ public void notifySuccess(Object result) { scheduler.scheduleToObjectGroup(new Object[1], func, future); } } + @Override public void notifyFailure(BError error) { callback.notifyFailure(error); @@ -353,41 +362,25 @@ public void registerStopHandler(BFunctionPointer stopHandler) { return func; } - private void invokeConfigInit() { + private void invokeConfigInit() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException { Class configClass = loadClass(CONFIGURATION_CLASS_NAME); ConfigDetails configDetails = LaunchUtils.getConfigurationDetails(); String funcName = Utils.encodeFunctionIdentifier("$configureInit"); - try { - final Method method = - configClass.getDeclaredMethod(funcName, Map.class, String[].class, Path[].class, String.class); - method.invoke(null, new HashMap<>(), new String[]{}, configDetails.paths, configDetails.configContent); - } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - throw ErrorCreator.createError(StringUtils.fromString("configurable initialization failed due to " + - RuntimeUtils.formatErrorMessage(e)), e); - } + Method method = configClass.getDeclaredMethod(funcName, Map.class, String[].class, Path[].class, String.class); + method.invoke(null, new HashMap<>(), new String[]{}, configDetails.paths, configDetails.configContent); } - private void invokeModuleStop() { + private void invokeModuleStop() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, + IllegalAccessException { Class configClass = loadClass(MODULE_INIT_CLASS_NAME); - try { - final Method method = - configClass.getDeclaredMethod("$currentModuleStop", RuntimeRegistry.class); - method.invoke(null, scheduler.getRuntimeRegistry()); - } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { - throw ErrorCreator.createError(StringUtils.fromString("configurable initialization failed due to " + - RuntimeUtils.formatErrorMessage(e)), e); - } + Method method = configClass.getDeclaredMethod("$currentModuleStop", RuntimeRegistry.class); + method.invoke(null, scheduler.getRuntimeRegistry()); } - private Class loadClass(String className) { + private Class loadClass(String className) throws ClassNotFoundException { String name = getFullQualifiedClassName(this.module, className); - Class clazz; - try { - clazz = Class.forName(name); - } catch (Throwable e) { - throw ErrorCreator.createError(StringUtils.fromString("failed to load configuration class :" + name), e); - } - return clazz; + return Class.forName(name); } private static String getFullQualifiedClassName(Module module, String className) { @@ -397,7 +390,7 @@ private static String getFullQualifiedClassName(Module module, String className) className = encodeNonFunctionIdentifier(packageName) + "." + module.getMajorVersion() + "." + className; } if (!ANON_ORG.equals(orgName)) { - className = encodeNonFunctionIdentifier(orgName) + "." + className; + className = encodeNonFunctionIdentifier(orgName) + "." + className; } return className; } @@ -416,8 +409,8 @@ private void invokeMethodSync(String functionName) { } } - private void invokeMethod(String functionName, Callback callback, Type returnType, - String strandName, Object... args) { + private void invokeMethod(String functionName, Callback callback, Type returnType, String strandName, + Object... args) { ValueCreator valueCreator = ValueCreator.getValueCreator(ValueCreator.getLookupKey(module.getOrg(), module.getName(), module.getMajorVersion(), module.isTestPkg())); Function func = o -> valueCreator.call((Strand) (((Object[]) o)[0]), functionName, args); From ddbb56e13dce0eec03bdd81c3107982d688527ac Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Tue, 9 Jul 2024 13:24:49 +0530 Subject: [PATCH 7/8] Improve error handling in runtime API --- .../io/ballerina/runtime/internal/BalRuntime.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java index 83b99a1cd180..03dd7fa0e3ee 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/BalRuntime.java @@ -95,10 +95,11 @@ public void init() { schedulerThread.start(); invokeMethodSync("$moduleInit"); moduleInitialized = true; - } catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException | - IllegalAccessException e) { + } catch (ClassNotFoundException e) { + throw ErrorCreator.createError(StringUtils.fromString(String.format("module '%s' does not exist", module))); + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) { throw ErrorCreator.createError(StringUtils.fromString("error occurred while initializing the ballerina " + - "module "), e); + "module due to " + e.getMessage()), e); } } @@ -138,9 +139,9 @@ public void stop() { moduleStopped = true; } catch (InterruptedException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { - throw ErrorCreator.createError(StringUtils.fromString("error occurred during module stop "), e); + throw ErrorCreator.createError(StringUtils.fromString("error occurred during module stop due to " + + e.getMessage()), e); } - } /** From 82475e4d12653fff4381ce3ac5e1214ac09398ff Mon Sep 17 00:00:00 2001 From: warunalakshitha Date: Tue, 9 Jul 2024 13:24:57 +0530 Subject: [PATCH 8/8] Add testcase for module not found --- .../test/runtime/api/RuntimeAPICallNegative.java | 9 +++++++++ .../ballerinalang/test/runtime/api/RuntimeAPITest.java | 10 ++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java index 4d7cd6866672..57160c8376b9 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPICallNegative.java @@ -73,5 +73,14 @@ public static void main(String[] args) { } catch (BError e) { out.println(e.getMessage()); } + + // Test non-existing ballerina module + module = new Module("testorg", "non-exist", "1"); + balRuntime = Runtime.from(module); + try { + balRuntime.init(); + } catch (BError e) { + out.println(e.getMessage()); + } } } diff --git a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java index ee3609340ffc..c4709f3d83e1 100644 --- a/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java +++ b/tests/jballerina-integration-test/src/test/java/org/ballerinalang/test/runtime/api/RuntimeAPITest.java @@ -132,12 +132,10 @@ public void testBalFunctionInvocationAPINegative() throws BallerinaTestException LogLeecher.LeecherType.ERROR)); leechers.add(new LogLeecher("function 'stop' is called before module initialization", LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'init' has already been called", - LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'start' has already been called", - LogLeecher.LeecherType.ERROR)); - leechers.add(new LogLeecher("function 'stop' has already been called", - LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'init' has already been called", LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'start' has already been called", LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("function 'stop' has already been called", LogLeecher.LeecherType.ERROR)); + leechers.add(new LogLeecher("module 'testorg/non-exist:1' does not exist", LogLeecher.LeecherType.ERROR)); addToServerInfoLogReader(serverInfoLogReader, leechers); serverInfoLogReader.start(); bMainInstance.waitForLeechers(leechers, 5000);