docs: @Override
      public boolean invokeFunction(String funcName, Object[] params) {
          // Run script if it is dirty. This makes sure the script is run
          // on the same thread as the caller (suppose the caller is always
          // calling from the same thread).
          checkDirty();
  
          // Skip bad functions
          if (isBadFunction(funcName)) {
              return false;
          }
  
          String statement = getInvokeStatementCached(funcName, params);
  
          synchronized (mEngineLock) {
              localBindings = mLocalEngine.getBindings(ScriptContext.ENGINE_SCOPE);
              if (localBindings == null) {
                  localBindings = mLocalEngine.createBindings();
                  mLocalEngine.setBindings(localBindings, ScriptContext.ENGINE_SCOPE);
              }
          }
  
          fillBindings(localBindings, params);
  
          try {
              mLocalEngine.eval(statement);
          } catch (ScriptException e) {
              // The function is either undefined or throws, avoid invoking it later
              addBadFunction(funcName);
              mLastError = e.getMessage();
              return false;
          } finally {
              removeBindings(localBindings, params);
          }
  
          return true;
      }