This is an automated email from the ASF dual-hosted git repository.

bamaer pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new bc70157ed5 Fix LoadScriptFromTab swallowing exceptions in the 
JavaScript transform (#8322)
bc70157ed5 is described below

commit bc70157ed55a601437ae26909efbef8f1918cc26
Author: Lance <[email protected]>
AuthorDate: Sun Sep 13 15:10:27 2026 +0800

    Fix LoadScriptFromTab swallowing exceptions in the JavaScript transform 
(#8322)
    
    * Fix LoadScriptFromTab swallowing exceptions in the JavaScript transform
    
    Signed-off-by: lance <[email protected]>
    
    * Fix LoadScriptFromTab swallowing exceptions in the JavaScript transform
    
    Signed-off-by: lance <[email protected]>
    
    ---------
    
    Signed-off-by: lance <[email protected]>
---
 .../0087-javascript-loadscript-from-tab.hpl        | 162 +++++++++++++++++++++
 .../golden-javascript-loadscript-from-tab.csv      |   4 +
 .../transforms/main-0087-javascript.hwf            |   9 +-
 .../golden-javascript-loadscript-from-tab.json     |  24 +++
 .../0087-javascript-loadscript-from-tab UNIT.json  |  32 ++++
 .../javascript/ScriptValuesAddedFunctions.java     |  25 +++-
 .../transforms/javascript/jsFunctionHelp.xml       |  10 +-
 ...ptValuesAddedFunctionsRhinoIntegrationTest.java |  49 ++++++-
 8 files changed, 299 insertions(+), 16 deletions(-)

diff --git 
a/integration-tests/transforms/0087-javascript-loadscript-from-tab.hpl 
b/integration-tests/transforms/0087-javascript-loadscript-from-tab.hpl
new file mode 100644
index 0000000000..7a43022804
--- /dev/null
+++ b/integration-tests/transforms/0087-javascript-loadscript-from-tab.hpl
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF 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.
+
+-->
+<pipeline>
+  <info>
+    <name>0087-javascript-loadscript-from-tab</name>
+    <name_sync_with_filename>Y</name_sync_with_filename>
+    <description>LoadScriptFromTab loads a library tab into the Start script 
context</description>
+    <extended_description/>
+    <pipeline_version/>
+    <pipeline_type>Normal</pipeline_type>
+    <parameters>
+    </parameters>
+    <capture_transform_performance>N</capture_transform_performance>
+    
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+    
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+    <created_user>-</created_user>
+    <created_date>2026/09/11 00:00:00.000</created_date>
+    <modified_user>-</modified_user>
+    <modified_date>2026/09/11 00:00:00.000</modified_date>
+  </info>
+  <notepads>
+  </notepads>
+  <order>
+    <hop>
+      <from>Input</from>
+      <to>JavaScript</to>
+      <enabled>Y</enabled>
+    </hop>
+    <hop>
+      <from>JavaScript</from>
+      <to>validate</to>
+      <enabled>Y</enabled>
+    </hop>
+  </order>
+  <transform>
+    <name>Input</name>
+    <type>DataGrid</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <data>
+      <line>
+        <item>1</item>
+      </line>
+      <line>
+        <item>2</item>
+      </line>
+      <line>
+        <item>3</item>
+      </line>
+    </data>
+    <fields>
+      <field>
+        <length>-1</length>
+        <precision>-1</precision>
+        <set_empty_string>N</set_empty_string>
+        <name>value</name>
+        <type>Integer</type>
+      </field>
+    </fields>
+    <attributes/>
+    <GUI>
+      <xloc>96</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>JavaScript</name>
+    <type>ScriptValueMod</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <fields>
+      <field>
+        <length>-1</length>
+        <name>result</name>
+        <precision>-1</precision>
+        <rename>result</rename>
+        <replace>N</replace>
+        <type>Integer</type>
+      </field>
+    </fields>
+    <jsScripts>
+      <jsScript>
+        <jsScript_name>Lib</jsScript_name>
+        <jsScript_script>
+function helper(v) {
+  return v * 2;
+}
+</jsScript_script>
+        <jsScript_type>-1</jsScript_type>
+      </jsScript>
+      <jsScript>
+        <jsScript_name>startScript</jsScript_name>
+        <jsScript_script>
+LoadScriptFromTab("Lib");
+</jsScript_script>
+        <jsScript_type>1</jsScript_type>
+      </jsScript>
+      <jsScript>
+        <jsScript_name>transformScript</jsScript_name>
+        <jsScript_script>
+var result = helper(value);
+</jsScript_script>
+        <jsScript_type>0</jsScript_type>
+      </jsScript>
+    </jsScripts>
+    <optimizationLevel>9</optimizationLevel>
+    <attributes/>
+    <GUI>
+      <xloc>288</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform>
+    <name>validate</name>
+    <type>Dummy</type>
+    <description/>
+    <distribute>Y</distribute>
+    <custom_distribution/>
+    <copies>1</copies>
+    <partitioning>
+      <method>none</method>
+      <schema_name/>
+    </partitioning>
+    <attributes/>
+    <GUI>
+      <xloc>432</xloc>
+      <yloc>80</yloc>
+    </GUI>
+  </transform>
+  <transform_error_handling>
+  </transform_error_handling>
+  <attributes/>
+</pipeline>
diff --git 
a/integration-tests/transforms/datasets/golden-javascript-loadscript-from-tab.csv
 
b/integration-tests/transforms/datasets/golden-javascript-loadscript-from-tab.csv
new file mode 100644
index 0000000000..fde7a9ca3d
--- /dev/null
+++ 
b/integration-tests/transforms/datasets/golden-javascript-loadscript-from-tab.csv
@@ -0,0 +1,4 @@
+value,result
+1,2
+2,4
+3,6
diff --git a/integration-tests/transforms/main-0087-javascript.hwf 
b/integration-tests/transforms/main-0087-javascript.hwf
index 17d6daada1..371fa0fc0a 100644
--- a/integration-tests/transforms/main-0087-javascript.hwf
+++ b/integration-tests/transforms/main-0087-javascript.hwf
@@ -50,7 +50,7 @@ limitations under the License.
       <attributes_hac/>
     </action>
     <action>
-      <name>0087-javascript-basics UNIT</name>
+      <name>Run Pipeline Unit Tests</name>
       <description/>
       <type>RunPipelineTests</type>
       <attributes/>
@@ -58,6 +58,9 @@ limitations under the License.
         <test_name>
           <name>0087-javascript-basics UNIT</name>
         </test_name>
+        <test_name>
+          <name>0087-javascript-loadscript-from-tab UNIT</name>
+        </test_name>
       </test_names>
       <parallel>N</parallel>
       <xloc>208</xloc>
@@ -108,13 +111,13 @@ limitations under the License.
   <hops>
     <hop>
       <from>Start</from>
-      <to>0087-javascript-basics UNIT</to>
+      <to>Run Pipeline Unit Tests</to>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
       <unconditional>Y</unconditional>
     </hop>
     <hop>
-      <from>0087-javascript-basics UNIT</from>
+      <from>Run Pipeline Unit Tests</from>
       <to>Simple evaluation</to>
       <enabled>Y</enabled>
       <evaluation>Y</evaluation>
diff --git 
a/integration-tests/transforms/metadata/dataset/golden-javascript-loadscript-from-tab.json
 
b/integration-tests/transforms/metadata/dataset/golden-javascript-loadscript-from-tab.json
new file mode 100644
index 0000000000..38919923f6
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/dataset/golden-javascript-loadscript-from-tab.json
@@ -0,0 +1,24 @@
+{
+  "base_filename": "golden-javascript-loadscript-from-tab.csv",
+  "name": "golden-javascript-loadscript-from-tab",
+  "description": "",
+  "dataset_fields": [
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "value",
+      "field_format": "####0;-####0"
+    },
+    {
+      "field_comment": "",
+      "field_length": -1,
+      "field_type": 5,
+      "field_precision": 0,
+      "field_name": "result",
+      "field_format": "####0;-####0"
+    }
+  ],
+  "folder_name": ""
+}
diff --git 
a/integration-tests/transforms/metadata/unit-test/0087-javascript-loadscript-from-tab
 UNIT.json 
b/integration-tests/transforms/metadata/unit-test/0087-javascript-loadscript-from-tab
 UNIT.json
new file mode 100644
index 0000000000..ebaf0eabc9
--- /dev/null
+++ 
b/integration-tests/transforms/metadata/unit-test/0087-javascript-loadscript-from-tab
 UNIT.json     
@@ -0,0 +1,32 @@
+{
+  "database_replacements": [],
+  "autoOpening": true,
+  "description": "Start script loads Lib via LoadScriptFromTab; transform uses 
helper()",
+  "persist_filename": "",
+  "test_type": "UNIT_TEST",
+  "variableValues": [],
+  "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+  "golden_data_sets": [
+    {
+      "field_mappings": [
+        {
+          "transform_field": "value",
+          "data_set_field": "value"
+        },
+        {
+          "transform_field": "result",
+          "data_set_field": "result"
+        }
+      ],
+      "field_order": [
+        "value"
+      ],
+      "data_set_name": "golden-javascript-loadscript-from-tab",
+      "transform_name": "validate"
+    }
+  ],
+  "input_data_sets": [],
+  "name": "0087-javascript-loadscript-from-tab UNIT",
+  "trans_test_tweaks": [],
+  "pipeline_filename": "./0087-javascript-loadscript-from-tab.hpl"
+}
diff --git 
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctions.java
 
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctions.java
index 6d50460095..4eaa1ef245 100644
--- 
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctions.java
+++ 
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctions.java
@@ -1772,14 +1772,27 @@ public class ScriptValuesAddedFunctions extends 
ScriptableObject {
   // Adding the ScriptsItemTab to the actual running Context
   public static void LoadScriptFromTab(
       Context actualContext, Scriptable actualObject, Object[] argList, 
Function functionContext) {
-    try {
-      for (Object o : argList) { // don't worry about "undefined" arguments
-        String strToLoad = Context.toString(o);
-        String strScript = actualObject.get(strToLoad, 
actualObject).toString();
+    if (argList.length == 0) {
+      throw Context.reportRuntimeError(
+          "The function call LoadScriptFromTab requires at least 1 argument.");
+    }
+
+    for (Object o : argList) {
+      String strToLoad = Context.toString(o);
+      Object scriptObj = actualObject.get(strToLoad, actualObject);
+      if (scriptObj == Scriptable.NOT_FOUND
+          || scriptObj == null
+          || scriptObj == Context.getUndefinedValue()) {
+        throw Context.reportRuntimeError("Unable to find script tab \"" + 
strToLoad + "\"");
+      }
+
+      try {
+        String strScript = Context.toString(scriptObj);
         actualContext.evaluateString(actualObject, strScript, "_" + strToLoad 
+ "_", 0, null);
+      } catch (Exception e) {
+        throw Context.reportRuntimeError(
+            "Unable to load script from tab \"" + strToLoad + "\": " + 
e.getMessage());
       }
-    } catch (Exception e) {
-      // TODO: DON'T EAT EXCEPTION
     }
   }
 
diff --git 
a/plugins/transforms/javascript/src/main/resources/org/apache/hop/pipeline/transforms/javascript/jsFunctionHelp.xml
 
b/plugins/transforms/javascript/src/main/resources/org/apache/hop/pipeline/transforms/javascript/jsFunctionHelp.xml
index cc3ddd0d45..f980875378 100644
--- 
a/plugins/transforms/javascript/src/main/resources/org/apache/hop/pipeline/transforms/javascript/jsFunctionHelp.xml
+++ 
b/plugins/transforms/javascript/src/main/resources/org/apache/hop/pipeline/transforms/javascript/jsFunctionHelp.xml
@@ -385,18 +385,16 @@
         </Arguments>
     </jsFunction>
     <jsFunction name="LoadScriptFromTab">
-        <help>Adds the content of a JavaScript_mod Tab into the current 
Context.</help>
-        <sample>// Load's JavaScript Functions from an existing tab
+        <help>Adds the content of a JavaScript Tab into the current Context. 
Throws an error if the tab name does not exist or the script cannot be 
evaluated.</help>
+        <sample>// Loads JavaScript functions from an existing tab
             // into your actual running Context.
-            // This function should called from your defined StartScript
+            // Call this from your defined StartScript
             // otherwise your functions are loaded on each processing
-            // Row.
+            // Row. Missing or invalid tabs fail the transform immediately.
             //
             // Usage:
             // LoadScriptFromTab(var);
             //
-            // 2006-11-15
-            //
             var xTab = "Your TabName";
             LoadScriptFromTab(xTab);
         </sample>
diff --git 
a/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctionsRhinoIntegrationTest.java
 
b/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctionsRhinoIntegrationTest.java
index 9e58219414..e87365cf59 100644
--- 
a/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctionsRhinoIntegrationTest.java
+++ 
b/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesAddedFunctionsRhinoIntegrationTest.java
@@ -64,9 +64,10 @@ class ScriptValuesAddedFunctionsRhinoIntegrationTest {
 
   @Test
   void getDigitsOnly_requiresOneArgument() {
+    Object[] args = args();
     assertThrows(
         EvaluatorException.class,
-        () -> ScriptValuesAddedFunctions.getDigitsOnly(cx, scope, args(), 
null));
+        () -> ScriptValuesAddedFunctions.getDigitsOnly(cx, scope, args, null));
   }
 
   @Test
@@ -221,4 +222,50 @@ class ScriptValuesAddedFunctionsRhinoIntegrationTest {
     assertNotNull(ScriptValuesAddedFunctions.getDayNumber(cx, scope, args(js, 
"m"), null));
     assertNotNull(ScriptValuesAddedFunctions.getDayNumber(cx, scope, args(js, 
"w"), null));
   }
+
+  @Test
+  void loadScriptFromTab_loadsFunctionsIntoScope() {
+    // Same shape as ScriptValues: tab name -> script source on the scope.
+    scope.put("Lib", scope, Context.toObject("function helper() { return 42; 
}", scope));
+
+    ScriptValuesAddedFunctions.LoadScriptFromTab(cx, scope, args("Lib"), null);
+
+    Object result = cx.evaluateString(scope, "helper()", "script", 1, null);
+    assertEquals(42.0, Context.toNumber(result), 1e-9);
+  }
+
+  @Test
+  void loadScriptFromTab_missingTabThrows() {
+    Object[] doesNotExists = args("DoesNotExist");
+    EvaluatorException ex =
+        assertThrows(
+            EvaluatorException.class,
+            () -> ScriptValuesAddedFunctions.LoadScriptFromTab(cx, scope, 
doesNotExists, null));
+    assertTrue(ex.getMessage().contains("Unable to find script tab"));
+    assertTrue(ex.getMessage().contains("DoesNotExist"));
+  }
+
+  @Test
+  void loadScriptFromTab_invalidScriptThrows() {
+    scope.put("Broken", scope, Context.toObject("function {", scope));
+    Object[] broken = args("Broken");
+
+    EvaluatorException ex =
+        assertThrows(
+            EvaluatorException.class,
+            () -> ScriptValuesAddedFunctions.LoadScriptFromTab(cx, scope, 
broken, null));
+    assertTrue(ex.getMessage().contains("Unable to load script from tab"));
+    assertTrue(ex.getMessage().contains("Broken"));
+  }
+
+  @Test
+  void loadScriptFromTab_requiresAtLeastOneArgument() {
+    Object[] args = args();
+
+    EvaluatorException ex =
+        assertThrows(
+            EvaluatorException.class,
+            () -> ScriptValuesAddedFunctions.LoadScriptFromTab(cx, scope, 
args, null));
+    assertTrue(ex.getMessage().contains("requires at least 1 argument"));
+  }
 }

Reply via email to