Date: 2026-07-12
Task: TASK-505 (docs-only refresh)
TASK-003 study output, refreshed for post-TASK-184 / pre-TASK-043 documentation accuracy. Source paths are read-only Android-Lua paths under /Users/dingyi/projects/java_projects/Android-Lua/app/src/main.
Honesty bound (post-TASK-184, pre-TASK-043): this page documents runtime import.lua / native LuaJava behavior and the repository’s static overlay relationship. It does not claim final green acceptance, suite green, or inventory finality. Product-surface library stubs and overlay work from the chain ending in TASK-184 are modeled and review-accepted for focused surfaces; serialized Gradle/test verification remains review-owned under TASK-043 (blocked); final acceptance remains TASK-037 (blocked). Inventory recount / freeze follow-ups (for example TASK-125) are not final until TASK-043. No Gradle, compile, or test command was run for this documentation task. Never hard-code Windows G:/ paths.
Verification notes, JVM metadata examples, and reflective Android framework resolution on this host use macOS paths only:
| Item | Host value | Status (2026-07-12 TASK-505 re-check) |
|---|---|---|
| JDK for JVM tests / docs examples | Corretto 17: /Users/dingyi/Library/Java/JavaVirtualMachines/corretto-17.0.19/Contents/Home |
Present; use as JAVA_HOME when review later runs verification |
| Preferred Android platform jar | /Users/dingyi/Library/Android/sdk/platforms/android-35/android.jar |
Present (27,092,450 bytes; Android SDK Platform 35) |
| Optional drop-in jar | /Users/dingyi/Downloads/android.jar |
Absent on this host; allowed only when present |
| Android SDK root | /Users/dingyi/Library/Android/sdk (ANDROID_HOME / ANDROID_SDK_ROOT when set) |
Present |
Dual-path android.jar policy: only Downloads + SDK android-35 candidates above. Prefer explicit metadata jvm.androidJar pointing at the SDK path for reproducible analysis. Product code may discover jars via ANDROID_HOME / well-known roots (see docs/android-platform-setup.md); missing jars must skip reflective Android mounting — never invent classpath entries. Never hard-code Windows G:/ (or other non-host) paths.
Related honesty documents:
docs/android-lua-compatibility-matrix.md— broader post-184 compatibility claims (awaiting TASK-043)docs/android-lua-import-resolution.md— static resolution orderdocs/java-interop-model.md/docs/jvm-reflection-classloader-design.md— JVM reflection projectiondocs/luajava-helper-surface-matrix.md— helper support matrix (may still carry older chain wording in places; this page + compatibility matrix own post-184 honesty for import/LuaJava study status)docs/android-platform-setup.md— SDK path, Corretto 17, skip-when-absent policy
Android-Lua exposes LuaJava by opening the native luajava library for each LuaState, then loading normal Lua modules through package.path and package.cpath.
java/com/luajava/LuaState.java:123-124:System.loadLibrary(LUAJAVA_LIB)loads the nativeluajavalibrary.jni/luajava/luajava.c:1560-1561:_openLuajavacallsluaL_requiref(L, "luajava", luaopen_luajava, 1), making theluajavatable global.jni/luajava/luajava.c:1508-1521: the exported Lua table includesbindClass,new,newInstance,loadLib,createProxy,newArray,createArray,astable,tostring,instanceof,getContext, andoverride.java/com/androlua/LuaActivity.java:1348-1375: activity states create a newLuaState, callopenLibs, set globalsactivity,service,this, push theLuaContext, and assignpackage.path/package.cpath.java/com/androlua/LuaThread.java:236-258: worker thread states repeat the same pattern and setactivityorservicedepending on the parent context.
require "import" resolves through the configured Lua path. The module is resources/lua/import.lua; it mutates the caller environment and returns the env_import function.
For static analysis in this repository, the active LuaJava module declaration is:
src/commonMain/resources/io/github/dingyi222666/luaparser/semantic/workspace/std/androlua53-luajava/luajava.lua
BuiltinOverlayLoader reads that resource for the LuaVersion.ANDROLUA_5_3 luajava provider and exposes it through the synthetic overlay path __lua_std__/androlua5.3/luajava.lua. Treat this file as the authoritative LuaJava overlay for active loading.
The external Android-Lua source studied for this document does not provide a standalone resources/lua/luajava.lua. Runtime LuaJava is registered by the native bridge (luaopen_luajava) and then extended by resources/lua/import.lua, which adds shared tables such as luajava.loaded, luajava.imported, layout id state, and package-chain metatable behavior. Those raw runtime sources explain behavior; they are not the active static declaration file.
The embedded Kotlin text in AndroidLua53LuaJavaBuiltinOverlaySources.luajavaSource is only a fallback used if the resource above cannot be read. It should remain compatible with the resource, but it is not the source of truth while std/androlua53-luajava/luajava.lua is present. This TASK-091 note documents that relationship only; it does not require resource, source, or test edits.
Android-Lua library stub / helper overlay surfaces (activity, service, loadlayout, loadbitmap, loadmenu, helper modules, layout fixtures) were restored under TASK-184 (focused review acceptance). That is product-surface modeling status only — not a substitute for TASK-043 serialized verification or final green.
Representative snippets:
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:1
local require = require
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:485
env_import(_G)
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:503
return env_import
Loading import.lua performs these runtime changes:
- Saves
luajava.loadedandluajava.importedtables for class/module cache and import history (import.lua:5-8). - Reads available dex class loaders and native library paths from
activityorservice(import.lua:15-17). - Adds a native-library searcher to
package.searchers(import.lua:19-28). The loader maps a module prefix tolibs[path:match("^%a+")]and callspackage.loadlib(path, "luaopen_" .. path:gsub("%.", "_")). - Installs a metatable on
_Gthroughenv_import(_G)so unresolved globals can lazily bind Java classes (import.lua:141-188,import.lua:485). - Injects helper functions from
_M, includingcompile,enum,each,dump,printstack,thread,task,timer, andgetids(import.lua:189-204,import.lua:213-467). - Imports helper Lua modules
loadlayout,loadbitmap, andloadmenuinto the environment (import.lua:206-208). - Adds a metatable to
luajavaitself so property chains such asluajava.java.lang.Stringcan progressively resolve classes or package proxy tables (import.lua:487-501).
The assets/javaapi/fiximport.lua script is not the runtime importer. It is an editor/helper tool that itself does require "import" and scans Lua source for likely class names (assets/javaapi/fiximport.lua:1-9, assets/javaapi/fiximport.lua:28-91).
env_import creates an import function in the target environment:
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:192-201
local import = function(package, env)
env = env or _env
if type(package) == "string" then
return local_import(env, packages, package)
elseif type(package) == "table" then
local ret = {}
for k, v in ipairs(package) do
ret[k] = local_import(env, packages, v)
end
return ret
end
end
Important cases:
import "java.io.File"extracts the last segment (File) and writes that class/module into the environment (import.lua:123-132).import "java.io.*"addsjava.io.to the environment-local package prefix list and returns a package proxy table (import.lua:117-121).import { "java.io.File", "java.util.*" }imports each array element in order and returns an array-like Lua table of results (import.lua:196-201). It usesipairs, so only sequential numeric entries are processed.import "dexname:fully.qualified.ClassName"loads a class throughluacontext.loadDex(dexname).loadClass(classname), binds the simple name into the environment, records the original import string, and returns the class (import.lua:106-115).- For non-wildcard imports, lookup tries Lua
require(package), thenluajava.bindClass, then all loaded dex class loaders (import.lua:89-95,import.lua:123-125).
Known limits:
importsilently ignores non-string and non-table arguments (import.lua:192-202).- Table imports use
ipairs, so sparse keys and map-style entries are ignored (import.lua:197-200). - Failed explicit imports raise
cannot find <package>(import.lua:134-135). - A
requireerror is rethrown unless the error text containsno file(import.lua:89-94).
Each imported environment keeps a packages list:
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:143-148
local packages = {}
append(packages, '')
append(packages, 'java.lang.')
append(packages, 'java.util.')
append(packages, 'com.androlua.')
import "java.io.*" appends java.io. to this list and records the wildcard string in luajava.imported (import.lua:117-121). Wildcards are lazy package prefixes; they do not enumerate classes. After a wildcard import, an unresolved global like File can be resolved by trying java.io.File.
Lookup order for direct global class exposure is:
- Already-loaded exact
loaded[classname]. luajava.bindClass(prefix .. classname)for every prefix inpackages(import.lua:150-157,import.lua:171-177).dex.loadClass(prefix .. classname)for every prefix inpackagesand every known dex class loader (import.lua:159-169,import.lua:171-177).
Because the initial package list includes the empty prefix, java.lang., java.util., and com.androlua., scripts can reference names such as String, ArrayList, or LuaThread after require "import" without an explicit import, if the runtime class exists.
Package proxy tables also exist. import "java.io.*" returns a table with __name = "java.io."; indexing pkg.File attempts luajava.bindClass("java.io." .. "File") and caches the result (import.lua:68-83).
Known limits:
- Wildcards only cover later simple-name lookup. They do not populate completion inventories by themselves.
- Prefix order matters. A simple name resolves to the first prefix that binds successfully.
_Gdirect lookup only happens for unresolved globals; local variables and existing globals shadow it.- Each
env_import(env)call has its own package list, butluajava.loadedis shared by the module.
import.lua accepts underscores as inner-class separators before binding:
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/resources/lua/import.lua:30-34
if classname:find('_') then
classname = classname:gsub('_', '$')
end
This applies to explicit class imports and dex class imports (import.lua:45-64). For example, import "android.view.View_OnClickListener" is massaged to android.view.View$OnClickListener.
The Java bridge also resolves nested classes when indexing a Java Class userdata:
java/com/luajava/LuaJavaAPI.java:1220-1233:checkClassfirst triesClass.forName(clazz.getName() + "$" + className).java/com/luajava/LuaJavaAPI.java:1237-1241: if that fails, it scansclazz.getClasses()by simple name.
Known limits:
- The underscore-to-dollar rewrite is broad. Any underscore in an explicit imported class name is converted to
$, which can break real Java class names that contain underscores. - The rewrite is not used for plain global simple-name lookup before prefixes are applied; it is used by explicit import paths and dex import paths.
- Java nested class lookup is only reached when indexing an already-bound
Classuserdata, not when parsing dotted Lua source.
luajava.bindClass(name) is a native function registered in jni/luajava/luajava.c:1508. The native implementation validates one string argument and calls LuaJavaAPI.javaBindClass (jni/luajava/luajava.c:800-822).
LuaJavaAPI.bindClass uses Class.forName(className) and has explicit fallbacks for primitive type names:
/Users/dingyi/projects/java_projects/Android-Lua/app/src/main/java/com/luajava/LuaJavaAPI.java:599-630
Class.forName(className)
case "boolean": clazz = Boolean.TYPE
case "byte": clazz = Byte.TYPE
...
default: throw new LuaException("Class not found: " + className)
luajava.newInstance(className, ...) is registered at jni/luajava/luajava.c:1510 and routes to LuaJavaAPI.javaNewInstance (jni/luajava/luajava.c:1030-1048). The Java side binds the class name, converts primitive constructors directly, and otherwise calls constructor resolution (LuaJavaAPI.java:650-660).
luajava.new(classUserdata, ...) constructs from a bound Class userdata (LuaJavaAPI.java:671-689). It also special-cases primitive class userdatas, abstract classes with a Lua override table, and normal constructors.
Constructor resolution:
- With no Lua constructor args,
getObjInstancefirst tries the no-arg Java constructor, then a singleContextconstructor usingL.getContext().getContext()(LuaJavaAPI.java:977-993). - With args, it iterates public constructors, requires the same arity, converts each Lua argument with
compareTypes, invokes the first compatible constructor, and reports candidate constructors on failure (LuaJavaAPI.java:995-1042).
There are several related table/array paths:
luajava.newArray(classUserdata, ...)creates a Java array of a bound class. The native wrapper passes the class userdata and dimensions toLuaJavaAPI.newArray(jni/luajava/luajava.c:866-887;LuaJavaAPI.java:566-596).luajava.createArray(className, table)creates a typed array by bindingclassNamethen converting the Lua table (jni/luajava/luajava.c:893-924;LuaJavaAPI.java:830-836).- Calling a Java class userdata with a Lua table chooses
LuaJavaAPI.javaCreate: primitive/String/array classes become arrays;ListandMapclasses become Java collections; interfaces become proxies; abstract classes become enhanced proxies; otherwise the table either initializes an array or falls back to constructor behavior (LuaJavaAPI.java:717-748). - Calling an existing Java object with a table applies table entries to the object: numeric keys set array values, string keys route through Java setters, and
Listobjects append values (LuaJavaAPI.java:771-807). This supports patterns such asObject { ... }seen inimport.lua:399-405,import.lua:439-443, andimport.lua:459-460. luajava.astable(javaObject)recursively converts Java arrays, collections, and maps to Lua tables (LuaJavaAPI.java:526-563).import.luauses it to turnluacontext.getClassLoaders()into a Lua table (import.lua:15-17).LuaObject.asArrayandLuaObject.asMapconvert Lua tables to Java object arrays/maps for Java-side callers (LuaObject.java:592-630).
Type conversion for Java calls is centralized in compareTypes:
nilmaps to Javanull(LuaJavaAPI.java:1751-1752).- Lua booleans, strings, numbers, functions, tables, and userdata are converted according to the target Java parameter (
LuaJavaAPI.java:1753-1863). - Lua functions and tables can become Java interface proxies (
LuaJavaAPI.java:1772-1779,LuaJavaAPI.java:1783-1795). - Lua tables can become Java arrays,
Lists, orMaps (LuaJavaAPI.java:1783-1793). - Numbers are converted to primitive/wrapper numeric types with integer vs floating detection (
LuaJavaAPI.java:1800-1812).
Known limits:
- Table-to-array conversion uses Lua sequence length/object length, so non-sequence numeric keys are not guaranteed to be preserved (
LuaJavaAPI.java:1582-1654). newArrayrequires a bound class userdata in argument 1;createArraytakes a class name string and a table.- Proxy conversion rejects Lua array-like tables when a table is used as an interface proxy (
LuaObject.java:686-695).
luajava.createProxy(interfaceNames, table) is exported in the native library (jni/luajava/luajava.c:1512). The native wrapper requires two args, with arg 1 a string and arg 2 a Lua table (jni/luajava/luajava.c:829-859), then calls LuaJavaAPI.createProxy.
Java proxy creation:
LuaJavaAPI.createProxyfetches the Lua object at stack index 2 and delegates toLuaObject.createProxy(LuaJavaAPI.java:822-827,LuaJavaAPI.java:1541-1553).LuaObject.createProxy(String)requires a Lua table, tokenizes comma-separated interface class names, resolves each withClass.forName, and returns aProxyusingLuaInvocationHandler(LuaObject.java:670-683).LuaObject.createProxy(Class)accepts a Lua table or a Lua function. A function is allowed only for a single-method interface; list-like tables are rejected (LuaObject.java:686-700).
Java setter syntax also auto-wires Android-style listeners:
LuaJavaAPI.java:1324-1325: assigning a Lua function to a key beginning withoncan route to listener setup.LuaJavaAPI.java:1395-1412:onClick = function ... endlooks forsetOnClickListener, wraps the function in a one-method table, creates a proxy for the listener interface, and invokes the setter.
There are two load-library paths:
luajava.loadLib(className, methodName)calls a Java static method named bymethodNameonclassName; that method must acceptLuaStateand may return an integer result count (jni/luajava/luajava.c:1056-1095;LuaJavaAPI.java:849-872).- The
import.lualibsloaderadded topackage.searchersmaps Luarequirenames to native.sopaths registered by the Android-Lua context, then callspackage.loadlibwithluaopen_<module_name>(import.lua:19-28).
LuaDexLoader populates the native library map used by import.lua:
java/com/androlua/LuaDexLoader.java:70-84: scans the scriptlibsdirectory for.sofiles and dex/jar artifacts.java/com/androlua/LuaDexLoader.java:84-101: copieslib<name>.sointo an app-private directory and recordslibCache[fn] = libPath.java/com/androlua/LuaActivity.java:155-159: exposesgetClassLoaders()andgetLibrarys()to Lua.
Known limits:
luajava.loadLibis for Java static loader methods, not arbitrary native.sopaths.import.luanative library lookup only uses an alphabetic prefix from the module name (path:match("^%a+")), so library aliases must match that prefix convention.
Java userdata behavior is implemented in native metamethods that delegate to LuaJavaAPI:
LuaJavaAPI.objectIndexlookup order is method, public field, JavaBean getter, nested class, thenLuaMetaTable.__index(LuaJavaAPI.java:95-124).LuaJavaAPI.objectNewIndexassignment order is public field, setter/listener/declared field, thenLuaMetaTable.__newIndex(LuaJavaAPI.java:302-326).LuaJavaAPI.callMethodresolves overloaded Java methods by arity andcompareTypes, with small caches for no-arg and one-arg string/boolean/integer/double calls (LuaJavaAPI.java:127-290).LuaJavaAPI.checkFieldreads public static fields fromClassuserdatas and public fields from instances; final fields return a different cache marker (LuaJavaAPI.java:1131-1171).LuaJavaAPI.javaGettermaps property access togetName()orisName(), and maps JavaMapaccess tomap.get(key)(LuaJavaAPI.java:1248-1298).LuaJavaAPI.javaSettermaps assignments toMap.put, Android listener shorthand,setName(value), or declared-field assignment (LuaJavaAPI.java:1301-1330,LuaJavaAPI.java:1422-1539).
Lua-to-Java stack conversion outside method resolution is exposed by LuaState.toJavaObject: booleans, strings, functions, tables, numbers, Java userdata, Lua userdata references, and nil are mapped to Java values (LuaState.java:1127-1153).
These are analysis targets, not a claim that every surface is suite-green under TASK-043:
- Parse
require "import"as a normal Luarequirecall, but semantic mode may treat it as enabling Android-Lua import globals for that file/environment (path-scoped activation is the TASK-176 surface; modeled/review-accepted, still awaiting TASK-043 confirmation). import "pkg.Class"introduces the simple class name into the environment.import "pkg.*"introduces a lazy package prefix, not concrete symbols.import { ... }should be modeled over sequential string entries only.- Unqualified global class names can resolve through the current default/imported package list after
require "import". - Inner-class compatibility should support explicit import strings that use
_as$, plus Java class-member nested lookup. - Lua table constructors are semantically overloaded when passed to Java class calls, array constructors, collection constructors, proxy creation, object calls, or Java method arguments.
- Completion should separate known imports from wildcard/package prefixes. Wildcards imply possible class resolution but do not provide a complete class inventory without Android/JDK/dex indexes.
- Authoritative Android framework class availability for static analysis prefers reflective
android.jarwhen configured (macOS dual-path above). Curated static framework resources may fill gaps when the jar is absent; missing jar ⇒ skip reflection, do not invent members. - Direct
luajava.bindClass/newInstancestring-literal helpers and multi-hop local helper aliases are modeled under the TASK-169 / TASK-140 chain (done in metadata); still not final green until TASK-043. - Android-Lua library stub / layout helper surfaces restored by TASK-184 remain subject to TASK-043 suite confirmation. Inventory is not final until that gate.
| Criterion | Where reflected |
|---|---|
| Post-TASK-184 / pre-TASK-043 accuracy | Honesty bound + implications section |
| macOS host paths, Corretto 17, android-35 jar | Host analysis environment table |
Dual-path Downloads + SDK; never G:/ |
Dual-path policy paragraph |
| Inventory not final until TASK-043 | Honesty bound + implications |
| Do not claim final green | Explicit throughout; no suite-green language |
| Docs-only; no Gradle | Stated in honesty bound |
When later review runs verification, use Corretto 17 JAVA_HOME and explicit jvm.androidJar to the SDK android-35 path. That work belongs to TASK-043, not this docs refresh.