• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "JniUtils.h"
18 #include "LuaEngine.h"
19 #include "ScriptExecutorListener.h"
20 #include "jni.h"
21 
22 #include <android-base/logging.h>
23 
24 #include <cstdint>
25 
26 namespace android {
27 namespace automotive {
28 namespace telemetry {
29 namespace script_executor {
30 
31 extern "C" {
32 
33 JNIEXPORT jlong JNICALL
Java_com_android_car_telemetry_ScriptExecutor_nativeInitLuaEngine(JNIEnv * env,jobject object)34 Java_com_android_car_telemetry_ScriptExecutor_nativeInitLuaEngine(JNIEnv* env, jobject object) {
35     // Cast first to intptr_t to ensure int can hold the pointer without loss.
36     return static_cast<jlong>(reinterpret_cast<intptr_t>(new LuaEngine()));
37 }
38 
Java_com_android_car_telemetry_ScriptExecutor_nativeDestroyLuaEngine(JNIEnv * env,jobject object,jlong luaEnginePtr)39 JNIEXPORT void JNICALL Java_com_android_car_telemetry_ScriptExecutor_nativeDestroyLuaEngine(
40         JNIEnv* env, jobject object, jlong luaEnginePtr) {
41     delete reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr));
42 }
43 
44 // Parses the inputs and loads them to Lua one at a time.
45 // Loading of data into Lua also triggers checks on Lua side to verify the
46 // inputs are valid. For example, pushing "functionName" into Lua stack verifies
47 // that the function name actually exists in the previously loaded body of the
48 // script.
49 //
50 // The steps are:
51 // Step 1: Parse the inputs for obvious programming errors.
52 // Step 2: Parse and load the body of the script.
53 // Step 3: Parse and push function name we want to execute in the provided
54 // script body to Lua stack. If the function name doesn't exist, we exit.
55 // Step 4: Parse publishedData, convert it into Lua table and push it to the
56 // stack.
57 // Step 5: Parse savedState Bundle object, convert it into Lua table and push it
58 // to the stack.
59 // Any errors that occur at the stage above result in quick exit or crash.
60 //
61 // All interaction with Lua happens via Lua stack. Therefore, order of how the
62 // inputs are parsed and processed is critical because Lua API methods such as
63 // lua_pcall assume specific order between function name and the input arguments
64 // on the stack.
65 // More information about how to work with Lua stack: https://www.lua.org/pil/24.2.html
66 // and how Lua functions are called via Lua API: https://www.lua.org/pil/25.2.html
67 //
68 // Finally, once parsing and pushing to Lua stack is complete, we do
69 //
70 // Step 6: attempt to run the provided function.
Java_com_android_car_telemetry_ScriptExecutor_nativeInvokeScript(JNIEnv * env,jobject object,jlong luaEnginePtr,jstring scriptBody,jstring functionName,jobject publishedData,jobject savedState,jobject listener)71 JNIEXPORT void JNICALL Java_com_android_car_telemetry_ScriptExecutor_nativeInvokeScript(
72         JNIEnv* env, jobject object, jlong luaEnginePtr, jstring scriptBody, jstring functionName,
73         jobject publishedData, jobject savedState, jobject listener) {
74     if (!luaEnginePtr) {
75         env->FatalError("luaEnginePtr parameter cannot be nil");
76     }
77     if (scriptBody == nullptr) {
78         env->FatalError("scriptBody parameter cannot be null");
79     }
80     if (functionName == nullptr) {
81         env->FatalError("functionName parameter cannot be null");
82     }
83     if (listener == nullptr) {
84         env->FatalError("listener parameter cannot be null");
85     }
86 
87     LuaEngine* engine = reinterpret_cast<LuaEngine*>(static_cast<intptr_t>(luaEnginePtr));
88 
89     // Load and parse the script
90     const char* scriptStr = env->GetStringUTFChars(scriptBody, nullptr);
91     auto status = engine->LoadScript(scriptStr);
92     env->ReleaseStringUTFChars(scriptBody, scriptStr);
93     // status == 0 if the script loads successfully.
94     if (status) {
95         env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
96                       "Failed to load the script.");
97         return;
98     }
99     engine->ResetListener(new ScriptExecutorListener(env, listener));
100 
101     // Push the function name we want to invoke to Lua stack
102     const char* functionNameStr = env->GetStringUTFChars(functionName, nullptr);
103     status = engine->PushFunction(functionNameStr);
104     env->ReleaseStringUTFChars(functionName, functionNameStr);
105     // status == 1 if the name is indeed a function.
106     if (!status) {
107         env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
108                       "symbol functionName does not correspond to a function.");
109         return;
110     }
111 
112     // TODO(b/189241508): Provide implementation to parse publishedData input,
113     // convert it into Lua table and push into Lua stack.
114     if (publishedData) {
115         env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
116                       "Parsing of publishedData is not implemented yet.");
117         return;
118     }
119 
120     // Unpack bundle in savedState, convert to Lua table and push it to Lua
121     // stack.
122     PushBundleToLuaTable(env, engine, savedState);
123 
124     // Execute the function. This will block until complete or error.
125     if (engine->Run()) {
126         env->ThrowNew(env->FindClass("java/lang/RuntimeException"),
127                       "Runtime error occurred while running the function.");
128         return;
129     }
130 }
131 
132 }  // extern "C"
133 
134 }  // namespace script_executor
135 }  // namespace telemetry
136 }  // namespace automotive
137 }  // namespace android
138