callLuaFunction method

List<LuaObject> callLuaFunction(
  1. LuaObject obj, {
  2. List<Object?> args = const [],
  3. LuaExceptionCallback? onException,
})
inherited

Given a callable lua obj and a list of args, configure a new scope with the provided args as parameters based on the LuaObject.funcDef record. Args exceeding the parameter list will be dropped. Any remaining parameters will be filled by LuaObject.nil values. Any exceptions are caught and tracked for the trace back later. The scope is popped and any return result LuaObjects are returned as an argpack.

Implementation

List<LuaObject> callLuaFunction(
  LuaObject obj, {
  List<Object?> args = const [],
  LuaExceptionCallback? onException,
}) {
  LuaObject? callable;
  if (obj.isFunc) {
    callable = obj;
  } else if (obj.isCallable) {
    callable = switch (obj.readMetatable('__call')) {
      final LuaObject lo => lo,
      _ => null,
    };
  }

  if (callable == null) {
    final type = obj.luaTypeInfo;
    final varname = obj.id;
    throw 'Attempt to call a $type value "$varname".';
  }

  List<LuaObject> res = [];
  final prevScope = scope;
  pushScope(parent: callable.scope);

  try {
    final defArgs = callable.funcDef!.args;
    final nilCount = defArgs.length - args.length;

    for (int i = 0; i < defArgs.length; i++) {
      final id = defArgs[i].lexeme;
      defLocal(args[i]?.toLua(id) ?? LuaObject.nil(id));
    }

    for (int i = 0; i < nilCount; i++) {
      defLocal(LuaObject.nil(defArgs[args.length + i].lexeme));
    }

    // For the public API utilites,
    // we expect friendly non-null values.
    // Return an empty list if null.
    final temp = callable.call();
    res = switch (temp) {
      final List<LuaObject?> ls => ls.nonNulls.toList(),
      final List<Object?> ls => ls.map((e) => e?.toLuaRet()).nonNulls.toList(),
      final Object o => [o.toLuaRet()],
      null => [],
    };
  } catch (e) {
    if (e is LuaReturnValueException) {
      res = e.argpack;
    } else if (onException != null) {
      onException.call(e);
    } else {
      rethrow;
    }
  } finally {
    restoreScope(prevScope);
  }

  return res;
}