From e4c18e3bf3d3f0c2498acca03dc03284ab2636d4 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 10:36:10 +0200 Subject: [PATCH 1/9] Upgrade gradle wrapper --- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++- gradlew | 14 ++++------ gradlew.bat | 32 +++++++---------------- 4 files changed, 18 insertions(+), 32 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 40229 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 delta 35349 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 /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -115,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -173,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -206,15 +203,14 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a21..24c62d5 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -23,8 +23,8 @@ @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,30 +65,18 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% From f44c993419a261f097133f16b282be645f9a4f16 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 10:38:43 +0200 Subject: [PATCH 2/9] Upgrade dependencies --- build.gradle | 4 ++-- gradle/libs.versions.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index ad877e9..6f0d1ee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ plugins { id 'java-library' id 'jacoco' - id "com.github.ben-manes.versions" version "0.51.0" - id "org.sonarqube" version "5.1.0.4882" + id "com.github.ben-manes.versions" version "0.54.0" + id "org.sonarqube" version "7.3.0.8198" } version = '0.0.1' diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d59826d..59bd112 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -junit-jupiter = "5.11.2" +junit-jupiter = "6.0.3" hamcrest = "3.0" -mockito = "5.14.1" +mockito = "5.23.0" [libraries] junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } From 531d9db57a88e0c30207a66ba471b2e4efe729ca Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 10:47:22 +0200 Subject: [PATCH 3/9] Upgrade java & jextract --- build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 6f0d1ee..e22ba43 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ dependencies { java { toolchain { - def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 23 + def javaVersion = project.hasProperty('javaVersion') ? project.getProperty('javaVersion') : 25 languageVersion = JavaLanguageVersion.of(javaVersion) } } @@ -133,7 +133,7 @@ task downloadJextract(type: DownloadTask) { throw new IllegalStateException("Unsupported architecture: ${arch}") } } - sourceUrl = "https://download.java.net/java/early_access/jextract/22/5/openjdk-22-jextract+5-33_${getOs()}-${getArch()}_bin.tar.gz" + sourceUrl = "https://download.java.net/java/early_access/jextract/25/2/openjdk-25-jextract+2-4_${getOs()}-${getArch()}_bin.tar.gz" target = file("${project.buildDir}/jextract.tar.gz") } @@ -143,7 +143,7 @@ task unpackJextract(type: Copy, dependsOn: [tasks.downloadJextract]) { } task generateNativeInterface(type: JextractTask, dependsOn: [tasks.unpackJextract]) { - jextractBinary = new File(tasks.unpackJextract.outputs.files[0], "jextract-22/bin/jextract") + jextractBinary = new File(tasks.unpackJextract.outputs.files[0], "jextract-25/bin/jextract") includeDir = new File(project.rootDir, "lua/src") outputDir = new File(project.buildDir, "generated/sources/jextract/") } From dccf7ac22ac06f4c4c08d1aca7db159bc1f70285 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 10:47:28 +0200 Subject: [PATCH 4/9] Adapt to new gradle api Co-authored-by: Copilot --- buildSrc/src/main/groovy/JextractTask.groovy | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/JextractTask.groovy b/buildSrc/src/main/groovy/JextractTask.groovy index 35db07a..18e262e 100644 --- a/buildSrc/src/main/groovy/JextractTask.groovy +++ b/buildSrc/src/main/groovy/JextractTask.groovy @@ -7,10 +7,19 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.PathSensitivity +import org.gradle.process.ExecOperations +import javax.inject.Inject @CacheableTask class JextractTask extends DefaultTask { + private final ExecOperations execOperations + + @Inject + JextractTask(ExecOperations execOperations) { + this.execOperations = execOperations + } + @InputFile @PathSensitive(PathSensitivity.RELATIVE) File jextractBinary @InputDirectory @PathSensitive(PathSensitivity.RELATIVE) File includeDir @@ -28,7 +37,7 @@ class JextractTask extends DefaultTask { '--header-class-name', 'Lua', "$includeDir/all_lua.h" ] - project.exec { + execOperations.exec { workingDir project.rootDir executable jextractBinary args arguments From 98e521ba10bd9671a7402ab2f92b7cfc6bf05c31 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 11:46:08 +0200 Subject: [PATCH 5/9] Upgrade to lua 5.5 --- lua/Makefile | 106 ++++ lua/src/Makefile | 87 ++-- lua/src/lapi.c | 370 +++++++------- lua/src/lapi.h | 49 +- lua/src/lauxlib.c | 216 +++++--- lua/src/lauxlib.h | 46 +- lua/src/lbaselib.c | 84 ++-- lua/src/lcode.c | 387 ++++++++------ lua/src/lcode.h | 14 +- lua/src/lcorolib.c | 21 +- lua/src/lctype.c | 2 +- lua/src/ldblib.c | 14 +- lua/src/ldebug.c | 101 ++-- lua/src/ldebug.h | 1 + lua/src/ldo.c | 476 +++++++++++------- lua/src/ldo.h | 48 +- lua/src/ldump.c | 159 ++++-- lua/src/lfunc.c | 82 +-- lua/src/lfunc.h | 13 +- lua/src/lgc.c | 815 ++++++++++++++++-------------- lua/src/lgc.h | 126 +++-- lua/src/linit.c | 50 +- lua/src/liolib.c | 62 +-- lua/src/ljumptab.h | 6 +- lua/src/llex.c | 85 ++-- lua/src/llex.h | 8 +- lua/src/llimits.h | 277 +++++----- lua/src/lmathlib.c | 134 +++-- lua/src/lmem.c | 18 +- lua/src/lmem.h | 13 +- lua/src/loadlib.c | 126 +++-- lua/src/lobject.c | 326 ++++++++---- lua/src/lobject.h | 121 +++-- lua/src/lopcodes.c | 46 +- lua/src/lopcodes.h | 156 +++--- lua/src/lopnames.h | 4 +- lua/src/loslib.c | 10 +- lua/src/lparser.c | 744 +++++++++++++++++---------- lua/src/lparser.h | 57 ++- lua/src/lstate.c | 193 ++++--- lua/src/lstate.h | 199 +++++--- lua/src/lstring.c | 123 ++++- lua/src/lstring.h | 32 +- lua/src/lstrlib.c | 292 ++++++----- lua/src/ltable.c | 1200 ++++++++++++++++++++++++++++---------------- lua/src/ltable.h | 147 +++++- lua/src/ltablib.c | 86 ++-- lua/src/ltm.c | 189 +++++-- lua/src/ltm.h | 26 +- lua/src/lua.c | 160 ++++-- lua/src/lua.h | 98 ++-- lua/src/lua.hpp | 3 +- lua/src/luac.c | 26 +- lua/src/luaconf.h | 141 ++---- lua/src/lualib.h | 39 +- lua/src/lundump.c | 253 +++++++--- lua/src/lundump.h | 13 +- lua/src/lutf8lib.c | 92 ++-- lua/src/lvm.c | 553 +++++++++++--------- lua/src/lvm.h | 45 +- lua/src/lzio.c | 37 +- lua/src/lzio.h | 3 +- 62 files changed, 5758 insertions(+), 3652 deletions(-) create mode 100644 lua/Makefile diff --git a/lua/Makefile b/lua/Makefile new file mode 100644 index 0000000..388fa17 --- /dev/null +++ b/lua/Makefile @@ -0,0 +1,106 @@ +# Makefile for installing Lua +# See doc/readme.html for installation and customization instructions. + +# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= + +# Your platform. See PLATS for possible values. +PLAT= guess + +# Where to install. The installation starts in the src and doc directories, +# so take care if INSTALL_TOP is not an absolute path. See the local target. +# You may want to make INSTALL_LMOD and INSTALL_CMOD consistent with +# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h. +INSTALL_TOP= /usr/local +INSTALL_BIN= $(INSTALL_TOP)/bin +INSTALL_INC= $(INSTALL_TOP)/include +INSTALL_LIB= $(INSTALL_TOP)/lib +INSTALL_MAN= $(INSTALL_TOP)/man/man1 +INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V +INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V + +# How to install. If your install program does not support "-p", then +# you may have to run ranlib on the installed liblua.a. +INSTALL= install -p +INSTALL_EXEC= $(INSTALL) -m 0755 +INSTALL_DATA= $(INSTALL) -m 0644 +# +# If you don't have "install" you can use "cp" instead. +# INSTALL= cp -p +# INSTALL_EXEC= $(INSTALL) +# INSTALL_DATA= $(INSTALL) + +# Other utilities. +MKDIR= mkdir -p +RM= rm -f + +# == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= + +# Convenience platforms targets. +PLATS= guess aix bsd c89 freebsd generic ios linux macosx mingw posix solaris + +# What to install. +TO_BIN= lua luac +TO_INC= lua.h luaconf.h lualib.h lauxlib.h lua.hpp +TO_LIB= liblua.a +TO_MAN= lua.1 luac.1 + +# Lua version and release. +V= 5.5 +R= $V.0 + +# Targets start here. +all: $(PLAT) + +$(PLATS) help test clean: + @cd src && $(MAKE) $@ + +install: dummy + cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD) + cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN) + cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC) + cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB) + cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN) + +uninstall: + cd src && cd $(INSTALL_BIN) && $(RM) $(TO_BIN) + cd src && cd $(INSTALL_INC) && $(RM) $(TO_INC) + cd src && cd $(INSTALL_LIB) && $(RM) $(TO_LIB) + cd doc && cd $(INSTALL_MAN) && $(RM) $(TO_MAN) + +local: + $(MAKE) install INSTALL_TOP=../install + +# make may get confused with install/ if it does not support .PHONY. +dummy: + +# Echo config parameters. +echo: + @cd src && $(MAKE) -s echo + @echo "PLAT= $(PLAT)" + @echo "V= $V" + @echo "R= $R" + @echo "TO_BIN= $(TO_BIN)" + @echo "TO_INC= $(TO_INC)" + @echo "TO_LIB= $(TO_LIB)" + @echo "TO_MAN= $(TO_MAN)" + @echo "INSTALL_TOP= $(INSTALL_TOP)" + @echo "INSTALL_BIN= $(INSTALL_BIN)" + @echo "INSTALL_INC= $(INSTALL_INC)" + @echo "INSTALL_LIB= $(INSTALL_LIB)" + @echo "INSTALL_MAN= $(INSTALL_MAN)" + @echo "INSTALL_LMOD= $(INSTALL_LMOD)" + @echo "INSTALL_CMOD= $(INSTALL_CMOD)" + @echo "INSTALL_EXEC= $(INSTALL_EXEC)" + @echo "INSTALL_DATA= $(INSTALL_DATA)" + +# Echo pkg-config data. +pc: + @echo "version=$R" + @echo "prefix=$(INSTALL_TOP)" + @echo "libdir=$(INSTALL_LIB)" + @echo "includedir=$(INSTALL_INC)" + +# Targets that do not create files (not all makes understand .PHONY). +.PHONY: all $(PLATS) help test clean install uninstall local dummy echo pc + +# (end of Makefile) diff --git a/lua/src/Makefile b/lua/src/Makefile index 8f2c5d8..acf33e0 100644 --- a/lua/src/Makefile +++ b/lua/src/Makefile @@ -7,7 +7,7 @@ PLAT= guess CC= gcc -std=gnu99 -CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS) -fPIC +CFLAGS= -O2 -Wall -Wextra $(SYSCFLAGS) $(MYCFLAGS) LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) LIBS= -lm $(SYSLIBS) $(MYLIBS) @@ -30,10 +30,7 @@ CMCFLAGS= # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE ======= -PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris - -LUA_SO= liblua.so -LUA_DYLIB= liblua.dylib +PLATS= guess aix bsd c89 freebsd generic ios linux macosx mingw posix solaris LUA_A= liblua.a CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o @@ -47,7 +44,7 @@ LUAC_T= luac LUAC_O= luac.o ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O) -ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) $(LUA_SO) $(LUA_DYLIB) +ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) ALL_A= $(LUA_A) # Targets start here. @@ -59,16 +56,6 @@ o: $(ALL_O) a: $(ALL_A) -so: $(LUA_SO) - -dylib: $(LUA_DYLIB) - -$(LUA_SO): $(CORE_O) $(LIB_O) - $(CC) -o $@ -shared $? - -$(LUA_DYLIB): $(CORE_O) $(LIB_O) - $(CC) -dynamiclib -o $@ $^ $(LIBS) - $(LUA_A): $(BASE_O) $(AR) $@ $(BASE_O) $(RANLIB) $@ @@ -133,19 +120,14 @@ generic: $(ALL) ios: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS" -Linux linux: linux-noreadline - -linux-noreadline: +Linux linux: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl" -linux-readline: - $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline" - Darwin macos macosx: $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline" mingw: - $(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \ + $(MAKE) "LUA_A=lua55.dll" "LUA_T=lua.exe" \ "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ "SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe $(MAKE) "LUAC_T=luac.exe" luac.exe @@ -174,40 +156,45 @@ lcode.o: lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \ ltable.h lundump.h lvm.h -lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h -lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h llimits.h +lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ - ldo.h lgc.h lstring.h ltable.h lvm.h -lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h + ldo.h lgc.h lstring.h ltable.h lvm.h lopnames.h +lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h -ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \ ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ lparser.h lstring.h ltable.h lundump.h lvm.h -ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \ - ltm.h lzio.h lmem.h lundump.h +ldump.o: ldump.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h lgc.h ltable.h lundump.h lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h -linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h -liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h llimits.h +liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \ lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \ lstring.h ltable.h -lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h -loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \ ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \ lvm.h -lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h -loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h \ + lobject.h +loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ ldo.h lfunc.h lstring.h lgc.h ltable.h @@ -216,23 +203,27 @@ lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ lstring.h ltable.h lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h -lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h -ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h -lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h -luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \ - lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h +lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h llimits.h +luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lapi.h llimits.h \ + lstate.h lobject.h ltm.h lzio.h lmem.h ldebug.h lopcodes.h lopnames.h \ + lundump.h lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \ - lundump.h -lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h -lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ - llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \ - ltable.h lvm.h ljumptab.h -lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \ - lobject.h ltm.h lzio.h + ltable.h lundump.h +lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h \ + llimits.h +lvm.o: lvm.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ + lstring.h ltable.h lvm.h ljumptab.h +lzio.o: lzio.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ + lobject.h ltm.h lzio.h lmem.h # (end of Makefile) diff --git a/lua/src/lapi.c b/lua/src/lapi.c index 332e97d..27fa524 100644 --- a/lua/src/lapi.c +++ b/lua/src/lapi.c @@ -40,10 +40,8 @@ const char lua_ident[] = /* ** Test for a valid index (one that is not the 'nilvalue'). -** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. -** However, it covers the most common cases in a faster way. */ -#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) +#define isvalid(L, o) ((o) != &G(L)->nilvalue) /* test for pseudo index */ @@ -92,7 +90,7 @@ static TValue *index2value (lua_State *L, int idx) { /* ** Convert a valid actual index (not a pseudo-index) to its address. */ -l_sinline StkId index2stack (lua_State *L, int idx) { +static StkId index2stack (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { StkId o = ci->func.p + idx; @@ -129,7 +127,7 @@ LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); - api_checknelems(from, n); + api_checkpop(from, n); api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); from->top.p -= n; @@ -195,10 +193,9 @@ LUA_API void lua_settop (lua_State *L, int idx) { api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } - api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot"); newtop = L->top.p + diff; if (diff < 0 && L->tbclist.p >= newtop) { - lua_assert(hastocloseCfunc(ci->nresults)); + lua_assert(ci->callstatus & CIST_TBC); newtop = luaF_close(L, newtop, CLOSEKTOP, 0); } L->top.p = newtop; /* correct top only after closing any upvalue */ @@ -210,7 +207,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { StkId level; lua_lock(L); level = index2stack(L, idx); - api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, + api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level), "no variable to close at given level"); level = luaF_close(L, level, CLOSEKTOP, 0); setnilvalue(s2v(level)); @@ -224,7 +221,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { ** Note that we move(copy) only the value inside the stack. ** (We do not move additional fields that may exist.) */ -l_sinline void reverse (lua_State *L, StkId from, StkId to) { +static void reverse (lua_State *L, StkId from, StkId to) { for (; from < to; from++, to--) { TValue temp; setobj(L, &temp, s2v(from)); @@ -243,6 +240,7 @@ LUA_API void lua_rotate (lua_State *L, int idx, int n) { lua_lock(L); t = L->top.p - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ + api_check(L, L->tbclist.p < p, "moving a to-be-closed slot"); api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ reverse(L, p, m); /* reverse the prefix with length 'n' */ @@ -335,15 +333,15 @@ LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { LUA_API void lua_arith (lua_State *L, int op) { lua_lock(L); if (op != LUA_OPUNM && op != LUA_OPBNOT) - api_checknelems(L, 2); /* all other operations expect two operands */ + api_checkpop(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ - api_checknelems(L, 1); + api_checkpop(L, 1); setobjs2s(L, L->top.p, L->top.p - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); - L->top.p--; /* remove second operand */ + L->top.p--; /* pop second operand */ lua_unlock(L); } @@ -368,6 +366,18 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { } +LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff) { + const TValue *o = index2value(L, idx); + if (ttisnumber(o)) { + unsigned len = luaO_tostringbuff(o, buff); + buff[len++] = '\0'; /* add final zero */ + return len; + } + else + return 0; +} + + LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { size_t sz = luaO_str2num(s, s2v(L->top.p)); if (sz != 0) @@ -416,20 +426,27 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { luaC_checkGC(L); o = index2value(L, idx); /* previous call may reallocate the stack */ } - if (len != NULL) - *len = tsslen(tsvalue(o)); lua_unlock(L); - return getstr(tsvalue(o)); + if (len != NULL) + return getlstr(tsvalue(o), *len); + else + return getstr(tsvalue(o)); } LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { const TValue *o = index2value(L, idx); switch (ttypetag(o)) { - case LUA_VSHRSTR: return tsvalue(o)->shrlen; - case LUA_VLNGSTR: return tsvalue(o)->u.lnglen; - case LUA_VUSERDATA: return uvalue(o)->len; - case LUA_VTABLE: return luaH_getn(hvalue(o)); + case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen); + case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen); + case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len); + case LUA_VTABLE: { + lua_Unsigned res; + lua_lock(L); + res = luaH_getn(L, hvalue(o)); + lua_unlock(L); + return res; + } default: return 0; } } @@ -467,7 +484,7 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) { /* ** Returns a pointer to the internal representation of an object. -** Note that ANSI C does not allow the conversion of a pointer to +** Note that ISO C does not allow the conversion of a pointer to ** function to a 'void*', so the conversion here goes through ** a 'size_t'. (As the returned pointer is only informative, this ** conversion should not be a problem.) @@ -535,6 +552,21 @@ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { } +LUA_API const char *lua_pushexternalstring (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud) { + TString *ts; + lua_lock(L); + api_check(L, len <= MAX_SIZE, "string too large"); + api_check(L, s[len] == '\0', "string not ending with zero"); + ts = luaS_newextlstr (L, s, len, falloc, ud); + setsvalue2s(L, L->top.p, ts); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getstr(ts); +} + + LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) @@ -567,9 +599,7 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); - va_start(argp, fmt); - ret = luaO_pushvfstring(L, fmt, argp); - va_end(argp); + pushvfstring(L, argp, fmt, ret); luaC_checkGC(L); lua_unlock(L); return ret; @@ -583,17 +613,18 @@ LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { api_incr_top(L); } else { + int i; CClosure *cl; - api_checknelems(L, n); + api_checkpop(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; - L->top.p -= n; - while (n--) { - setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n)); + for (i = 0; i < n; i++) { + setobj2n(L, &cl->upvalue[i], s2v(L->top.p - n + i)); /* does not need barrier because closure is white */ lua_assert(iswhite(cl)); } + L->top.p -= n; setclCvalue(L, s2v(L->top.p), cl); api_incr_top(L); luaC_checkGC(L); @@ -626,7 +657,7 @@ LUA_API int lua_pushthread (lua_State *L) { setthvalue(L, s2v(L->top.p), L); api_incr_top(L); lua_unlock(L); - return (G(L)->mainthread == L); + return (mainthread(G(L)) == L); } @@ -636,53 +667,54 @@ LUA_API int lua_pushthread (lua_State *L) { */ -l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *slot; +static int auxgetstr (lua_State *L, const TValue *t, const char *k) { + lu_byte tag; TString *str = luaS_new(L, k); - if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - setobj2s(L, L->top.p, slot); + luaV_fastget(t, str, s2v(L->top.p), luaH_getstr, tag); + if (!tagisempty(tag)) api_incr_top(L); - } else { setsvalue2s(L, L->top.p, str); api_incr_top(L); - luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + tag = luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, tag); } lua_unlock(L); - return ttype(s2v(L->top.p - 1)); + return novariant(tag); } /* -** Get the global table in the registry. Since all predefined -** indices in the registry were inserted right when the registry -** was created and never removed, they must always be in the array -** part of the registry. +** The following function assumes that the registry cannot be a weak +** table; so, an emergency collection while using the global table +** cannot collect it. */ -#define getGtable(L) \ - (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1]) +static void getGlobalTable (lua_State *L, TValue *gt) { + Table *registry = hvalue(&G(L)->l_registry); + lu_byte tag = luaH_getint(registry, LUA_RIDX_GLOBALS, gt); + (void)tag; /* avoid not-used warnings when checks are off */ + api_check(L, novariant(tag) == LUA_TTABLE, "global table must exist"); +} LUA_API int lua_getglobal (lua_State *L, const char *name) { - const TValue *G; + TValue gt; lua_lock(L); - G = getGtable(L); - return auxgetstr(L, G, name); + getGlobalTable(L, >); + return auxgetstr(L, >, name); } LUA_API int lua_gettable (lua_State *L, int idx) { - const TValue *slot; + lu_byte tag; TValue *t; lua_lock(L); + api_checkpop(L, 1); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) { - setobj2s(L, L->top.p - 1, slot); - } - else - luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot); + luaV_fastget(t, s2v(L->top.p - 1), s2v(L->top.p - 1), luaH_get, tag); + if (tagisempty(tag)) + tag = luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, tag); lua_unlock(L); - return ttype(s2v(L->top.p - 1)); + return novariant(tag); } @@ -694,35 +726,31 @@ LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { TValue *t; - const TValue *slot; + lu_byte tag; lua_lock(L); t = index2value(L, idx); - if (luaV_fastgeti(L, t, n, slot)) { - setobj2s(L, L->top.p, slot); - } - else { - TValue aux; - setivalue(&aux, n); - luaV_finishget(L, t, &aux, L->top.p, slot); + luaV_fastgeti(t, n, s2v(L->top.p), tag); + if (tagisempty(tag)) { + TValue key; + setivalue(&key, n); + tag = luaV_finishget(L, t, &key, L->top.p, tag); } api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top.p - 1)); + return novariant(tag); } -l_sinline int finishrawget (lua_State *L, const TValue *val) { - if (isempty(val)) /* avoid copying empty items to the stack */ +static int finishrawget (lua_State *L, lu_byte tag) { + if (tagisempty(tag)) /* avoid copying empty items to the stack */ setnilvalue(s2v(L->top.p)); - else - setobj2s(L, L->top.p, val); api_incr_top(L); lua_unlock(L); - return ttype(s2v(L->top.p - 1)); + return novariant(tag); } -static Table *gettable (lua_State *L, int idx) { +l_sinline Table *gettable (lua_State *L, int idx) { TValue *t = index2value(L, idx); api_check(L, ttistable(t), "table expected"); return hvalue(t); @@ -731,21 +759,23 @@ static Table *gettable (lua_State *L, int idx) { LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; - const TValue *val; + lu_byte tag; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); - val = luaH_get(t, s2v(L->top.p - 1)); - L->top.p--; /* remove key */ - return finishrawget(L, val); + tag = luaH_get(t, s2v(L->top.p - 1), s2v(L->top.p - 1)); + L->top.p--; /* pop key */ + return finishrawget(L, tag); } LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { Table *t; + lu_byte tag; lua_lock(L); t = gettable(L, idx); - return finishrawget(L, luaH_getint(t, n)); + luaH_fastgeti(t, n, s2v(L->top.p), tag); + return finishrawget(L, tag); } @@ -755,7 +785,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { lua_lock(L); t = gettable(L, idx); setpvalue(&k, cast_voidp(p)); - return finishrawget(L, luaH_get(t, &k)); + return finishrawget(L, luaH_get(t, &k, s2v(L->top.p))); } @@ -766,7 +796,7 @@ LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { sethvalue2s(L, L->top.p, t); api_incr_top(L); if (narray > 0 || nrec > 0) - luaH_resize(L, t, narray, nrec); + luaH_resize(L, t, cast_uint(narray), cast_uint(nrec)); luaC_checkGC(L); lua_unlock(L); } @@ -827,17 +857,18 @@ LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *slot; + int hres; TString *str = luaS_new(L, k); - api_checknelems(L, 1); - if (luaV_fastget(L, t, str, slot, luaH_getstr)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); + api_checkpop(L, 1); + luaV_fastset(t, str, s2v(L->top.p - 1), hres, luaH_psetstr); + if (hres == HOK) { + luaV_finishfastset(L, t, s2v(L->top.p - 1)); L->top.p--; /* pop value */ } else { setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); - luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot); + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), hres); L->top.p -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ @@ -845,24 +876,24 @@ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { LUA_API void lua_setglobal (lua_State *L, const char *name) { - const TValue *G; + TValue gt; lua_lock(L); /* unlock done in 'auxsetstr' */ - G = getGtable(L); - auxsetstr(L, G, name); + getGlobalTable(L, >); + auxsetstr(L, >, name); } LUA_API void lua_settable (lua_State *L, int idx) { TValue *t; - const TValue *slot; + int hres; lua_lock(L); - api_checknelems(L, 2); + api_checkpop(L, 2); t = index2value(L, idx); - if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); - } + luaV_fastset(t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres, luaH_pset); + if (hres == HOK) + luaV_finishfastset(L, t, s2v(L->top.p - 1)); else - luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot); + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres); L->top.p -= 2; /* pop index and value */ lua_unlock(L); } @@ -876,17 +907,17 @@ LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { TValue *t; - const TValue *slot; + int hres; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = index2value(L, idx); - if (luaV_fastgeti(L, t, n, slot)) { - luaV_finishfastset(L, t, slot, s2v(L->top.p - 1)); - } + luaV_fastseti(t, n, s2v(L->top.p - 1), hres); + if (hres == HOK) + luaV_finishfastset(L, t, s2v(L->top.p - 1)); else { - TValue aux; - setivalue(&aux, n); - luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot); + TValue temp; + setivalue(&temp, n); + luaV_finishset(L, t, &temp, s2v(L->top.p - 1), hres); } L->top.p--; /* pop value */ lua_unlock(L); @@ -896,7 +927,7 @@ LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { Table *t; lua_lock(L); - api_checknelems(L, n); + api_checkpop(L, n); t = gettable(L, idx); luaH_set(L, t, key, s2v(L->top.p - 1)); invalidateTMcache(t); @@ -921,7 +952,7 @@ LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); luaH_setint(L, t, n, s2v(L->top.p - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); @@ -934,7 +965,7 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); obj = index2value(L, objindex); if (ttisnil(s2v(L->top.p - 1))) mt = NULL; @@ -974,7 +1005,7 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) @@ -996,9 +1027,11 @@ LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { #define checkresults(L,na,nr) \ - api_check(L, (nr) == LUA_MULTRET \ + (api_check(L, (nr) == LUA_MULTRET \ || (L->ci->top.p - L->top.p >= (nr) - (na)), \ - "results from function overflow current stack size") + "results from function overflow current stack size"), \ + api_check(L, LUA_MULTRET <= (nr) && (nr) <= MAXRESULTS, \ + "invalid number of results")) LUA_API void lua_callk (lua_State *L, int nargs, int nresults, @@ -1007,7 +1040,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults, lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); - api_checknelems(L, nargs+1); + api_checkpop(L, nargs + 1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); func = L->top.p - (nargs+1); @@ -1043,12 +1076,12 @@ static void f_call (lua_State *L, void *ud) { LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k) { struct CallS c; - int status; + TStatus status; ptrdiff_t func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); - api_checknelems(L, nargs+1); + api_checkpop(L, nargs + 1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); if (errfunc == 0) @@ -1071,7 +1104,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, ci->u2.funcidx = cast_int(savestack(L, c.func)); ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; - setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ + setoah(ci, L->allowhook); /* save value of 'allowhook' */ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; @@ -1080,14 +1113,14 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, } adjustresults(L, nresults); lua_unlock(L); - return status; + return APIstatus(status); } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode) { ZIO z; - int status; + TStatus status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); @@ -1096,34 +1129,38 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ - const TValue *gt = getGtable(L); + TValue gt; + getGlobalTable(L, >); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ - setobj(L, f->upvals[0]->v.p, gt); - luaC_barrier(L, f->upvals[0], gt); + setobj(L, f->upvals[0]->v.p, >); + luaC_barrier(L, f->upvals[0], >); } } lua_unlock(L); - return status; + return APIstatus(status); } +/* +** Dump a Lua function, calling 'writer' to write its parts. Ensure +** the stack returns with its original size. +*/ LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { int status; - TValue *o; + ptrdiff_t otop = savestack(L, L->top.p); /* original top */ + TValue *f = s2v(L->top.p - 1); /* function to be dumped */ lua_lock(L); - api_checknelems(L, 1); - o = s2v(L->top.p - 1); - if (isLfunction(o)) - status = luaU_dump(L, getproto(o), writer, data, strip); - else - status = 1; + api_checkpop(L, 1); + api_check(L, isLfunction(f), "Lua function expected"); + status = luaU_dump(L, clLvalue(f)->p, writer, data, strip); + L->top.p = restorestack(L, otop); /* restore top */ lua_unlock(L); return status; } LUA_API int lua_status (lua_State *L) { - return L->status; + return APIstatus(L->status); } @@ -1134,7 +1171,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { va_list argp; int res = 0; global_State *g = G(L); - if (g->gcstp & GCSTPGC) /* internal stop? */ + if (g->gcstp & (GCSTPGC | GCSTPCLS)) /* internal stop? */ return -1; /* all options are invalid when stopped */ lua_lock(L); va_start(argp, what); @@ -1145,7 +1182,7 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { } case LUA_GCRESTART: { luaE_setdebt(g, 0); - g->gcstp = 0; /* (GCSTPGC must be already zero here) */ + g->gcstp = 0; /* (other bits must be zero here) */ break; } case LUA_GCCOLLECT: { @@ -1162,34 +1199,17 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { break; } case LUA_GCSTEP: { - int data = va_arg(argp, int); - l_mem debt = 1; /* =1 to signal that it did an actual step */ lu_byte oldstp = g->gcstp; - g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ - if (data == 0) { - luaE_setdebt(g, 0); /* do a basic step */ - luaC_step(L); - } - else { /* add 'data' to total debt */ - debt = cast(l_mem, data) * 1024 + g->GCdebt; - luaE_setdebt(g, debt); - luaC_checkGC(L); - } - g->gcstp = oldstp; /* restore previous state */ - if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ + l_mem n = cast(l_mem, va_arg(argp, size_t)); + int work = 0; /* true if GC did some work */ + g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ + if (n <= 0) + n = g->GCdebt; /* force to run one basic step */ + luaE_setdebt(g, g->GCdebt - n); + luaC_condGC(L, (void)0, work = 1); + if (work && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ - break; - } - case LUA_GCSETPAUSE: { - int data = va_arg(argp, int); - res = getgcparam(g->gcpause); - setgcparam(g->gcpause, data); - break; - } - case LUA_GCSETSTEPMUL: { - int data = va_arg(argp, int); - res = getgcparam(g->gcstepmul); - setgcparam(g->gcstepmul, data); + g->gcstp = oldstp; /* restore previous state */ break; } case LUA_GCISRUNNING: { @@ -1197,30 +1217,24 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { break; } case LUA_GCGEN: { - int minormul = va_arg(argp, int); - int majormul = va_arg(argp, int); - res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; - if (minormul != 0) - g->genminormul = minormul; - if (majormul != 0) - setgcparam(g->genmajormul, majormul); - luaC_changemode(L, KGC_GEN); + res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; + luaC_changemode(L, KGC_GENMINOR); break; } case LUA_GCINC: { - int pause = va_arg(argp, int); - int stepmul = va_arg(argp, int); - int stepsize = va_arg(argp, int); - res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; - if (pause != 0) - setgcparam(g->gcpause, pause); - if (stepmul != 0) - setgcparam(g->gcstepmul, stepmul); - if (stepsize != 0) - g->gcstepsize = stepsize; + res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; luaC_changemode(L, KGC_INC); break; } + case LUA_GCPARAM: { + int param = va_arg(argp, int); + int value = va_arg(argp, int); + api_check(L, 0 <= param && param < LUA_GCPN, "invalid parameter"); + res = cast_int(luaO_applyparam(g->gcparams[param], 100)); + if (value >= 0) + g->gcparams[param] = luaO_codeparam(cast_uint(value)); + break; + } default: res = -1; /* invalid option */ } va_end(argp); @@ -1239,7 +1253,7 @@ LUA_API int lua_error (lua_State *L) { TValue *errobj; lua_lock(L); errobj = s2v(L->top.p - 1); - api_checknelems(L, 1); + api_checkpop(L, 1); /* error object is the memory error message? */ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) luaM_error(L); /* raise a memory error */ @@ -1254,30 +1268,25 @@ LUA_API int lua_next (lua_State *L, int idx) { Table *t; int more; lua_lock(L); - api_checknelems(L, 1); + api_checkpop(L, 1); t = gettable(L, idx); more = luaH_next(L, t, L->top.p - 1); - if (more) { + if (more) api_incr_top(L); - } else /* no more elements */ - L->top.p -= 1; /* remove key */ + L->top.p--; /* pop key */ lua_unlock(L); return more; } LUA_API void lua_toclose (lua_State *L, int idx) { - int nresults; StkId o; lua_lock(L); o = index2stack(L, idx); - nresults = L->ci->nresults; api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ - if (!hastocloseCfunc(nresults)) /* function not marked yet? */ - L->ci->nresults = codeNresults(nresults); /* mark it */ - lua_assert(hastocloseCfunc(L->ci->nresults)); + L->ci->callstatus |= CIST_TBC; /* mark that function has TBC slots */ lua_unlock(L); } @@ -1285,13 +1294,14 @@ LUA_API void lua_toclose (lua_State *L, int idx) { LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); - if (n > 0) + if (n > 0) { luaV_concat(L, n); + luaC_checkGC(L); + } else { /* nothing to concatenate */ setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ api_incr_top(L); } - luaC_checkGC(L); lua_unlock(L); } @@ -1343,8 +1353,8 @@ void lua_warning (lua_State *L, const char *msg, int tocont) { LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { Udata *u; lua_lock(L); - api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); - u = luaS_newudata(L, size, nuvalue); + api_check(L, 0 <= nuvalue && nuvalue < SHRT_MAX, "invalid value"); + u = luaS_newudata(L, size, cast(unsigned short, nuvalue)); setuvalue(L, s2v(L->top.p), u); api_incr_top(L); luaC_checkGC(L); diff --git a/lua/src/lapi.h b/lua/src/lapi.h index a742427..9b54534 100644 --- a/lua/src/lapi.h +++ b/lua/src/lapi.h @@ -12,10 +12,29 @@ #include "lstate.h" +#if defined(LUA_USE_APICHECK) +#include +#define api_check(l,e,msg) assert(e) +#else /* for testing */ +#define api_check(l,e,msg) ((void)(l), lua_assert((e) && msg)) +#endif + + + /* Increments 'L->top.p', checking for stack overflows */ -#define api_incr_top(L) {L->top.p++; \ - api_check(L, L->top.p <= L->ci->top.p, \ - "stack overflow");} +#define api_incr_top(L) \ + (L->top.p++, api_check(L, L->top.p <= L->ci->top.p, "stack overflow")) + + +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + /* @@ -30,23 +49,17 @@ /* Ensure the stack has at least 'n' elements */ #define api_checknelems(L,n) \ - api_check(L, (n) < (L->top.p - L->ci->func.p), \ - "not enough elements in the stack") + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") -/* -** To reduce the overhead of returning from C functions, the presence of -** to-be-closed variables in these functions is coded in the CallInfo's -** field 'nresults', in a way that functions with no to-be-closed variables -** with zero, one, or "all" wanted results have no overhead. Functions -** with other number of wanted results, as well as functions with -** variables to be closed, have an extra check. +/* Ensure the stack has at least 'n' elements to be popped. (Some +** functions only update a slot after checking it for popping, but that +** is only an optimization for a pop followed by a push.) */ - -#define hastocloseCfunc(n) ((n) < LUA_MULTRET) - -/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ -#define codeNresults(n) (-(n) - 3) -#define decodeNresults(n) (-(n) - 3) +#define api_checkpop(L,n) \ + api_check(L, (n) < L->top.p - L->ci->func.p && \ + L->tbclist.p < L->top.p - (n), \ + "not enough free elements in the stack") #endif diff --git a/lua/src/lauxlib.c b/lua/src/lauxlib.c index 923105e..7cf90cb 100644 --- a/lua/src/lauxlib.c +++ b/lua/src/lauxlib.c @@ -25,12 +25,7 @@ #include "lua.h" #include "lauxlib.h" - - -#if !defined(MAX_SIZET) -/* maximum value for size_t */ -#define MAX_SIZET ((size_t)(~(size_t)0)) -#endif +#include "llimits.h" /* @@ -99,14 +94,14 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { static void pushfuncname (lua_State *L, lua_Debug *ar) { - if (pushglobalfuncname(L, ar)) { /* try first a global name */ - lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); - lua_remove(L, -2); /* remove name */ - } - else if (*ar->namewhat != '\0') /* is there a name from code? */ + if (*ar->namewhat != '\0') /* is there a name from code? */ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); + else if (pushglobalfuncname(L, ar)) { /* try a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); else /* nothing left... */ @@ -175,19 +170,27 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { lua_Debug ar; + const char *argword; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); - lua_getinfo(L, "n", &ar); - if (strcmp(ar.namewhat, "method") == 0) { - arg--; /* do not count 'self' */ - if (arg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling '%s' on bad self (%s)", - ar.name, extramsg); + lua_getinfo(L, "nt", &ar); + if (arg <= ar.extraargs) /* error in an extra argument? */ + argword = "extra argument"; + else { + arg -= ar.extraargs; /* do not count extra arguments */ + if (strcmp(ar.namewhat, "method") == 0) { /* colon syntax? */ + arg--; /* do not count (extra) self argument */ + if (arg == 0) /* error in self argument? */ + return luaL_error(L, "calling '%s' on bad self (%s)", + ar.name, extramsg); + /* else go through; error in a regular argument */ + } + argword = "argument"; } if (ar.name == NULL) ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; - return luaL_error(L, "bad argument #%d to '%s' (%s)", - arg, ar.name, extramsg); + return luaL_error(L, "bad %s #%d to '%s' (%s)", + argword, arg, ar.name, extramsg); } @@ -230,7 +233,7 @@ LUALIB_API void luaL_where (lua_State *L, int level) { /* ** Again, the use of 'lua_pushvfstring' ensures this function does ** not need reserved stack space when called. (At worst, it generates -** an error with "stack overflow" instead of the given message.) +** a memory error instead of the given message.) */ LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { va_list argp; @@ -473,18 +476,27 @@ typedef struct UBox { } UBox; +/* Resize the buffer used by a box. Optimize for the common case of +** resizing to the old size. (For instance, __gc will resize the box +** to 0 even after it was closed. 'pushresult' may also resize it to a +** final size that is equal to the one set when the buffer was created.) +*/ static void *resizebox (lua_State *L, int idx, size_t newsize) { - void *ud; - lua_Alloc allocf = lua_getallocf(L, &ud); UBox *box = (UBox *)lua_touserdata(L, idx); - void *temp = allocf(ud, box->box, box->bsize, newsize); - if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ - lua_pushliteral(L, "not enough memory"); - lua_error(L); /* raise a memory error */ + if (box->bsize == newsize) /* not changing size? */ + return box->box; /* keep the buffer */ + else { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + box->box = temp; + box->bsize = newsize; + return temp; } - box->box = temp; - box->bsize = newsize; - return temp; } @@ -529,15 +541,17 @@ static void newbox (lua_State *L) { /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' -** bytes. (The test for "not big enough" also gets the case when the -** computation of 'newsize' overflows.) +** bytes plus one for a terminating zero. */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { - size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */ - if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ - return luaL_error(B->L, "buffer too large"); - if (newsize < B->n + sz) /* not big enough? */ - newsize = B->n + sz; + size_t newsize = B->size; + if (l_unlikely(sz >= MAX_SIZE - B->n)) + return cast_sizet(luaL_error(B->L, "resulting string too large")); + /* else B->n + sz + 1 <= MAX_SIZE */ + if (newsize <= MAX_SIZE/3 * 2) /* no overflow? */ + newsize += (newsize >> 1); /* new size *= 1.5 */ + if (newsize < B->n + sz + 1) /* not big enough? */ + newsize = B->n + sz + 1; return newsize; } @@ -597,9 +611,23 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; checkbufferlevel(B, -1); - lua_pushlstring(L, B->b, B->n); - if (buffonstack(B)) + if (!buffonstack(B)) /* using static buffer? */ + lua_pushlstring(L, B->b, B->n); /* save result as regular string */ + else { /* reuse buffer already allocated */ + UBox *box = (UBox *)lua_touserdata(L, -1); + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); /* function to free buffer */ + size_t len = B->n; /* final string length */ + char *s; + resizebox(L, -1, len + 1); /* adjust box size to content size */ + s = (char*)box->box; /* final buffer address */ + s[len] = '\0'; /* add ending zero */ + /* clear box, as Lua will take control of the buffer */ + box->bsize = 0; box->box = NULL; + lua_pushexternalstring(L, s, len, allocf, ud); lua_closeslot(L, -2); /* close the box */ + lua_gc(L, LUA_GCSTEP, len); + } lua_remove(L, -2); /* remove box or placeholder from the stack */ } @@ -653,13 +681,10 @@ LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { ** ======================================================= */ -/* index of free-list header (after the predefined values) */ -#define freelist (LUA_RIDX_LAST + 1) - /* -** The previously freed references form a linked list: -** t[freelist] is the index of a first free index, or zero if list is -** empty; t[t[freelist]] is the index of the second element; etc. +** The previously freed references form a linked list: t[1] is the index +** of a first free index, t[t[1]] is the index of the second element, +** etc. A zero signals the end of the list. */ LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; @@ -668,19 +693,18 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); - if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ + if (lua_rawgeti(L, t, 1) == LUA_TNUMBER) /* already initialized? */ + ref = (int)lua_tointeger(L, -1); /* ref = t[1] */ + else { /* first access */ + lua_assert(!lua_toboolean(L, -1)); /* must be nil or false */ ref = 0; /* list is empty */ lua_pushinteger(L, 0); /* initialize as an empty list */ - lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ - } - else { /* already initialized */ - lua_assert(lua_isinteger(L, -1)); - ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ + lua_rawseti(L, t, 1); /* ref = t[1] = 0 */ } lua_pop(L, 1); /* remove element from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ + lua_rawseti(L, t, 1); /* (t[1] = t[ref]) */ } else /* no free elements */ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ @@ -692,11 +716,11 @@ LUALIB_API int luaL_ref (lua_State *L, int t) { LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = lua_absindex(L, t); - lua_rawgeti(L, t, freelist); + lua_rawgeti(L, t, 1); lua_assert(lua_isinteger(L, -1)); - lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ + lua_rawseti(L, t, ref); /* t[ref] = t[1] */ lua_pushinteger(L, ref); - lua_rawseti(L, t, freelist); /* t[freelist] = ref */ + lua_rawseti(L, t, 1); /* t[1] = ref */ } } @@ -710,7 +734,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { */ typedef struct LoadF { - int n; /* number of pre-read characters */ + unsigned n; /* number of pre-read characters */ FILE *f; /* file being read */ char buff[BUFSIZ]; /* area for reading file */ } LoadF; @@ -718,7 +742,7 @@ typedef struct LoadF { static const char *getF (lua_State *L, void *ud, size_t *size) { LoadF *lf = (LoadF *)ud; - (void)L; /* not used */ + UNUSED(L); if (lf->n > 0) { /* are there pre-read characters to be read? */ *size = lf->n; /* return them (chars already in buffer) */ lf->n = 0; /* no more pre-read characters */ @@ -810,10 +834,10 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, } } if (c != EOF) - lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ - errno = 0; + lf.buff[lf.n++] = cast_char(c); /* 'c' is the first character */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); + errno = 0; /* no useful error number until here */ if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ @@ -832,7 +856,7 @@ typedef struct LoadS { static const char *getS (lua_State *L, void *ud, size_t *size) { LoadS *ls = (LoadS *)ud; - (void)L; /* not used */ + UNUSED(L); if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; @@ -904,10 +928,9 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { else { switch (lua_type(L, idx)) { case LUA_TNUMBER: { - if (lua_isinteger(L, idx)) - lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); - else - lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); + char buff[LUA_N2SBUFFSZ]; + lua_numbertocstring(L, idx, buff); + lua_pushstring(L, buff); break; } case LUA_TSTRING: @@ -1005,7 +1028,7 @@ LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, const char *wild; size_t l = strlen(p); while ((wild = strstr(s, p)) != NULL) { - luaL_addlstring(b, s, wild - s); /* push prefix */ + luaL_addlstring(b, s, ct_diff2sz(wild - s)); /* push prefix */ luaL_addstring(b, r); /* push replacement in place of pattern */ s = wild + l; /* continue after 'p' */ } @@ -1023,8 +1046,8 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, } -static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { - (void)ud; (void)osize; /* not used */ +void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + UNUSED(ud); UNUSED(osize); if (nsize == 0) { free(ptr); return NULL; @@ -1035,7 +1058,7 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { /* -** Standard panic funcion just prints an error message. The test +** Standard panic function just prints an error message. The test ** with 'lua_type' avoids possible memory errors in 'lua_tostring'. */ static int panic (lua_State *L) { @@ -1105,11 +1128,64 @@ static void warnfon (void *ud, const char *message, int tocont) { } -LUALIB_API lua_State *luaL_newstate (void) { - lua_State *L = lua_newstate(l_alloc, NULL); + +/* +** A function to compute an unsigned int with some level of +** randomness. Rely on Address Space Layout Randomization (if present) +** and the current time. +*/ +#if !defined(luai_makeseed) + +#include + + +/* Size for the buffer, in bytes */ +#define BUFSEEDB (sizeof(void*) + sizeof(time_t)) + +/* Size for the buffer in int's, rounded up */ +#define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int)) + +/* +** Copy the contents of variable 'v' into the buffer pointed by 'b'. +** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.) +*/ +#define addbuff(b,v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v)) + + +static unsigned int luai_makeseed (void) { + unsigned int buff[BUFSEED]; + unsigned int res; + unsigned int i; + time_t t = time(NULL); + char *b = (char*)buff; + addbuff(b, b); /* local variable's address */ + addbuff(b, t); /* time */ + /* fill (rare but possible) remain of the buffer with zeros */ + memset(b, 0, sizeof(buff) - BUFSEEDB); + res = buff[0]; + for (i = 1; i < BUFSEED; i++) + res ^= (res >> 3) + (res << 7) + buff[i]; + return res; +} + +#endif + + +LUALIB_API unsigned int luaL_makeseed (lua_State *L) { + UNUSED(L); + return luai_makeseed(); +} + + +/* +** Use the name with parentheses so that headers can redefine it +** as a macro. +*/ +LUALIB_API lua_State *(luaL_newstate) (void) { + lua_State *L = lua_newstate(luaL_alloc, NULL, luaL_makeseed(NULL)); if (l_likely(L)) { lua_atpanic(L, &panic); - lua_setwarnf(L, warnfoff, L); /* default is warnings off */ + lua_setwarnf(L, warnfon, L); } return L; } diff --git a/lua/src/lauxlib.h b/lua/src/lauxlib.h index 5b977e2..7f1d3ca 100644 --- a/lua/src/lauxlib.h +++ b/lua/src/lauxlib.h @@ -81,6 +81,9 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); +LUALIB_API void *luaL_alloc (void *ud, void *ptr, size_t osize, + size_t nsize); + /* predefined references */ #define LUA_NOREF (-2) @@ -100,6 +103,8 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate) (void); +LUALIB_API unsigned luaL_makeseed (lua_State *L); + LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, @@ -163,21 +168,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, /* push the value used to represent failure/error */ -#define luaL_pushfail(L) lua_pushnil(L) - - -/* -** Internal assertions for in-house debugging -*/ -#if !defined(lua_assert) - -#if defined LUAI_ASSERT - #include - #define lua_assert(c) assert(c) +#if defined(LUA_FAILISFALSE) +#define luaL_pushfail(L) lua_pushboolean(L, 0) #else - #define lua_assert(c) ((void)0) -#endif - +#define luaL_pushfail(L) lua_pushnil(L) #endif @@ -249,30 +243,6 @@ typedef struct luaL_Stream { /* }====================================================== */ -/* -** {================================================================== -** "Abstraction Layer" for basic report of messages and errors -** =================================================================== -*/ - -/* print a string */ -#if !defined(lua_writestring) -#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) -#endif - -/* print a newline and flush the output */ -#if !defined(lua_writeline) -#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) -#endif - -/* print an error message */ -#if !defined(lua_writestringerror) -#define lua_writestringerror(s,p) \ - (fprintf(stderr, (s), (p)), fflush(stderr)) -#endif - -/* }================================================================== */ - /* ** {============================================================ diff --git a/lua/src/lbaselib.c b/lua/src/lbaselib.c index 1d60c9d..891bb90 100644 --- a/lua/src/lbaselib.c +++ b/lua/src/lbaselib.c @@ -19,6 +19,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" static int luaB_print (lua_State *L) { @@ -57,21 +58,22 @@ static int luaB_warn (lua_State *L) { #define SPACECHARS " \f\n\r\t\v" -static const char *b_str2int (const char *s, int base, lua_Integer *pn) { +static const char *b_str2int (const char *s, unsigned base, lua_Integer *pn) { lua_Unsigned n = 0; int neg = 0; s += strspn(s, SPACECHARS); /* skip initial spaces */ if (*s == '-') { s++; neg = 1; } /* handle sign */ else if (*s == '+') s++; - if (!isalnum((unsigned char)*s)) /* no digit? */ + if (!isalnum(cast_uchar(*s))) /* no digit? */ return NULL; do { - int digit = (isdigit((unsigned char)*s)) ? *s - '0' - : (toupper((unsigned char)*s) - 'A') + 10; + unsigned digit = cast_uint(isdigit(cast_uchar(*s)) + ? *s - '0' + : (toupper(cast_uchar(*s)) - 'A') + 10); if (digit >= base) return NULL; /* invalid numeral */ n = n * base + digit; s++; - } while (isalnum((unsigned char)*s)); + } while (isalnum(cast_uchar(*s))); s += strspn(s, SPACECHARS); /* skip trailing spaces */ *pn = (lua_Integer)((neg) ? (0u - n) : n); return s; @@ -101,7 +103,7 @@ static int luaB_tonumber (lua_State *L) { luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - if (b_str2int(s, (int)base, &n) == s + l) { + if (b_str2int(s, cast_uint(base), &n) == s + l) { lua_pushinteger(L, n); return 1; } /* else not a number */ @@ -158,7 +160,7 @@ static int luaB_rawlen (lua_State *L) { int t = lua_type(L, 1); luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, "table or string"); - lua_pushinteger(L, lua_rawlen(L, 1)); + lua_pushinteger(L, l_castU2S(lua_rawlen(L, 1))); return 1; } @@ -198,11 +200,11 @@ static int pushmode (lua_State *L, int oldmode) { static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", - "count", "step", "setpause", "setstepmul", - "isrunning", "generational", "incremental", NULL}; - static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, - LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, - LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; + "count", "step", "isrunning", "generational", "incremental", + "param", NULL}; + static const char optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC, + LUA_GCPARAM}; int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; switch (o) { case LUA_GCCOUNT: { @@ -213,20 +215,12 @@ static int luaB_collectgarbage (lua_State *L) { return 1; } case LUA_GCSTEP: { - int step = (int)luaL_optinteger(L, 2, 0); - int res = lua_gc(L, o, step); + lua_Integer n = luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, cast_sizet(n)); checkvalres(res); lua_pushboolean(L, res); return 1; } - case LUA_GCSETPAUSE: - case LUA_GCSETSTEPMUL: { - int p = (int)luaL_optinteger(L, 2, 0); - int previous = lua_gc(L, o, p); - checkvalres(previous); - lua_pushinteger(L, previous); - return 1; - } case LUA_GCISRUNNING: { int res = lua_gc(L, o); checkvalres(res); @@ -234,15 +228,22 @@ static int luaB_collectgarbage (lua_State *L) { return 1; } case LUA_GCGEN: { - int minormul = (int)luaL_optinteger(L, 2, 0); - int majormul = (int)luaL_optinteger(L, 3, 0); - return pushmode(L, lua_gc(L, o, minormul, majormul)); + return pushmode(L, lua_gc(L, o)); } case LUA_GCINC: { - int pause = (int)luaL_optinteger(L, 2, 0); - int stepmul = (int)luaL_optinteger(L, 3, 0); - int stepsize = (int)luaL_optinteger(L, 4, 0); - return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); + return pushmode(L, lua_gc(L, o)); + } + case LUA_GCPARAM: { + static const char *const params[] = { + "minormul", "majorminor", "minormajor", + "pause", "stepmul", "stepsize", NULL}; + static const char pnum[] = { + LUA_GCPMINORMUL, LUA_GCPMAJORMINOR, LUA_GCPMINORMAJOR, + LUA_GCPPAUSE, LUA_GCPSTEPMUL, LUA_GCPSTEPSIZE}; + int p = pnum[luaL_checkoption(L, 2, NULL, params)]; + lua_Integer value = luaL_optinteger(L, 3, -1); + lua_pushinteger(L, lua_gc(L, o, p, (int)value)); + return 1; } default: { int res = lua_gc(L, o); @@ -278,21 +279,22 @@ static int luaB_next (lua_State *L) { static int pairscont (lua_State *L, int status, lua_KContext k) { (void)L; (void)status; (void)k; /* unused */ - return 3; + return 4; /* __pairs did all the work, just return its results */ } static int luaB_pairs (lua_State *L) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ - lua_pushcfunction(L, luaB_next); /* will return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ + lua_pushcfunction(L, luaB_next); /* will return generator and */ + lua_pushvalue(L, 1); /* state */ + lua_pushnil(L); /* initial value */ + lua_pushnil(L); /* to-be-closed object */ } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ - lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ + lua_callk(L, 1, 4, 0, pairscont); /* get 4 values from metamethod */ } - return 3; + return 4; } @@ -337,9 +339,17 @@ static int load_aux (lua_State *L, int status, int envidx) { } +static const char *getMode (lua_State *L, int idx) { + const char *mode = luaL_optstring(L, idx, "bt"); + if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */ + luaL_argerror(L, idx, "invalid mode"); + return mode; +} + + static int luaB_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); - const char *mode = luaL_optstring(L, 2, NULL); + const char *mode = getMode(L, 2); int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ int status = luaL_loadfilex(L, fname, mode); return load_aux(L, status, env); @@ -388,7 +398,7 @@ static int luaB_load (lua_State *L) { int status; size_t l; const char *s = lua_tolstring(L, 1, &l); - const char *mode = luaL_optstring(L, 3, "bt"); + const char *mode = getMode(L, 3); int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ if (s != NULL) { /* loading a string? */ const char *chunkname = luaL_optstring(L, 2, s); diff --git a/lua/src/lcode.c b/lua/src/lcode.c index 8761614..4caa804 100644 --- a/lua/src/lcode.c +++ b/lua/src/lcode.c @@ -31,10 +31,7 @@ #include "lvm.h" -/* Maximum number of registers in a Lua function (must fit in 8 bits) */ -#define MAXREGS 255 - - +/* (note that expressions VJMP also have jumps.) */ #define hasjumps(e) ((e)->t != (e)->f) @@ -43,8 +40,12 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k); /* semantic error */ -l_noret luaK_semerror (LexState *ls, const char *msg) { +l_noret luaK_semerror (LexState *ls, const char *fmt, ...) { + const char *msg; + va_list argp; + pushvfstring(ls->L, argp, fmt, msg); ls->t.token = 0; /* remove "near " from final message */ + ls->linenumber = ls->lastline; /* back to line of last used token */ luaX_syntaxerror(ls, msg); } @@ -211,6 +212,7 @@ void luaK_ret (FuncState *fs, int first, int nret) { case 1: op = OP_RETURN1; break; default: op = OP_RETURN; break; } + luaY_checklimit(fs, nret + 1, MAXARG_B, "returns"); luaK_codeABC(fs, op, first, nret + 1, 0); } @@ -331,15 +333,15 @@ static void savelineinfo (FuncState *fs, Proto *f, int line) { int pc = fs->pc - 1; /* last instruction coded */ if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) { luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, - f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines"); + f->sizeabslineinfo, AbsLineInfo, INT_MAX, "lines"); f->abslineinfo[fs->nabslineinfo].pc = pc; f->abslineinfo[fs->nabslineinfo++].line = line; linedif = ABSLINEINFO; /* signal that there is absolute information */ fs->iwthabs = 1; /* restart counter */ } luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, - MAX_INT, "opcodes"); - f->lineinfo[pc] = linedif; + INT_MAX, "opcodes"); + f->lineinfo[pc] = cast(ls_byte, linedif); fs->previousline = line; /* last line saved */ } @@ -383,7 +385,7 @@ int luaK_code (FuncState *fs, Instruction i) { Proto *f = fs->f; /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, - MAX_INT, "opcodes"); + INT_MAX, "opcodes"); f->code[fs->pc++] = i; savelineinfo(fs, f, fs->ls->lastline); return fs->pc - 1; /* index of new instruction */ @@ -394,32 +396,40 @@ int luaK_code (FuncState *fs, Instruction i) { ** Format and emit an 'iABC' instruction. (Assertions check consistency ** of parameters versus opcode.) */ -int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) { +int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) { lua_assert(getOpMode(o) == iABC); - lua_assert(a <= MAXARG_A && b <= MAXARG_B && - c <= MAXARG_C && (k & ~1) == 0); - return luaK_code(fs, CREATE_ABCk(o, a, b, c, k)); + lua_assert(A <= MAXARG_A && B <= MAXARG_B && + C <= MAXARG_C && (k & ~1) == 0); + return luaK_code(fs, CREATE_ABCk(o, A, B, C, k)); +} + + +int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) { + lua_assert(getOpMode(o) == ivABC); + lua_assert(A <= MAXARG_A && B <= MAXARG_vB && + C <= MAXARG_vC && (k & ~1) == 0); + return luaK_code(fs, CREATE_vABCk(o, A, B, C, k)); } /* ** Format and emit an 'iABx' instruction. */ -int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { +int luaK_codeABx (FuncState *fs, OpCode o, int A, int Bc) { lua_assert(getOpMode(o) == iABx); - lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); - return luaK_code(fs, CREATE_ABx(o, a, bc)); + lua_assert(A <= MAXARG_A && Bc <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, A, Bc)); } /* ** Format and emit an 'iAsBx' instruction. */ -static int codeAsBx (FuncState *fs, OpCode o, int a, int bc) { - unsigned int b = bc + OFFSET_sBx; +static int codeAsBx (FuncState *fs, OpCode o, int A, int Bc) { + int b = Bc + OFFSET_sBx; lua_assert(getOpMode(o) == iAsBx); - lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); - return luaK_code(fs, CREATE_ABx(o, a, b)); + lua_assert(A <= MAXARG_A && b <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, A, b)); } @@ -427,7 +437,7 @@ static int codeAsBx (FuncState *fs, OpCode o, int a, int bc) { ** Format and emit an 'isJ' instruction. */ static int codesJ (FuncState *fs, OpCode o, int sj, int k) { - unsigned int j = sj + OFFSET_sJ; + int j = sj + OFFSET_sJ; lua_assert(getOpMode(o) == isJ); lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); return luaK_code(fs, CREATE_sJ(o, j, k)); @@ -437,9 +447,9 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k) { /* ** Emit an "extra argument" instruction (format 'iAx') */ -static int codeextraarg (FuncState *fs, int a) { - lua_assert(a <= MAXARG_Ax); - return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); +static int codeextraarg (FuncState *fs, int A) { + lua_assert(A <= MAXARG_Ax); + return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, A)); } @@ -466,9 +476,7 @@ static int luaK_codek (FuncState *fs, int reg, int k) { void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { - if (newstack >= MAXREGS) - luaX_syntaxerror(fs->ls, - "function or expression needs too many registers"); + luaY_checklimit(fs, newstack, MAX_FSTACK, "registers"); fs->f->maxstacksize = cast_byte(newstack); } } @@ -479,7 +487,7 @@ void luaK_checkstack (FuncState *fs, int n) { */ void luaK_reserveregs (FuncState *fs, int n) { luaK_checkstack(fs, n); - fs->freereg += n; + fs->freereg = cast_byte(fs->freereg + n); } @@ -533,39 +541,45 @@ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { /* ** Add constant 'v' to prototype's list of constants (field 'k'). +*/ +static int addk (FuncState *fs, Proto *f, TValue *v) { + lua_State *L = fs->ls->L; + int oldsize = f->sizek; + int k = fs->nk; + luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); + while (oldsize < f->sizek) + setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[k], v); + fs->nk++; + luaC_barrier(L, f, v); + return k; +} + + +/* ** Use scanner's table to cache position of constants in constant list ** and try to reuse constants. Because some values should not be used ** as keys (nil cannot be a key, integer keys can collapse with float ** keys), the caller must provide a useful 'key' for indexing the cache. -** Note that all functions share the same table, so entering or exiting -** a function can make some indices wrong. */ -static int addk (FuncState *fs, TValue *key, TValue *v) { +static int k2proto (FuncState *fs, TValue *key, TValue *v) { TValue val; - lua_State *L = fs->ls->L; Proto *f = fs->f; - const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */ - int k, oldsize; - if (ttisinteger(idx)) { /* is there an index there? */ - k = cast_int(ivalue(idx)); - /* correct value? (warning: must distinguish floats from integers!) */ - if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && - luaV_rawequalobj(&f->k[k], v)) - return k; /* reuse index */ + int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */ + if (!tagisempty(tag)) { /* is there an index there? */ + int k = cast_int(ivalue(&val)); + /* collisions can happen only for float keys */ + lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v)); + return k; /* reuse index */ + } + else { /* constant not found; create a new entry */ + int k = addk(fs, f, v); + /* cache it for reuse; numerical value does not need GC barrier; + table is not a metatable, so it does not need to invalidate cache */ + setivalue(&val, k); + luaH_set(fs->ls->L, fs->kcache, key, &val); + return k; } - /* constant not found; create a new entry */ - oldsize = f->sizek; - k = fs->nk; - /* numerical value does not need GC barrier; - table has no metatable, so it does not need to invalidate cache */ - setivalue(&val, k); - luaH_finishset(L, fs->ls->h, key, idx, &val); - luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); - while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); - setobj(L, &f->k[k], v); - fs->nk++; - luaC_barrier(L, f, v); - return k; } @@ -575,7 +589,7 @@ static int addk (FuncState *fs, TValue *key, TValue *v) { static int stringK (FuncState *fs, TString *s) { TValue o; setsvalue(fs->ls->L, &o, s); - return addk(fs, &o, &o); /* use string itself as key */ + return k2proto(fs, &o, &o); /* use string itself as key */ } @@ -585,36 +599,42 @@ static int stringK (FuncState *fs, TString *s) { static int luaK_intK (FuncState *fs, lua_Integer n) { TValue o; setivalue(&o, n); - return addk(fs, &o, &o); /* use integer itself as key */ + return k2proto(fs, &o, &o); /* use integer itself as key */ } /* ** Add a float to list of constants and return its index. Floats ** with integral values need a different key, to avoid collision -** with actual integers. To that, we add to the number its smaller +** with actual integers. To that end, we add to the number its smaller ** power-of-two fraction that is still significant in its scale. -** For doubles, that would be 1/2^52. -** (This method is not bulletproof: there may be another float -** with that value, and for floats larger than 2^53 the result is -** still an integer. At worst, this only wastes an entry with -** a duplicate.) +** (For doubles, the fraction would be 2^-52). +** This method is not bulletproof: different numbers may generate the +** same key (e.g., very large numbers will overflow to 'inf') and for +** floats larger than 2^53 the result is still an integer. For those +** cases, just generate a new entry. At worst, this only wastes an entry +** with a duplicate. */ static int luaK_numberK (FuncState *fs, lua_Number r) { - TValue o; - lua_Integer ik; - setfltvalue(&o, r); - if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */ - return addk(fs, &o, &o); /* use number itself as key */ - else { /* must build an alternative key */ + TValue o, kv; + setfltvalue(&o, r); /* value as a TValue */ + if (r == 0) { /* handle zero as a special case */ + setpvalue(&kv, fs); /* use FuncState as index */ + return k2proto(fs, &kv, &o); /* cannot collide */ + } + else { const int nbm = l_floatatt(MANT_DIG); const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); - const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */ - TValue kv; - setfltvalue(&kv, k); - /* result is not an integral value, unless value is too large */ - lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) || - l_mathop(fabs)(r) >= l_mathop(1e6)); - return addk(fs, &kv, &o); + const lua_Number k = r * (1 + q); /* key */ + lua_Integer ik; + setfltvalue(&kv, k); /* key as a TValue */ + if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integer value? */ + int n = k2proto(fs, &kv, &o); /* use key */ + if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */ + return n; + } + /* else, either key is still an integer or there was a collision; + anyway, do not try to reuse constant; instead, create a new one */ + return addk(fs, fs->f, &o); } } @@ -625,7 +645,7 @@ static int luaK_numberK (FuncState *fs, lua_Number r) { static int boolF (FuncState *fs) { TValue o; setbfvalue(&o); - return addk(fs, &o, &o); /* use boolean itself as key */ + return k2proto(fs, &o, &o); /* use boolean itself as key */ } @@ -635,7 +655,7 @@ static int boolF (FuncState *fs) { static int boolT (FuncState *fs) { TValue o; setbtvalue(&o); - return addk(fs, &o, &o); /* use boolean itself as key */ + return k2proto(fs, &o, &o); /* use boolean itself as key */ } @@ -645,9 +665,9 @@ static int boolT (FuncState *fs) { static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); - /* cannot use nil as key; instead use table itself to represent nil */ - sethvalue(fs->ls->L, &k, fs->ls->h); - return addk(fs, &k, &v); + /* cannot use nil as key; instead use table itself */ + sethvalue(fs->ls->L, &k, fs->kcache); + return k2proto(fs, &k, &v); } @@ -686,6 +706,22 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) { } +/* +** Get the value of 'var' in a register and generate an opcode to check +** whether that register is nil. 'k' is the index of the variable name +** in the list of constants. If its value cannot be encoded in Bx, a 0 +** will use '?' for the name. +*/ +void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) { + luaK_exp2anyreg(fs, var); + luaK_fixline(fs, line); + k = (k >= MAXARG_Bx) ? 0 : k + 1; + luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k); + luaK_fixline(fs, line); + freeexp(fs, var); +} + + /* ** Convert a constant in 'v' into an expression description 'e' */ @@ -720,6 +756,7 @@ static void const2exp (TValue *v, expdesc *e) { */ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { Instruction *pc = &getinstruction(fs, e); + luaY_checklimit(fs, nresults + 1, MAXARG_C, "multiple results"); if (e->k == VCALL) /* expression is an open function call? */ SETARG_C(*pc, nresults + 1); else { @@ -734,10 +771,11 @@ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { /* ** Convert a VKSTR to a VK */ -static void str2K (FuncState *fs, expdesc *e) { +static int str2K (FuncState *fs, expdesc *e) { lua_assert(e->k == VKSTR); e->u.info = stringK(fs, e->u.strval); e->k = VK; + return e->u.info; } @@ -764,6 +802,15 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { } } +/* +** Change a vararg parameter into a regular local variable +*/ +void luaK_vapar2local (FuncState *fs, expdesc *var) { + needvatab(fs->f); /* function will need a vararg table */ + /* now a vararg parameter is equivalent to a regular local variable */ + var->k = VLOCAL; +} + /* ** Ensure that expression 'e' is not a variable (nor a ). @@ -775,6 +822,9 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { const2exp(const2val(fs, e), e); break; } + case VVARGVAR: { + luaK_vapar2local(fs, e); /* turn it into a local variable */ + } /* FALLTHROUGH */ case VLOCAL: { /* already in a register */ int temp = e->u.var.ridx; e->u.info = temp; /* (can't do a direct assignment; values overlap) */ @@ -809,6 +859,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) { e->k = VRELOC; break; } + case VVARGIND: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; @@ -971,11 +1027,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { /* -** Ensures final expression result is either in a register -** or in an upvalue. +** Ensures final expression result is either in a register, +** in an upvalue, or it is the vararg parameter. */ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { - if (e->k != VUPVAL || hasjumps(e)) + if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e)) luaK_exp2anyreg(fs, e); } @@ -985,7 +1041,7 @@ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { ** or it is a constant. */ void luaK_exp2val (FuncState *fs, expdesc *e) { - if (hasjumps(e)) + if (e->k == VJMP || hasjumps(e)) luaK_exp2anyreg(fs, e); else luaK_dischargevars(fs, e); @@ -1036,10 +1092,10 @@ static int exp2RK (FuncState *fs, expdesc *e) { } -static void codeABRK (FuncState *fs, OpCode o, int a, int b, +static void codeABRK (FuncState *fs, OpCode o, int A, int B, expdesc *ec) { int k = exp2RK(fs, ec); - luaK_codeABCk(fs, o, a, b, ec->u.info, k); + luaK_codeABCk(fs, o, A, B, ec->u.info, k); } @@ -1070,6 +1126,10 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); break; } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + /* now, assignment is to a regular table */ + } /* FALLTHROUGH */ case VINDEXED: { codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); break; @@ -1080,22 +1140,6 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { } -/* -** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). -*/ -void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { - int ereg; - luaK_exp2anyreg(fs, e); - ereg = e->u.info; /* register where 'e' was placed */ - freeexp(fs, e); - e->u.info = fs->freereg; /* base register for op_self */ - e->k = VNONRELOC; /* self expression has a fixed register */ - luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ - codeABRK(fs, OP_SELF, e->u.info, ereg, key); - freeexp(fs, key); -} - - /* ** Negate condition 'e' (where 'e' is a comparison). */ @@ -1158,7 +1202,7 @@ void luaK_goiftrue (FuncState *fs, expdesc *e) { /* ** Emit code to go through if 'e' is false, jump otherwise. */ -void luaK_goiffalse (FuncState *fs, expdesc *e) { +static void luaK_goiffalse (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { @@ -1219,7 +1263,7 @@ static void codenot (FuncState *fs, expdesc *e) { ** Check whether expression 'e' is a short literal string */ static int isKstr (FuncState *fs, expdesc *e) { - return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK && ttisshrstring(&fs->f->k[e->u.info])); } @@ -1270,6 +1314,40 @@ static int isSCnumber (expdesc *e, int *pi, int *isfloat) { } +/* +** Emit SELF instruction or equivalent: the code will convert +** expression 'e' into 'e.key(e,'. +*/ +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int ereg, base; + luaK_exp2anyreg(fs, e); + ereg = e->u.info; /* register where 'e' (the receiver) was placed */ + freeexp(fs, e); + base = e->u.info = fs->freereg; /* base register for op_self */ + e->k = VNONRELOC; /* self expression has a fixed register */ + luaK_reserveregs(fs, 2); /* method and 'self' produced by op_self */ + lua_assert(key->k == VKSTR); + /* is method name a short string in a valid K index? */ + if (strisshr(key->u.strval) && luaK_exp2K(fs, key)) { + /* can use 'self' opcode */ + luaK_codeABCk(fs, OP_SELF, base, ereg, key->u.info, 0); + } + else { /* cannot use 'self' opcode; use move+gettable */ + luaK_exp2anyreg(fs, key); /* put method name in a register */ + luaK_codeABC(fs, OP_MOVE, base + 1, ereg, 0); /* copy self to base+1 */ + luaK_codeABC(fs, OP_GETTABLE, base, ereg, key->u.info); /* get method */ + } + freeexp(fs, key); +} + + +/* auxiliary function to define indexing expressions */ +static void fillidxk (expdesc *t, int idx, expkind k) { + t->u.ind.idx = cast_byte(idx); + t->k = k; +} + + /* ** Create expression 't[k]'. 't' must have its final result already in a ** register or upvalue. Upvalues can only be indexed by literal strings. @@ -1277,35 +1355,39 @@ static int isSCnumber (expdesc *e, int *pi, int *isfloat) { ** values in registers. */ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + int keystr = -1; if (k->k == VKSTR) - str2K(fs, k); + keystr = str2K(fs, k); lua_assert(!hasjumps(t) && - (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); + (t->k == VLOCAL || t->k == VVARGVAR || + t->k == VNONRELOC || t->k == VUPVAL)); if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ luaK_exp2anyreg(fs, t); /* put it in a register */ if (t->k == VUPVAL) { - int temp = t->u.info; /* upvalue index */ - lua_assert(isKstr(fs, k)); + lu_byte temp = cast_byte(t->u.info); /* upvalue index */ t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ - t->u.ind.idx = k->u.info; /* literal short string */ - t->k = VINDEXUP; + lua_assert(isKstr(fs, k)); + fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ + } + else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */ + int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */ + lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */ + lua_assert(vreg == fs->f->numparams); + t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap) */ + fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */ } else { /* register index of the table */ - t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info; - if (isKstr(fs, k)) { - t->u.ind.idx = k->u.info; /* literal short string */ - t->k = VINDEXSTR; - } - else if (isCint(k)) { - t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ - t->k = VINDEXI; - } - else { - t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ - t->k = VINDEXED; - } + t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); + if (isKstr(fs, k)) + fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ + else if (isCint(k)) /* int. constant in proper range? */ + fillidxk(t, cast_int(k->u.ival), VINDEXI); + else + fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */ } + t->u.ind.keystr = keystr; /* string index in 'k' */ + t->u.ind.ro = 0; /* by default, not read-only */ } @@ -1412,7 +1494,7 @@ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, e1->u.info = pc; e1->k = VRELOC; /* all those operations are relocatable */ luaK_fixline(fs, line); - luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */ + luaK_codeABCk(fs, mmop, v1, v2, cast_int(event), flip); /* metamethod */ luaK_fixline(fs, line); } @@ -1617,7 +1699,7 @@ void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) { luaK_dischargevars(fs, e); switch (opr) { case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ - if (constfolding(fs, opr + LUA_OPUNM, e, &ef)) + if (constfolding(fs, cast_int(opr + LUA_OPUNM), e, &ef)) break; /* else */ /* FALLTHROUGH */ case OPR_LEN: @@ -1705,7 +1787,7 @@ static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) { void luaK_posfix (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { luaK_dischargevars(fs, e2); - if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2)) + if (foldbinop(opr) && constfolding(fs, cast_int(opr + LUA_OPADD), e1, e2)) return; /* done by folding */ switch (opr) { case OPR_AND: { @@ -1791,11 +1873,11 @@ void luaK_fixline (FuncState *fs, int line) { void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { Instruction *inst = &fs->f->code[pc]; - int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */ - int extra = asize / (MAXARG_C + 1); /* higher bits of array size */ - int rc = asize % (MAXARG_C + 1); /* lower bits of array size */ + int extra = asize / (MAXARG_vC + 1); /* higher bits of array size */ + int rc = asize % (MAXARG_vC + 1); /* lower bits of array size */ int k = (extra > 0); /* true iff needs extra argument */ - *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k); + hsize = (hsize != 0) ? luaO_ceillog2(cast_uint(hsize)) + 1 : 0; + *inst = CREATE_vABCk(OP_NEWTABLE, ra, hsize, rc, k); *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); } @@ -1808,18 +1890,18 @@ void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { ** table (or LUA_MULTRET to add up to stack top). */ void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { - lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); + lua_assert(tostore != 0); if (tostore == LUA_MULTRET) tostore = 0; - if (nelems <= MAXARG_C) - luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems); + if (nelems <= MAXARG_vC) + luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 0); else { - int extra = nelems / (MAXARG_C + 1); - nelems %= (MAXARG_C + 1); - luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1); + int extra = nelems / (MAXARG_vC + 1); + nelems %= (MAXARG_vC + 1); + luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 1); codeextraarg(fs, extra); } - fs->freereg = base + 1; /* free registers with list values */ + fs->freereg = cast_byte(base + 1); /* free registers with list values */ } @@ -1832,8 +1914,8 @@ static int finaltarget (Instruction *code, int i) { Instruction pc = code[i]; if (GET_OPCODE(pc) != OP_JMP) break; - else - i += GETARG_sJ(pc) + 1; + else + i += GETARG_sJ(pc) + 1; } return i; } @@ -1843,15 +1925,20 @@ static int finaltarget (Instruction *code, int i) { ** Do a final pass over the code of a function, doing small peephole ** optimizations and adjustments. */ +#include "lopnames.h" void luaK_finish (FuncState *fs) { int i; Proto *p = fs->f; + if (p->flag & PF_VATAB) /* will it use a vararg table? */ + p->flag &= cast_byte(~PF_VAHID); /* then it will not use hidden args. */ for (i = 0; i < fs->pc; i++) { Instruction *pc = &p->code[i]; - lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); + /* avoid "not used" warnings when assert is off (for 'onelua.c') */ + (void)luaP_isOT; (void)luaP_isIT; + lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); switch (GET_OPCODE(*pc)) { case OP_RETURN0: case OP_RETURN1: { - if (!(fs->needclose || p->is_vararg)) + if (!(fs->needclose || (p->flag & PF_VAHID))) break; /* no extra work */ /* else use OP_RETURN to do the extra work */ SET_OPCODE(*pc, OP_RETURN); @@ -1859,13 +1946,23 @@ void luaK_finish (FuncState *fs) { case OP_RETURN: case OP_TAILCALL: { if (fs->needclose) SETARG_k(*pc, 1); /* signal that it needs to close */ - if (p->is_vararg) - SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ + if (p->flag & PF_VAHID) /* does it use hidden arguments? */ + SETARG_C(*pc, p->numparams + 1); /* signal that */ + break; + } + case OP_GETVARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */ + break; + } + case OP_VARARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SETARG_k(*pc, 1); /* must get vararg there */ break; } - case OP_JMP: { + case OP_JMP: { /* to optimize jumps to jumps */ int target = finaltarget(p->code, i); - fixjump(fs, i, target); + fixjump(fs, i, target); /* jump directly to final target */ break; } default: break; diff --git a/lua/src/lcode.h b/lua/src/lcode.h index 0b971fc..09e5c80 100644 --- a/lua/src/lcode.h +++ b/lua/src/lcode.h @@ -60,15 +60,20 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); -LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); -LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, - int B, int C, int k); +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, int Bx); +LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, + int k); +LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, + int k); LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, + int line); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); +LUAI_FUNC void luaK_vapar2local (FuncState *fs, expdesc *var); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); @@ -77,7 +82,6 @@ LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); -LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); @@ -95,7 +99,7 @@ LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize); LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); LUAI_FUNC void luaK_finish (FuncState *fs); -LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg); +LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *fmt, ...); #endif diff --git a/lua/src/lcorolib.c b/lua/src/lcorolib.c index c64adf0..eb30bf4 100644 --- a/lua/src/lcorolib.c +++ b/lua/src/lcorolib.c @@ -16,6 +16,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" static lua_State *getco (lua_State *L) { @@ -153,8 +154,13 @@ static int luaB_costatus (lua_State *L) { } +static lua_State *getoptco (lua_State *L) { + return (lua_isnone(L, 1) ? L : getco(L)); +} + + static int luaB_yieldable (lua_State *L) { - lua_State *co = lua_isnone(L, 1) ? L : getco(L); + lua_State *co = getoptco(L); lua_pushboolean(L, lua_isyieldable(co)); return 1; } @@ -168,7 +174,7 @@ static int luaB_corunning (lua_State *L) { static int luaB_close (lua_State *L) { - lua_State *co = getco(L); + lua_State *co = getoptco(L); int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { @@ -183,8 +189,17 @@ static int luaB_close (lua_State *L) { return 2; } } - default: /* normal or running coroutine */ + case COS_NORM: return luaL_error(L, "cannot close a %s coroutine", statname[status]); + case COS_RUN: + lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */ + if (lua_tothread(L, -1) == co) + return luaL_error(L, "cannot close main thread"); + lua_closethread(co, L); /* close itself */ + /* previous call does not return *//* FALLTHROUGH */ + default: + lua_assert(0); + return 0; } } diff --git a/lua/src/lctype.c b/lua/src/lctype.c index 9542280..b1a43e4 100644 --- a/lua/src/lctype.c +++ b/lua/src/lctype.c @@ -18,7 +18,7 @@ #if defined (LUA_UCID) /* accept UniCode IDentifiers? */ -/* consider all non-ascii codepoints to be alphabetic */ +/* consider all non-ASCII codepoints to be alphabetic */ #define NONA 0x01 #else #define NONA 0x00 /* default */ diff --git a/lua/src/ldblib.c b/lua/src/ldblib.c index 6dcbaa9..c7b7481 100644 --- a/lua/src/ldblib.c +++ b/lua/src/ldblib.c @@ -18,6 +18,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" /* @@ -190,8 +191,10 @@ static int db_getinfo (lua_State *L) { settabsi(L, "ftransfer", ar.ftransfer); settabsi(L, "ntransfer", ar.ntransfer); } - if (strchr(options, 't')) + if (strchr(options, 't')) { settabsb(L, "istailcall", ar.istailcall); + settabsi(L, "extraargs", ar.extraargs); + } if (strchr(options, 'L')) treatstackoption(L, L1, "activelines"); if (strchr(options, 'f')) @@ -446,14 +449,6 @@ static int db_traceback (lua_State *L) { } -static int db_setcstacklimit (lua_State *L) { - int limit = (int)luaL_checkinteger(L, 1); - int res = lua_setcstacklimit(L, limit); - lua_pushinteger(L, res); - return 1; -} - - static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getuservalue", db_getuservalue}, @@ -471,7 +466,6 @@ static const luaL_Reg dblib[] = { {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_traceback}, - {"setcstacklimit", db_setcstacklimit}, {NULL, NULL} }; diff --git a/lua/src/ldebug.c b/lua/src/ldebug.c index 591b352..8df5f5f 100644 --- a/lua/src/ldebug.c +++ b/lua/src/ldebug.c @@ -33,6 +33,8 @@ #define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL) +static const char strlocal[] = "local"; +static const char strupval[] = "upvalue"; static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name); @@ -63,7 +65,7 @@ static int getbaseline (const Proto *f, int pc, int *basepc) { return f->linedefined; } else { - int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ + int i = pc / MAXIWTHABS - 1; /* get an estimate */ /* estimate must be a lower bound of the correct base */ lua_assert(i < 0 || (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); @@ -182,7 +184,7 @@ static const char *upvalname (const Proto *p, int uv) { static const char *findvararg (CallInfo *ci, int n, StkId *pos) { - if (clLvalue(s2v(ci->func.p))->p->is_vararg) { + if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ *pos = ci->func.p - nextra - (n + 1); @@ -245,6 +247,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { + api_checkpop(L, 1); setobjs2s(L, pos, L->top.p - 1); L->top.p--; /* pop value */ } @@ -264,8 +267,7 @@ static void funcinfo (lua_Debug *ar, Closure *cl) { else { const Proto *p = cl->l.p; if (p->source) { - ar->source = getstr(p->source); - ar->srclen = tsslen(p->source); + ar->source = getlstr(p->source, ar->srclen); } else { ar->source = "=?"; @@ -302,7 +304,7 @@ static void collectvalidlines (lua_State *L, Closure *f) { int i; TValue v; setbtvalue(&v); /* boolean 'true' to be the value of all indices */ - if (!p->is_vararg) /* regular function? */ + if (!(isvararg(p))) /* regular function? */ i = 0; /* consider all instructions */ else { /* vararg function */ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); @@ -346,13 +348,21 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, ar->nparams = 0; } else { - ar->isvararg = f->l.p->is_vararg; + ar->isvararg = (isvararg(f->l.p)) ? 1 : 0; ar->nparams = f->l.p->numparams; } break; } case 't': { - ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; + if (ci != NULL) { + ar->istailcall = !!(ci->callstatus & CIST_TAIL); + ar->extraargs = + cast_uchar((ci->callstatus & MAX_CCMT) >> CIST_CCMT); + } + else { + ar->istailcall = 0; + ar->extraargs = 0; + } break; } case 'n': { @@ -364,11 +374,11 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, break; } case 'r': { - if (ci == NULL || !(ci->callstatus & CIST_TRAN)) + if (ci == NULL || !(ci->callstatus & CIST_HOOKED)) ar->ftransfer = ar->ntransfer = 0; else { - ar->ftransfer = ci->u2.transferinfo.ftransfer; - ar->ntransfer = ci->u2.transferinfo.ntransfer; + ar->ftransfer = L->transferinfo.ftransfer; + ar->ntransfer = L->transferinfo.ntransfer; } break; } @@ -497,7 +507,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, int pc = *ppc; *name = luaF_getlocalname(p, reg + 1, pc); if (*name) /* is a local? */ - return "local"; + return strlocal; /* else try symbolic execution */ *ppc = pc = findsetreg(p, pc, reg); if (pc != -1) { /* could find instruction? */ @@ -512,7 +522,7 @@ static const char *basicgetobjname (const Proto *p, int *ppc, int reg, } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); - return "upvalue"; + return strupval; } case OP_LOADK: return kname(p, GETARG_Bx(i), name); case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name); @@ -533,18 +543,6 @@ static void rname (const Proto *p, int pc, int c, const char **name) { } -/* -** Find a "name" for a 'C' value in an RK instruction. -*/ -static void rkname (const Proto *p, int pc, Instruction i, const char **name) { - int c = GETARG_C(i); /* key index */ - if (GETARG_k(i)) /* is 'c' a constant? */ - kname(p, c, name); - else /* 'c' is a register */ - rname(p, pc, c, name); -} - - /* ** Check whether table being indexed by instruction 'i' is the ** environment '_ENV' @@ -554,8 +552,13 @@ static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) { const char *name; /* name of indexed variable */ if (isup) /* is 't' an upvalue? */ name = upvalname(p, t); - else /* 't' is a register */ - basicgetobjname(p, &pc, t, &name); + else { /* 't' is a register */ + const char *what = basicgetobjname(p, &pc, t, &name); + /* 'name' must be the name of a local variable (at the current + level or an upvalue) */ + if (what != strlocal && what != strupval) + name = NULL; /* cannot be the variable _ENV */ + } return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; } @@ -592,7 +595,8 @@ static const char *getobjname (const Proto *p, int lastpc, int reg, return isEnv(p, lastpc, i, 0); } case OP_SELF: { - rkname(p, lastpc, i, name); + int k = GETARG_C(i); /* key index */ + kname(p, k, name); return "method"; } default: break; /* go through to return NULL */ @@ -701,7 +705,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o, for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v.p == o) { *name = upvalname(c->p, i); - return "upvalue"; + return strupval; } } return NULL; @@ -810,16 +814,26 @@ l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { } +l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) { + const char *globalname = "?"; /* default name if k == 0 */ + if (k > 0) + kname(cl->p, k - 1, &globalname); + luaG_runerror(L, "global '%s' already defined", globalname); +} + + /* add src:line information to 'msg' */ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line) { - char buff[LUA_IDSIZE]; - if (src) - luaO_chunkid(buff, getstr(src), tsslen(src)); - else { /* no source available; use "?" instead */ - buff[0] = '?'; buff[1] = '\0'; + if (src == NULL) /* no debug information? */ + return luaO_pushfstring(L, "?:?: %s", msg); + else { + char buff[LUA_IDSIZE]; + size_t idlen; + const char *id = getlstr(src, idlen); + luaO_chunkid(buff, id, idlen); + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } - return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } @@ -832,6 +846,10 @@ l_noret luaG_errormsg (lua_State *L) { L->top.p++; /* assume EXTRA_STACK */ luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ } + if (ttisnil(s2v(L->top.p - 1))) { /* error object is nil? */ + /* change it to a proper message */ + setsvalue2s(L, L->top.p - 1, luaS_newliteral(L, "")); + } luaD_throw(L, LUA_ERRRUN); } @@ -841,10 +859,9 @@ l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ - va_start(argp, fmt); - msg = luaO_pushvfstring(L, fmt, argp); /* format message */ - va_end(argp); - if (isLua(ci)) { /* if Lua function, add source:line information */ + pushvfstring(L, argp, fmt, msg); + if (isLua(ci)) { /* Lua function? */ + /* add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ L->top.p--; @@ -895,9 +912,9 @@ int luaG_tracecall (lua_State *L) { Proto *p = ci_func(ci)->p; ci->u.l.trap = 1; /* ensure hooks will be checked */ if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ - if (p->is_vararg) + if (isvararg(p)) return 0; /* hooks will start at VARARGPREP instruction */ - else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */ + else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ luaD_hookcall(L, ci); /* check 'call' hook */ } return 1; /* keep 'trap' on */ @@ -918,7 +935,7 @@ int luaG_tracecall (lua_State *L) { */ int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; - lu_byte mask = L->hookmask; + lu_byte mask = cast_byte(L->hookmask); const Proto *p = ci_func(ci)->p; int counthook; if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ @@ -936,7 +953,7 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) { ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ return 1; /* do not call hook again (VM yielded, so it did not move) */ } - if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ + if (!luaP_isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ L->top.p = ci->top.p; /* correct top */ if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ diff --git a/lua/src/ldebug.h b/lua/src/ldebug.h index 2bfce3c..20d0781 100644 --- a/lua/src/ldebug.h +++ b/lua/src/ldebug.h @@ -53,6 +53,7 @@ LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); +LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k); LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line); diff --git a/lua/src/ldo.c b/lua/src/ldo.c index ea05295..6d0184e 100644 --- a/lua/src/ldo.c +++ b/lua/src/ldo.c @@ -38,16 +38,37 @@ #define errorstatus(s) ((s) > LUA_YIELD) +/* +** these macros allow user-specific actions when a thread is +** resumed/yielded. +*/ +#if !defined(luai_userstateresume) +#define luai_userstateresume(L,n) ((void)L) +#endif + +#if !defined(luai_userstateyield) +#define luai_userstateyield(L,n) ((void)L) +#endif + + /* ** {====================================================== ** Error-recovery functions ** ======================================================= */ +/* chained list of long jump buffers */ +typedef struct lua_longjmp { + struct lua_longjmp *previous; + jmp_buf b; + volatile TStatus status; /* error code */ +} lua_longjmp; + + /* ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By ** default, Lua handles errors with exceptions when compiling as -** C++ code, with _longjmp/_setjmp when asked to use them, and with +** C++ code, with _longjmp/_setjmp when available (POSIX), and with ** longjmp/setjmp otherwise. */ #if !defined(LUAI_THROW) /* { */ @@ -56,73 +77,64 @@ /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) -#define LUAI_TRY(L,c,a) \ - try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } -#define luai_jmpbuf int /* dummy variable */ + +static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) { + try { + f(L, ud); /* call function protected */ + } + catch (lua_longjmp *c1) { /* Lua error */ + if (c1 != c) /* not the correct level? */ + throw; /* rethrow to upper level */ + } + catch (...) { /* non-Lua exception */ + c->status = -1; /* create some error code */ + } +} + #elif defined(LUA_USE_POSIX) /* }{ */ -/* in POSIX, try _longjmp/_setjmp (more efficient) */ +/* in POSIX, use _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf +#define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud)) #else /* }{ */ /* ISO C handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) -#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } -#define luai_jmpbuf jmp_buf +#define LUAI_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud)) #endif /* } */ #endif /* } */ - -/* chain list of long jump buffers */ -struct lua_longjmp { - struct lua_longjmp *previous; - luai_jmpbuf b; - volatile int status; /* error code */ -}; - - -void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { - switch (errcode) { - case LUA_ERRMEM: { /* memory error? */ - setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ - break; - } - case LUA_ERRERR: { - setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); - break; - } - case LUA_OK: { /* special case only for closing upvalues */ - setnilvalue(s2v(oldtop)); /* no error message */ - break; - } - default: { - lua_assert(errorstatus(errcode)); /* real error */ - setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */ - break; - } +void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) { + if (errcode == LUA_ERRMEM) { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + } + else { + lua_assert(errorstatus(errcode)); /* must be a real error */ + lua_assert(!ttisnil(s2v(L->top.p - 1))); /* with a non-nil object */ + setobjs2s(L, oldtop, L->top.p - 1); /* move it to 'oldtop' */ } - L->top.p = oldtop + 1; + L->top.p = oldtop + 1; /* top goes back to old top plus error object */ } -l_noret luaD_throw (lua_State *L, int errcode) { +l_noret luaD_throw (lua_State *L, TStatus errcode) { if (L->errorJmp) { /* thread has an error handler? */ L->errorJmp->status = errcode; /* set status */ LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ global_State *g = G(L); + lua_State *mainth = mainthread(g); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ - if (g->mainthread->errorJmp) { /* main thread has a handler? */ - setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */ - luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ + L->status = errcode; + if (mainth->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, mainth->top.p++, L->top.p - 1); /* copy error obj. */ + luaD_throw(mainth, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ if (g->panic) { /* panic function? */ @@ -135,15 +147,23 @@ l_noret luaD_throw (lua_State *L, int errcode) { } -int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { +l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) { + if (L->errorJmp) { + /* unroll error entries up to the first level */ + while (L->errorJmp->previous != NULL) + L->errorJmp = L->errorJmp->previous; + } + luaD_throw(L, errcode); +} + + +TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { l_uint32 oldnCcalls = L->nCcalls; - struct lua_longjmp lj; + lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; - LUAI_TRY(L, &lj, - (*f)(L, ud); - ); + LUAI_TRY(L, &lj, f, ud); /* call 'f' catching errors */ L->errorJmp = lj.previous; /* restore old error handler */ L->nCcalls = oldnCcalls; return lj.status; @@ -158,7 +178,74 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { ** =================================================================== */ +/* some stack space for error handling */ +#define STACKERRSPACE 200 + + +/* +** LUAI_MAXSTACK limits the size of the Lua stack. +** It must fit into INT_MAX/2. +*/ + +#if !defined(LUAI_MAXSTACK) +#if 1000000 < (INT_MAX / 2) +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK (INT_MAX / 2u) +#endif +#endif + + +/* maximum stack size that respects size_t */ +#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) + +/* +** Minimum between LUAI_MAXSTACK and MAXSTACK_BYSIZET +** (Maximum size for the stack must respect size_t.) +*/ +#define MAXSTACK cast_int(LUAI_MAXSTACK < MAXSTACK_BYSIZET \ + ? LUAI_MAXSTACK : MAXSTACK_BYSIZET) + + +/* stack size with extra space for error handling */ +#define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE) + + +/* raise a stack error while running the message handler */ +l_noret luaD_errerr (lua_State *L) { + TString *msg = luaS_newliteral(L, "error in error handling"); + setsvalue2s(L, L->top.p, msg); + L->top.p++; /* assume EXTRA_STACK */ + luaD_throw(L, LUA_ERRERR); +} + +/* +** Check whether stack has enough space to run a simple function (such +** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack and +** 2 slots in the C stack. +*/ +int luaD_checkminstack (lua_State *L) { + return ((stacksize(L) < MAXSTACK - BASIC_STACK_SIZE) && + (getCcalls(L) < LUAI_MAXCCALLS - 2)); +} + + +/* +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before a stack reallocation, all pointers +** should be changed to offsets, and after the reallocation they should +** be changed back to pointers. As during the reallocation the pointers +** are invalid, the reallocation cannot run emergency collections. +** Alternatively, we can use the old address after the deallocation. +** That is not strict ISO C, but seems to work fine everywhere. +** The following macro chooses how strict is the code. +*/ +#if !defined(LUAI_STRICT_ADDRESS) +#define LUAI_STRICT_ADDRESS 1 +#endif + +#if LUAI_STRICT_ADDRESS /* ** Change all pointers to the stack into offsets. */ @@ -179,9 +266,10 @@ static void relstack (lua_State *L) { /* ** Change back all offsets into pointers. */ -static void correctstack (lua_State *L) { +static void correctstack (lua_State *L, StkId oldstack) { CallInfo *ci; UpVal *up; + UNUSED(oldstack); L->top.p = restorestack(L, L->top.offset); L->tbclist.p = restorestack(L, L->tbclist.offset); for (up = L->openupval; up != NULL; up = up->u.open.next) @@ -194,18 +282,40 @@ static void correctstack (lua_State *L) { } } +#else +/* +** Assume that it is fine to use an address after its deallocation, +** as long as we do not dereference it. +*/ + +static void relstack (lua_State *L) { UNUSED(L); } /* do nothing */ + + +/* +** Correct pointers into 'oldstack' to point into 'L->stack'. +*/ +static void correctstack (lua_State *L, StkId oldstack) { + CallInfo *ci; + UpVal *up; + StkId newstack = L->stack.p; + if (oldstack == newstack) + return; + L->top.p = L->top.p - oldstack + newstack; + L->tbclist.p = L->tbclist.p - oldstack + newstack; + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(uplevel(up) - oldstack + newstack); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = ci->top.p - oldstack + newstack; + ci->func.p = ci->func.p - oldstack + newstack; + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ + } +} +#endif -/* some space for error handling */ -#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) /* ** Reallocate the stack to a new size, correcting all pointers into it. -** In ISO C, any pointer use after the pointer has been deallocated is -** undefined behavior. So, before the reallocation, all pointers are -** changed to offsets, and after the reallocation they are changed back -** to pointers. As during the reallocation the pointers are invalid, the -** reallocation cannot run emergency collections. -** ** In case of allocation error, raise an error or return false according ** to 'raiseerror'. */ @@ -213,21 +323,22 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int oldsize = stacksize(L); int i; StkId newstack; - int oldgcstop = G(L)->gcstopem; - lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); + StkId oldstack = L->stack.p; + lu_byte oldgcstop = G(L)->gcstopem; + lua_assert(newsize <= MAXSTACK || newsize == ERRORSTACKSIZE); relstack(L); /* change pointers to offsets */ G(L)->gcstopem = 1; /* stop emergency collection */ - newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK, + newstack = luaM_reallocvector(L, oldstack, oldsize + EXTRA_STACK, newsize + EXTRA_STACK, StackValue); G(L)->gcstopem = oldgcstop; /* restore emergency collection */ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ - correctstack(L); /* change offsets back to pointers */ + correctstack(L, oldstack); /* change offsets back to pointers */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } L->stack.p = newstack; - correctstack(L); /* change offsets back to pointers */ + correctstack(L, oldstack); /* change offsets back to pointers */ L->stack_last.p = L->stack.p + newsize; for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) setnilvalue(s2v(newstack + i)); /* erase new segment */ @@ -241,23 +352,23 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { */ int luaD_growstack (lua_State *L, int n, int raiseerror) { int size = stacksize(L); - if (l_unlikely(size > LUAI_MAXSTACK)) { + if (l_unlikely(size > MAXSTACK)) { /* if stack is larger than maximum, thread is already using the extra space reserved for errors, that is, thread is handling a stack error; cannot grow further than that. */ lua_assert(stacksize(L) == ERRORSTACKSIZE); if (raiseerror) - luaD_throw(L, LUA_ERRERR); /* error inside message handler */ + luaD_errerr(L); /* stack error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } - else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */ - int newsize = 2 * size; /* tentative new size */ + else if (n < MAXSTACK) { /* avoids arithmetic overflows */ + int newsize = size + (size >> 1); /* tentative new size (size * 1.5) */ int needed = cast_int(L->top.p - L->stack.p) + n; - if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ - newsize = LUAI_MAXSTACK; + if (newsize > MAXSTACK) /* cannot cross the limit */ + newsize = MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; - if (l_likely(newsize <= LUAI_MAXSTACK)) + if (l_likely(newsize <= MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); } /* else stack overflow */ @@ -293,28 +404,28 @@ static int stackinuse (lua_State *L) { ** to twice the current use. (So, the final stack size is at most 2/3 the ** previous size, and half of its entries are empty.) ** As a particular case, if stack was handling a stack overflow and now -** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than +** it is not, 'max' (limited by MAXSTACK) will be smaller than ** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack ** will be reduced to a "regular" size. */ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); - int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3; + int max = (inuse > MAXSTACK / 3) ? MAXSTACK : inuse * 3; /* if thread is currently not handling a stack overflow and its size is larger than maximum "reasonable" size, shrink it */ - if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) { - int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2; + if (inuse <= MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > MAXSTACK / 2) ? MAXSTACK : inuse * 2; luaD_reallocstack(L, nsize, 0); /* ok if that fails */ } else /* don't change stack */ - condmovestack(L,{},{}); /* (change only for debugging) */ + condmovestack(L,(void)0,(void)0); /* (change only for debugging) */ luaE_shrinkCI(L); /* shrink CI list */ } void luaD_inctop (lua_State *L) { - luaD_checkstack(L, 1); L->top.p++; + luaD_checkstack(L, 1); } /* }================================================================== */ @@ -329,7 +440,6 @@ void luaD_hook (lua_State *L, int event, int line, int ftransfer, int ntransfer) { lua_Hook hook = L->hook; if (hook && L->allowhook) { /* make sure there is a hook */ - int mask = CIST_HOOKED; CallInfo *ci = L->ci; ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ @@ -337,18 +447,15 @@ void luaD_hook (lua_State *L, int event, int line, ar.event = event; ar.currentline = line; ar.i_ci = ci; - if (ntransfer != 0) { - mask |= CIST_TRAN; /* 'ci' has transfer information */ - ci->u2.transferinfo.ftransfer = ftransfer; - ci->u2.transferinfo.ntransfer = ntransfer; - } + L->transferinfo.ftransfer = ftransfer; + L->transferinfo.ntransfer = ntransfer; if (isLua(ci) && L->top.p < ci->top.p) L->top.p = ci->top.p; /* protect entire activation register */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ if (ci->top.p < L->top.p + LUA_MINSTACK) ci->top.p = L->top.p + LUA_MINSTACK; L->allowhook = 0; /* cannot call hooks inside a hook */ - ci->callstatus |= mask; + ci->callstatus |= CIST_HOOKED; lua_unlock(L); (*hook)(L, &ar); lua_lock(L); @@ -356,7 +463,7 @@ void luaD_hook (lua_State *L, int event, int line, L->allowhook = 1; ci->top.p = restorestack(L, ci_top); L->top.p = restorestack(L, top); - ci->callstatus &= ~mask; + ci->callstatus &= ~CIST_HOOKED; } } @@ -391,11 +498,11 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { int ftransfer; if (isLua(ci)) { Proto *p = ci_func(ci)->p; - if (p->is_vararg) + if (p->flag & PF_VAHID) delta = ci->u.l.nextraargs + p->numparams + 1; } ci->func.p += delta; /* if vararg, back to virtual 'func' */ - ftransfer = cast(unsigned short, firstres - ci->func.p); + ftransfer = cast_int(firstres - ci->func.p); luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ ci->func.p -= delta; } @@ -406,51 +513,72 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { /* ** Check whether 'func' has a '__call' metafield. If so, put it in the -** stack, below original 'func', so that 'luaD_precall' can call it. Raise -** an error if there is no '__call' metafield. +** stack, below original 'func', so that 'luaD_precall' can call it. +** Raise an error if there is no '__call' metafield. +** Bits CIST_CCMT in status count how many _call metamethods were +** invoked and how many corresponding extra arguments were pushed. +** (This count will be saved in the 'callstatus' of the call). +** Raise an error if this counter overflows. */ -static StkId tryfuncTM (lua_State *L, StkId func) { +static unsigned tryfuncTM (lua_State *L, StkId func, unsigned status) { const TValue *tm; StkId p; - checkstackGCp(L, 1, func); /* space for metamethod */ - tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ - if (l_unlikely(ttisnil(tm))) - luaG_callerror(L, s2v(func)); /* nothing to call */ + tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); + if (l_unlikely(ttisnil(tm))) /* no metamethod? */ + luaG_callerror(L, s2v(func)); for (p = L->top.p; p > func; p--) /* open space for metamethod */ setobjs2s(L, p, p-1); L->top.p++; /* stack space pre-allocated by the caller */ setobj2s(L, func, tm); /* metamethod is the new function to be called */ - return func; + if ((status & MAX_CCMT) == MAX_CCMT) /* is counter full? */ + luaG_runerror(L, "'__call' chain too long"); + return status + (1u << CIST_CCMT); /* increment counter */ +} + + +/* Generic case for 'moveresult' */ +l_sinline void genmoveresults (lua_State *L, StkId res, int nres, + int wanted) { + StkId firstresult = L->top.p - nres; /* index of first result */ + int i; + if (nres > wanted) /* extra results? */ + nres = wanted; /* don't need them */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstresult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(s2v(res + i)); + L->top.p = res + wanted; /* top points after the last result */ } /* -** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. -** Handle most typical cases (zero results for commands, one result for -** expressions, multiple results for tail calls/single parameters) -** separated. +** Given 'nres' results at 'firstResult', move 'fwanted-1' of them +** to 'res'. Handle most typical cases (zero results for commands, +** one result for expressions, multiple results for tail calls/single +** parameters) separated. The flag CIST_TBC in 'fwanted', if set, +** forces the switch to go to the default case. */ -l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { - StkId firstresult; - int i; - switch (wanted) { /* handle typical cases separately */ - case 0: /* no values needed */ +l_sinline void moveresults (lua_State *L, StkId res, int nres, + l_uint32 fwanted) { + switch (fwanted) { /* handle typical cases separately */ + case 0 + 1: /* no values needed */ L->top.p = res; return; - case 1: /* one value needed */ + case 1 + 1: /* one value needed */ if (nres == 0) /* no results? */ setnilvalue(s2v(res)); /* adjust with nil */ else /* at least one result */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ L->top.p = res + 1; return; - case LUA_MULTRET: - wanted = nres; /* we want all results */ + case LUA_MULTRET + 1: + genmoveresults(L, res, nres, nres); /* we want all results */ break; - default: /* two/more results and/or to-be-closed variables */ - if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ - L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ + default: { /* two/more results and/or to-be-closed variables */ + int wanted = get_nresults(fwanted); + if (fwanted & CIST_TBC) { /* to-be-closed variables? */ L->ci->u2.nres = nres; + L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ res = luaF_close(L, res, CLOSEKTOP, 1); L->ci->callstatus &= ~CIST_CLSRET; if (L->hookmask) { /* if needed, call hook after '__close's */ @@ -458,21 +586,13 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { rethook(L, L->ci, nres); res = restorestack(L, savedres); /* hook can move stack */ } - wanted = decodeNresults(wanted); if (wanted == LUA_MULTRET) wanted = nres; /* we want all results */ } + genmoveresults(L, res, nres, wanted); break; + } } - /* generic case */ - firstresult = L->top.p - nres; /* index of first result */ - if (nres > wanted) /* extra results? */ - nres = wanted; /* don't need them */ - for (i = 0; i < nres; i++) /* move all results to correct place */ - setobjs2s(L, res + i, firstresult + i); - for (; i < wanted; i++) /* complete wanted number of results */ - setnilvalue(s2v(res + i)); - L->top.p = res + wanted; /* top points after the last result */ } @@ -483,14 +603,14 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { ** that. */ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { - int wanted = ci->nresults; - if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) + l_uint32 fwanted = ci->callstatus & (CIST_TBC | CIST_NRESULTS); + if (l_unlikely(L->hookmask) && !(fwanted & CIST_TBC)) rethook(L, ci, nres); /* move results to proper place */ - moveresults(L, ci->func.p, nres, wanted); + moveresults(L, ci->func.p, nres, fwanted); /* function cannot be in any of these cases when returning */ lua_assert(!(ci->callstatus & - (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); + (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_CLSRET))); L->ci = ci->previous; /* back to caller (after closing variables) */ } @@ -499,12 +619,18 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) -l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, - int mask, StkId top) { +/* +** Allocate and initialize CallInfo structure. At this point, the +** only valid fields in the call status are number of results, +** CIST_C (if it's a C function), and number of extra arguments. +** (All these bit-fields fit in 16-bit values.) +*/ +l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, unsigned status, + StkId top) { CallInfo *ci = L->ci = next_ci(L); /* new frame */ ci->func.p = func; - ci->nresults = nret; - ci->callstatus = mask; + lua_assert((status & ~(CIST_NRESULTS | CIST_C | MAX_CCMT)) == 0); + ci->callstatus = status; ci->top.p = top; return ci; } @@ -513,12 +639,12 @@ l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, /* ** precall for C functions */ -l_sinline int precallC (lua_State *L, StkId func, int nresults, +l_sinline int precallC (lua_State *L, StkId func, unsigned status, lua_CFunction f) { int n; /* number of returns */ CallInfo *ci; - checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ - L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, + checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = prepCallInfo(L, func, status | CIST_C, L->top.p + LUA_MINSTACK); lua_assert(ci->top.p <= L->stack_last.p); if (l_unlikely(L->hookmask & LUA_MASKCALL)) { @@ -542,18 +668,19 @@ l_sinline int precallC (lua_State *L, StkId func, int nresults, */ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta) { + unsigned status = LUA_MULTRET + 1; retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ - return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); + return precallC(L, func, status, clCvalue(s2v(func))->f); case LUA_VLCF: /* light C function */ - return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); + return precallC(L, func, status, fvalue(s2v(func))); case LUA_VLCL: { /* Lua function */ Proto *p = clLvalue(s2v(func))->p; int fsize = p->maxstacksize; /* frame size */ int nfixparams = p->numparams; int i; - checkstackGCp(L, fsize - delta, func); + checkstackp(L, fsize - delta, func); ci->func.p -= delta; /* restore 'func' (if vararg) */ for (i = 0; i < narg1; i++) /* move down function and arguments */ setobjs2s(L, ci->func.p + i, func + i); @@ -568,8 +695,8 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, return -1; } default: { /* not a function */ - func = tryfuncTM(L, func); /* try to get '__call' metamethod */ - /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ + checkstackp(L, 1, func); /* space for metamethod */ + status = tryfuncTM(L, func, status); /* try '__call' metamethod */ narg1++; goto retry; /* try again */ } @@ -586,13 +713,15 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, ** original function position. */ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { + unsigned status = cast_uint(nresults + 1); + lua_assert(status <= MAXRESULTS + 1); retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ - precallC(L, func, nresults, clCvalue(s2v(func))->f); + precallC(L, func, status, clCvalue(s2v(func))->f); return NULL; case LUA_VLCF: /* light C function */ - precallC(L, func, nresults, fvalue(s2v(func))); + precallC(L, func, status, fvalue(s2v(func))); return NULL; case LUA_VLCL: { /* Lua function */ CallInfo *ci; @@ -600,8 +729,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ int nfixparams = p->numparams; int fsize = p->maxstacksize; /* frame size */ - checkstackGCp(L, fsize, func); - L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); + checkstackp(L, fsize, func); + L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize); ci->u.l.savedpc = p->code; /* starting point */ for (; narg < nfixparams; narg++) setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ @@ -609,8 +738,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { return ci; } default: { /* not a function */ - func = tryfuncTM(L, func); /* try to get '__call' metamethod */ - /* return luaD_precall(L, func, nresults); */ + checkstackp(L, 1, func); /* space for metamethod */ + status = tryfuncTM(L, func, status); /* try '__call' metamethod */ goto retry; /* try again with metamethod */ } } @@ -633,7 +762,7 @@ l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) { luaE_checkcstack(L); } if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ - ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ + ci->callstatus |= CIST_FRESH; /* mark that it is a "fresh" execute */ luaV_execute(L, ci); /* call it */ } L->nCcalls -= inc; @@ -672,13 +801,13 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { ** particular, field CIST_RECST preserves the error status across these ** multiple runs, changing only if there is a new error. */ -static int finishpcallk (lua_State *L, CallInfo *ci) { - int status = getcistrecst(ci); /* get original status */ +static TStatus finishpcallk (lua_State *L, CallInfo *ci) { + TStatus status = getcistrecst(ci); /* get original status */ if (l_likely(status == LUA_OK)) /* no error? */ status = LUA_YIELD; /* was interrupted by an yield */ else { /* error */ StkId func = restorestack(L, ci->u2.funcidx); - L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ + L->allowhook = getoah(ci); /* restore 'allowhook' */ func = luaF_close(L, func, status, 1); /* can yield or raise an error */ luaD_seterrorobj(L, status, func); luaD_shrinkstack(L); /* restore stack size in case of overflow */ @@ -707,20 +836,21 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { */ static void finishCcall (lua_State *L, CallInfo *ci) { int n; /* actual number of results from C function */ - if (ci->callstatus & CIST_CLSRET) { /* was returning? */ - lua_assert(hastocloseCfunc(ci->nresults)); + if (ci->callstatus & CIST_CLSRET) { /* was closing TBC variable? */ + lua_assert(ci->callstatus & CIST_TBC); n = ci->u2.nres; /* just redo 'luaD_poscall' */ /* don't need to reset CIST_CLSRET, as it will be set again anyway */ } else { - int status = LUA_YIELD; /* default if there were no errors */ + TStatus status = LUA_YIELD; /* default if there were no errors */ + lua_KFunction kf = ci->u.c.k; /* continuation function */ /* must have a continuation and must be able to call it */ - lua_assert(ci->u.c.k != NULL && yieldable(L)); + lua_assert(kf != NULL && yieldable(L)); if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ status = finishpcallk(L, ci); /* finish it */ adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ lua_unlock(L); - n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ + n = (*kf)(L, APIstatus(status), ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); } @@ -767,6 +897,7 @@ static CallInfo *findpcall (lua_State *L) { ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { + api_checkpop(L, narg); L->top.p -= narg; /* remove args from the stack */ setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ api_incr_top(L); @@ -821,7 +952,7 @@ static void resume (lua_State *L, void *ud) { ** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't ** find a recover point). */ -static int precover (lua_State *L, int status) { +static TStatus precover (lua_State *L, TStatus status) { CallInfo *ci; while (errorstatus(status) && (ci = findpcall(L)) != NULL) { L->ci = ci; /* go down to recovery functions */ @@ -834,7 +965,7 @@ static int precover (lua_State *L, int status) { LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, int *nresults) { - int status; + TStatus status; lua_lock(L); if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ @@ -849,21 +980,21 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, return resume_error(L, "C stack overflow", nargs); L->nCcalls++; luai_userstateresume(L, nargs); - api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + api_checkpop(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); /* continue running after recoverable errors */ status = precover(L, status); if (l_likely(!errorstatus(status))) lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ - L->status = cast_byte(status); /* mark thread as 'dead' */ + L->status = status; /* mark thread as 'dead' */ luaD_seterrorobj(L, status, L->top.p); /* push error message */ L->ci->top.p = L->top.p; } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield : cast_int(L->top.p - (L->ci->func.p + 1)); lua_unlock(L); - return status; + return APIstatus(status); } @@ -878,9 +1009,9 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, luai_userstateyield(L, nresults); lua_lock(L); ci = L->ci; - api_checknelems(L, nresults); + api_checkpop(L, nresults); if (l_unlikely(!yieldable(L))) { - if (L != G(L)->mainthread) + if (L != mainthread(G(L))) luaG_runerror(L, "attempt to yield across a C-call boundary"); else luaG_runerror(L, "attempt to yield from outside a coroutine"); @@ -908,7 +1039,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, */ struct CloseP { StkId level; - int status; + TStatus status; }; @@ -925,7 +1056,7 @@ static void closepaux (lua_State *L, void *ud) { ** Calls 'luaF_close' in protected mode. Return the original status ** or, in case of errors, the new status. */ -int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { +TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, TStatus status) { CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; for (;;) { /* keep closing upvalues until no more errors */ @@ -947,9 +1078,9 @@ int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { ** thread information ('allowhook', etc.) and in particular ** its stack level in case of errors. */ -int luaD_pcall (lua_State *L, Pfunc func, void *u, - ptrdiff_t old_top, ptrdiff_t ef) { - int status; +TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, + ptrdiff_t ef) { + TStatus status; CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; @@ -981,7 +1112,7 @@ struct SParser { /* data to 'f_parser' */ static void checkmode (lua_State *L, const char *mode, const char *x) { - if (mode && strchr(mode, x[0]) == NULL) { + if (strchr(mode, x[0]) == NULL) { luaO_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", x, mode); luaD_throw(L, LUA_ERRSYNTAX); @@ -992,13 +1123,18 @@ static void checkmode (lua_State *L, const char *mode, const char *x) { static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); + const char *mode = p->mode ? p->mode : "bt"; int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { - checkmode(L, p->mode, "binary"); - cl = luaU_undump(L, p->z, p->name); + int fixed = 0; + if (strchr(mode, 'B') != NULL) + fixed = 1; + else + checkmode(L, mode, "binary"); + cl = luaU_undump(L, p->z, p->name, fixed); } else { - checkmode(L, p->mode, "text"); + checkmode(L, mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } lua_assert(cl->nupvalues == cl->p->sizeupvalues); @@ -1006,10 +1142,10 @@ static void f_parser (lua_State *L, void *ud) { } -int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, - const char *mode) { +TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { struct SParser p; - int status; + TStatus status; incnny(L); /* cannot yield during parsing */ p.z = z; p.name = name; p.mode = mode; p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; @@ -1018,9 +1154,9 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, luaZ_initbuffer(L, &p.buff); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); luaZ_freebuffer(L, &p.buff); - luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); - luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); - luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); + luaM_freearray(L, p.dyd.actvar.arr, cast_sizet(p.dyd.actvar.size)); + luaM_freearray(L, p.dyd.gt.arr, cast_sizet(p.dyd.gt.size)); + luaM_freearray(L, p.dyd.label.arr, cast_sizet(p.dyd.label.size)); decnny(L); return status; } diff --git a/lua/src/ldo.h b/lua/src/ldo.h index 56008ab..b647295 100644 --- a/lua/src/ldo.h +++ b/lua/src/ldo.h @@ -23,10 +23,19 @@ ** 'condmovestack' is used in heavy tests to force a stack reallocation ** at every check. */ + +#if !defined(HARDSTACKTESTS) +#define condmovestack(L,pre,pos) ((void)0) +#else +/* realloc stack keeping its size */ +#define condmovestack(L,pre,pos) \ + { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } +#endif + #define luaD_checkstackaux(L,n,pre,pos) \ if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ { pre; luaD_growstack(L, n, 1); pos; } \ - else { condmovestack(L,pre,pos); } + else { condmovestack(L,pre,pos); } /* In general, 'pre'/'pos' are empty (nothing to save) */ #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) @@ -44,24 +53,24 @@ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ -/* macro to check stack size and GC, preserving 'p' */ -#define checkstackGCp(L,n,p) \ - luaD_checkstackaux(L, n, \ - ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ - luaC_checkGC(L), /* stack grow uses memory */ \ - p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ - - -/* macro to check stack size and GC */ -#define checkstackGC(L,fsize) \ - luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) +/* +** Maximum depth for nested C calls, syntactical nested non-terminals, +** and other features implemented through recursion in C. (Value must +** fit in a 16-bit unsigned integer. It must also be compatible with +** the size of the C stack.) +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 200 +#endif /* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); -LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); -LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, +LUAI_FUNC l_noret luaD_errerr (lua_State *L); +LUAI_FUNC void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop); +LUAI_FUNC TStatus luaD_protectedparser (lua_State *L, ZIO *z, + const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); @@ -71,17 +80,20 @@ LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); -LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); -LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, +LUAI_FUNC TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, + TStatus status); +LUAI_FUNC TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); LUAI_FUNC void luaD_shrinkstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L); +LUAI_FUNC int luaD_checkminstack (lua_State *L); -LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); -LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); +LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); +LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode); +LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); #endif diff --git a/lua/src/ldump.c b/lua/src/ldump.c index f231691..5795788 100644 --- a/lua/src/ldump.c +++ b/lua/src/ldump.c @@ -15,8 +15,11 @@ #include "lua.h" +#include "lapi.h" +#include "lgc.h" #include "lobject.h" #include "lstate.h" +#include "ltable.h" #include "lundump.h" @@ -24,8 +27,11 @@ typedef struct { lua_State *L; lua_Writer writer; void *data; + size_t offset; /* current position relative to beginning of dump */ int strip; int status; + Table *h; /* table to track saved strings */ + lua_Unsigned nstr; /* counter for counting saved strings */ } DumpState; @@ -38,12 +44,33 @@ typedef struct { #define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) +/* +** Dump the block of memory pointed by 'b' with given 'size'. +** 'b' should not be NULL, except for the last call signaling the end +** of the dump. +*/ static void dumpBlock (DumpState *D, const void *b, size_t size) { - if (D->status == 0 && size > 0) { + if (D->status == 0) { /* do not write anything after an error */ lua_unlock(D->L); D->status = (*D->writer)(D->L, b, size, D->data); lua_lock(D->L); + D->offset += size; + } +} + + +/* +** Dump enough zeros to ensure that current position is a multiple of +** 'align'. +*/ +static void dumpAlign (DumpState *D, unsigned align) { + unsigned padding = align - cast_uint(D->offset % align); + if (padding < align) { /* padding == align means no padding */ + static lua_Integer paddingContent = 0; + lua_assert(align <= sizeof(lua_Integer)); + dumpBlock(D, &paddingContent, padding); } + lua_assert(D->offset % align == 0); } @@ -57,25 +84,32 @@ static void dumpByte (DumpState *D, int y) { /* -** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6" -** rounds up the division.) +** size for 'dumpVarint' buffer: each byte can store up to 7 bits. +** (The "+6" rounds up the division.) */ -#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7) +#define DIBS ((l_numbits(lua_Unsigned) + 6) / 7) -static void dumpSize (DumpState *D, size_t x) { +/* +** Dumps an unsigned integer using the MSB Varint encoding +*/ +static void dumpVarint (DumpState *D, lua_Unsigned x) { lu_byte buff[DIBS]; - int n = 0; - do { - buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ - x >>= 7; - } while (x != 0); - buff[DIBS - 1] |= 0x80; /* mark last byte */ + unsigned n = 1; + buff[DIBS - 1] = x & 0x7f; /* fill least-significant byte */ + while ((x >>= 7) != 0) /* fill other bytes in reverse order */ + buff[DIBS - (++n)] = cast_byte((x & 0x7f) | 0x80); dumpVector(D, buff + DIBS - n, n); } +static void dumpSize (DumpState *D, size_t sz) { + dumpVarint(D, cast(lua_Unsigned, sz)); +} + + static void dumpInt (DumpState *D, int x) { - dumpSize(D, x); + lua_assert(x >= 0); + dumpVarint(D, cast_uint(x)); } @@ -84,30 +118,65 @@ static void dumpNumber (DumpState *D, lua_Number x) { } +/* +** Signed integers are coded to keep small values small. (Coding -1 as +** 0xfff...fff would use too many bytes to save a quite common value.) +** A non-negative x is coded as 2x; a negative x is coded as -2x - 1. +** (0 => 0; -1 => 1; 1 => 2; -2 => 3; 2 => 4; ...) +*/ static void dumpInteger (DumpState *D, lua_Integer x) { - dumpVar(D, x); + lua_Unsigned cx = (x >= 0) ? 2u * l_castS2U(x) + : (2u * ~l_castS2U(x)) + 1; + dumpVarint(D, cx); } -static void dumpString (DumpState *D, const TString *s) { - if (s == NULL) - dumpSize(D, 0); +/* +** Dump a String. First dump its "size": +** size==0 is followed by an index and means "reuse saved string with +** that index"; index==0 means NULL. +** size>=1 is followed by the string contents with real size==size-1 and +** means that string, which will be saved with the next available index. +** The real size does not include the ending '\0' (which is not dumped), +** so adding 1 to it cannot overflow a size_t. +*/ +static void dumpString (DumpState *D, TString *ts) { + if (ts == NULL) { + dumpVarint(D, 0); /* will "reuse" NULL */ + dumpVarint(D, 0); /* special index for NULL */ + } else { - size_t size = tsslen(s); - const char *str = getstr(s); - dumpSize(D, size + 1); - dumpVector(D, str, size); + TValue idx; + int tag = luaH_getstr(D->h, ts, &idx); + if (!tagisempty(tag)) { /* string already saved? */ + dumpVarint(D, 0); /* reuse a saved string */ + dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */ + } + else { /* must write and save the string */ + TValue key, value; /* to save the string in the hash */ + size_t size; + const char *s = getlstr(ts, size); + dumpSize(D, size + 1); + dumpVector(D, s, size + 1); /* include ending '\0' */ + D->nstr++; /* one more saved string */ + setsvalue(D->L, &key, ts); /* the string is the key */ + setivalue(&value, l_castU2S(D->nstr)); /* its index is the value */ + luaH_set(D->L, D->h, &key, &value); /* h[ts] = nstr */ + /* integer value does not need barrier */ + } } } static void dumpCode (DumpState *D, const Proto *f) { dumpInt(D, f->sizecode); - dumpVector(D, f->code, f->sizecode); + dumpAlign(D, sizeof(f->code[0])); + lua_assert(f->code != NULL); + dumpVector(D, f->code, cast_uint(f->sizecode)); } -static void dumpFunction(DumpState *D, const Proto *f, TString *psource); +static void dumpFunction (DumpState *D, const Proto *f); static void dumpConstants (DumpState *D, const Proto *f) { int i; @@ -140,7 +209,7 @@ static void dumpProtos (DumpState *D, const Proto *f) { int n = f->sizep; dumpInt(D, n); for (i = 0; i < n; i++) - dumpFunction(D, f->p[i], f->source); + dumpFunction(D, f->p[i]); } @@ -159,12 +228,14 @@ static void dumpDebug (DumpState *D, const Proto *f) { int i, n; n = (D->strip) ? 0 : f->sizelineinfo; dumpInt(D, n); - dumpVector(D, f->lineinfo, n); + if (f->lineinfo != NULL) + dumpVector(D, f->lineinfo, cast_uint(n)); n = (D->strip) ? 0 : f->sizeabslineinfo; dumpInt(D, n); - for (i = 0; i < n; i++) { - dumpInt(D, f->abslineinfo[i].pc); - dumpInt(D, f->abslineinfo[i].line); + if (n > 0) { + /* 'abslineinfo' is an array of structures of int's */ + dumpAlign(D, sizeof(int)); + dumpVector(D, f->abslineinfo, cast_uint(n)); } n = (D->strip) ? 0 : f->sizelocvars; dumpInt(D, n); @@ -180,51 +251,57 @@ static void dumpDebug (DumpState *D, const Proto *f) { } -static void dumpFunction (DumpState *D, const Proto *f, TString *psource) { - if (D->strip || f->source == psource) - dumpString(D, NULL); /* no debug info or same source as its parent */ - else - dumpString(D, f->source); +static void dumpFunction (DumpState *D, const Proto *f) { dumpInt(D, f->linedefined); dumpInt(D, f->lastlinedefined); dumpByte(D, f->numparams); - dumpByte(D, f->is_vararg); + dumpByte(D, f->flag); dumpByte(D, f->maxstacksize); dumpCode(D, f); dumpConstants(D, f); dumpUpvalues(D, f); dumpProtos(D, f); + dumpString(D, D->strip ? NULL : f->source); dumpDebug(D, f); } +#define dumpNumInfo(D, tvar, value) \ + { tvar i = value; dumpByte(D, sizeof(tvar)); dumpVar(D, i); } + + static void dumpHeader (DumpState *D) { dumpLiteral(D, LUA_SIGNATURE); dumpByte(D, LUAC_VERSION); dumpByte(D, LUAC_FORMAT); dumpLiteral(D, LUAC_DATA); - dumpByte(D, sizeof(Instruction)); - dumpByte(D, sizeof(lua_Integer)); - dumpByte(D, sizeof(lua_Number)); - dumpInteger(D, LUAC_INT); - dumpNumber(D, LUAC_NUM); + dumpNumInfo(D, int, LUAC_INT); + dumpNumInfo(D, Instruction, LUAC_INST); + dumpNumInfo(D, lua_Integer, LUAC_INT); + dumpNumInfo(D, lua_Number, LUAC_NUM); } /* ** dump Lua function as precompiled chunk */ -int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, - int strip) { +int luaU_dump (lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { DumpState D; + D.h = luaH_new(L); /* aux. table to keep strings already dumped */ + sethvalue2s(L, L->top.p, D.h); /* anchor it */ + L->top.p++; D.L = L; D.writer = w; + D.offset = 0; D.data = data; D.strip = strip; D.status = 0; + D.nstr = 0; dumpHeader(&D); dumpByte(&D, f->sizeupvalues); - dumpFunction(&D, f, NULL); + dumpFunction(&D, f); + dumpBlock(&D, NULL, 0); /* signal end of dump */ return D.status; } diff --git a/lua/src/lfunc.c b/lua/src/lfunc.c index 0945f24..b6fd9ce 100644 --- a/lua/src/lfunc.c +++ b/lua/src/lfunc.c @@ -100,21 +100,23 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { /* -** Call closing method for object 'obj' with error message 'err'. The +** Call closing method for object 'obj' with error object 'err'. The ** boolean 'yy' controls whether the call is yieldable. ** (This function assumes EXTRA_STACK.) */ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { StkId top = L->top.p; + StkId func = top; const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); - setobj2s(L, top, tm); /* will call metamethod... */ - setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ - setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ - L->top.p = top + 3; /* add function and arguments */ + setobj2s(L, top++, tm); /* will call metamethod... */ + setobj2s(L, top++, obj); /* with 'self' as the 1st argument */ + if (err != NULL) /* if there was an error... */ + setobj2s(L, top++, err); /* then error object will be 2nd argument */ + L->top.p = top; /* add function and arguments */ if (yy) - luaD_call(L, top, 0); + luaD_call(L, func, 0); else - luaD_callnoyield(L, top, 0); + luaD_callnoyield(L, func, 0); } @@ -140,26 +142,28 @@ static void checkclosemth (lua_State *L, StkId level) { ** the 'level' of the upvalue being closed, as everything after that ** won't be used again. */ -static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { +static void prepcallclosemth (lua_State *L, StkId level, TStatus status, + int yy) { TValue *uv = s2v(level); /* value being closed */ TValue *errobj; - if (status == CLOSEKTOP) - errobj = &G(L)->nilvalue; /* error object is nil */ - else { /* 'luaD_seterrorobj' will set top to level + 2 */ - errobj = s2v(level + 1); /* error object goes after 'uv' */ - luaD_seterrorobj(L, status, level + 1); /* set error object */ + switch (status) { + case LUA_OK: + L->top.p = level + 1; /* call will be at this level */ + /* FALLTHROUGH */ + case CLOSEKTOP: /* don't need to change top */ + errobj = NULL; /* no error object */ + break; + default: /* 'luaD_seterrorobj' will set top to level + 2 */ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + break; } callclosemethod(L, uv, errobj, yy); } -/* -** Maximum value for deltas in 'tbclist', dependent on the type -** of delta. (This macro assumes that an 'L' is in scope where it -** is used.) -*/ -#define MAXDELTA \ - ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1) +/* Maximum value for deltas in 'tbclist' */ +#define MAXDELTA USHRT_MAX /* @@ -192,8 +196,7 @@ void luaF_unlinkupval (UpVal *uv) { */ void luaF_closeupval (lua_State *L, StkId level) { UpVal *uv; - StkId upl; /* stack index pointed by 'uv' */ - while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { + while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { TValue *slot = &uv->u.value; /* new position for value */ lua_assert(uplevel(uv) < L->top.p); luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ @@ -224,7 +227,7 @@ static void poptbclist (lua_State *L) { ** Close all upvalues and to-be-closed variables up to the given stack ** level. Return restored 'level'. */ -StkId luaF_close (lua_State *L, StkId level, int status, int yy) { +StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy) { ptrdiff_t levelrel = savestack(L, level); luaF_closeupval(L, level); /* first, close the upvalues */ while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ @@ -253,7 +256,7 @@ Proto *luaF_newproto (lua_State *L) { f->upvalues = NULL; f->sizeupvalues = 0; f->numparams = 0; - f->is_vararg = 0; + f->flag = 0; f->maxstacksize = 0; f->locvars = NULL; f->sizelocvars = 0; @@ -264,14 +267,31 @@ Proto *luaF_newproto (lua_State *L) { } +lu_mem luaF_protosize (Proto *p) { + lu_mem sz = cast(lu_mem, sizeof(Proto)) + + cast_uint(p->sizep) * sizeof(Proto*) + + cast_uint(p->sizek) * sizeof(TValue) + + cast_uint(p->sizelocvars) * sizeof(LocVar) + + cast_uint(p->sizeupvalues) * sizeof(Upvaldesc); + if (!(p->flag & PF_FIXED)) { + sz += cast_uint(p->sizecode) * sizeof(Instruction); + sz += cast_uint(p->sizelineinfo) * sizeof(lu_byte); + sz += cast_uint(p->sizeabslineinfo) * sizeof(AbsLineInfo); + } + return sz; +} + + void luaF_freeproto (lua_State *L, Proto *f) { - luaM_freearray(L, f->code, f->sizecode); - luaM_freearray(L, f->p, f->sizep); - luaM_freearray(L, f->k, f->sizek); - luaM_freearray(L, f->lineinfo, f->sizelineinfo); - luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); - luaM_freearray(L, f->locvars, f->sizelocvars); - luaM_freearray(L, f->upvalues, f->sizeupvalues); + if (!(f->flag & PF_FIXED)) { + luaM_freearray(L, f->code, cast_sizet(f->sizecode)); + luaM_freearray(L, f->lineinfo, cast_sizet(f->sizelineinfo)); + luaM_freearray(L, f->abslineinfo, cast_sizet(f->sizeabslineinfo)); + } + luaM_freearray(L, f->p, cast_sizet(f->sizep)); + luaM_freearray(L, f->k, cast_sizet(f->sizek)); + luaM_freearray(L, f->locvars, cast_sizet(f->sizelocvars)); + luaM_freearray(L, f->upvalues, cast_sizet(f->sizeupvalues)); luaM_free(L, f); } diff --git a/lua/src/lfunc.h b/lua/src/lfunc.h index 3be265e..d6aad3a 100644 --- a/lua/src/lfunc.h +++ b/lua/src/lfunc.h @@ -11,11 +11,11 @@ #include "lobject.h" -#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ - cast_int(sizeof(TValue)) * (n)) +#define sizeCclosure(n) \ + (offsetof(CClosure, upvalue) + sizeof(TValue) * cast_uint(n)) -#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ - cast_int(sizeof(TValue *)) * (n)) +#define sizeLclosure(n) \ + (offsetof(LClosure, upvals) + sizeof(UpVal *) * cast_uint(n)) /* test whether thread is in 'twups' list */ @@ -44,7 +44,7 @@ /* special status to close upvalues preserving the top of the stack */ -#define CLOSEKTOP (-1) +#define CLOSEKTOP (LUA_ERRERR + 1) LUAI_FUNC Proto *luaF_newproto (lua_State *L); @@ -54,8 +54,9 @@ LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); -LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy); LUAI_FUNC void luaF_unlinkupval (UpVal *uv); +LUAI_FUNC lu_mem luaF_protosize (Proto *p); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, int pc); diff --git a/lua/src/lgc.c b/lua/src/lgc.c index 5817f9e..c64d74b 100644 --- a/lua/src/lgc.c +++ b/lua/src/lgc.c @@ -9,7 +9,6 @@ #include "lprefix.h" -#include #include @@ -32,32 +31,13 @@ ** (Large enough to dissipate fixed overheads but small enough ** to allow small steps for the collector.) */ -#define GCSWEEPMAX 100 - -/* -** Maximum number of finalizers to call in each single step. -*/ -#define GCFINMAX 10 - - -/* -** Cost of calling one finalizer. -*/ -#define GCFINALIZECOST 50 - - -/* -** The equivalent, in bytes, of one unit of "work" (visiting a slot, -** sweeping an object, etc.) -*/ -#define WORK2MEM sizeof(TValue) +#define GCSWEEPMAX 20 /* -** macro to adjust 'pause': 'pause' is actually used like -** 'pause / PAUSEADJ' (value chosen by tests) +** Cost (in work units) of running one finalizer. */ -#define PAUSEADJ 100 +#define CWUFIN 10 /* mask with all color bits */ @@ -91,7 +71,14 @@ #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) -#define markvalue(g,o) { checkliveness(g->mainthread,o); \ +/* +** Access to collectable objects in array part of tables +*/ +#define gcvalarr(t,i) \ + ((*getArrTag(t,i) & BIT_ISCOLLECTABLE) ? getArrVal(t,i)->gc : NULL) + + +#define markvalue(g,o) { checkliveness(mainthread(g),o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } @@ -104,8 +91,9 @@ */ #define markobjectN(g,t) { if (t) markobject(g,t); } + static void reallymarkobject (global_State *g, GCObject *o); -static lu_mem atomic (lua_State *L); +static void atomic (lua_State *L); static void entersweep (lua_State *L); @@ -122,6 +110,56 @@ static void entersweep (lua_State *L); #define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) +static l_mem objsize (GCObject *o) { + lu_mem res; + switch (o->tt) { + case LUA_VTABLE: { + res = luaH_size(gco2t(o)); + break; + } + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + res = sizeLclosure(cl->nupvalues); + break; + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + res = sizeCclosure(cl->nupvalues); + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + res = sizeudata(u->nuvalue, u->len); + break; + } + case LUA_VPROTO: { + res = luaF_protosize(gco2p(o)); + break; + } + case LUA_VTHREAD: { + res = luaE_threadsize(gco2th(o)); + break; + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + res = sizestrshr(cast_uint(ts->shrlen)); + break; + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + res = luaS_sizelngstr(ts->u.lnglen, ts->shrlen); + break; + } + case LUA_VUPVAL: { + res = sizeof(UpVal); + break; + } + default: res = 0; lua_assert(0); + } + return cast(l_mem, res); +} + + static GCObject **getgclist (GCObject *o) { switch (o->tt) { case LUA_VTABLE: return &gco2t(o)->gclist; @@ -203,7 +241,7 @@ static int iscleared (global_State *g, const GCObject *o) { ** incremental sweep phase, it clears the black object to white (sweep ** it) to avoid other barrier calls for this same object. (That cannot ** be done is generational mode, as its sweep does not distinguish -** whites from deads.) +** white from dead.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); @@ -217,7 +255,7 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { } else { /* sweep phase */ lua_assert(issweepphase(g)); - if (g->gckind == KGC_INC) /* incremental mode? */ + if (g->gckind != KGC_GENMINOR) /* incremental mode? */ makewhite(g, o); /* mark 'o' as white to avoid other barriers */ } } @@ -230,7 +268,8 @@ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { void luaC_barrierback_ (lua_State *L, GCObject *o) { global_State *g = G(L); lua_assert(isblack(o) && !isdead(g, o)); - lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); + lua_assert((g->gckind != KGC_GENMINOR) + || (isold(o) && getage(o) != G_TOUCHED1)); if (getage(o) == G_TOUCHED2) /* already in gray list? */ set2gray(o); /* make it gray to become touched1 */ else /* link it in 'grayagain' and paint it gray */ @@ -255,7 +294,7 @@ void luaC_fix (lua_State *L, GCObject *o) { ** create a new collectable object (with given type, size, and offset) ** and link it to 'allgc' list. */ -GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { +GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, size_t offset) { global_State *g = G(L); char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); GCObject *o = cast(GCObject *, p + offset); @@ -267,7 +306,10 @@ GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) { } -GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { +/* +** create a new collectable object with no offset. +*/ +GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz) { return luaC_newobjdt(L, tt, sz, 0); } @@ -295,6 +337,7 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { ** (only closures can), and a userdata's metatable must be a table. */ static void reallymarkobject (global_State *g, GCObject *o) { + g->GCmarked += objsize(o); switch (o->tt) { case LUA_VSHRSTR: case LUA_VLNGSTR: { @@ -334,7 +377,7 @@ static void reallymarkobject (global_State *g, GCObject *o) { */ static void markmt (global_State *g) { int i; - for (i=0; i < LUA_NUMTAGS; i++) + for (i=0; i < LUA_NUMTYPES; i++) markobjectN(g, g->mt[i]); } @@ -342,14 +385,10 @@ static void markmt (global_State *g) { /* ** mark all objects in list of being-finalized */ -static lu_mem markbeingfnz (global_State *g) { +static void markbeingfnz (global_State *g) { GCObject *o; - lu_mem count = 0; - for (o = g->tobefnz; o != NULL; o = o->next) { - count++; + for (o = g->tobefnz; o != NULL; o = o->next) markobject(g, o); - } - return count; } @@ -364,12 +403,10 @@ static lu_mem markbeingfnz (global_State *g) { ** upvalues, as they have nothing to be checked. (If the thread gets an ** upvalue later, it will be linked in the list again.) */ -static int remarkupvals (global_State *g) { +static void remarkupvals (global_State *g) { lua_State *thread; lua_State **p = &g->twups; - int work = 0; /* estimate of how much work was done here */ while ((thread = *p) != NULL) { - work++; if (!iswhite(thread) && thread->openupval != NULL) p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ @@ -379,7 +416,6 @@ static int remarkupvals (global_State *g) { thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { lua_assert(getage(uv) <= getage(thread)); - work++; if (!iswhite(uv)) { /* upvalue already visited? */ lua_assert(upisopen(uv) && isgray(uv)); markvalue(g, uv->v.p); /* mark its value */ @@ -387,7 +423,6 @@ static int remarkupvals (global_State *g) { } } } - return work; } @@ -398,11 +433,14 @@ static void cleargraylists (global_State *g) { /* -** mark root set and reset all gray lists, to start a new collection +** mark root set and reset all gray lists, to start a new collection. +** 'GCmarked' is initialized to count the total number of live bytes +** during a cycle. */ static void restartcollection (global_State *g) { cleargraylists(g); - markobject(g, g->mainthread); + g->GCmarked = 0; + markobject(g, mainthread(g)); markvalue(g, &g->l_registry); markmt(g); markbeingfnz(g); /* mark any finalizing object left from previous cycle */ @@ -426,6 +464,8 @@ static void restartcollection (global_State *g) { ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go ** back to a gray list, but then it must become OLD. (That is what ** 'correctgraylist' does when it finds a TOUCHED2 object.) +** This function is a no-op in incremental mode, as objects cannot be +** marked as touched in that mode. */ static void genlink (global_State *g, GCObject *o) { lua_assert(isblack(o)); @@ -433,7 +473,7 @@ static void genlink (global_State *g, GCObject *o) { linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ } /* everything else do not need to be linked back */ else if (getage(o) == G_TOUCHED2) - changeage(o, G_TOUCHED2, G_OLD); /* advance age */ + setage(o, G_OLD); /* advance age */ } @@ -441,13 +481,14 @@ static void genlink (global_State *g, GCObject *o) { ** Traverse a table with weak values and link it to proper list. During ** propagate phase, keep it in 'grayagain' list, to be revisited in the ** atomic phase. In the atomic phase, if table has any white value, -** put it in 'weak' list, to be cleared. +** put it in 'weak' list, to be cleared; otherwise, call 'genlink' +** to check table age in generational mode. */ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (it is not worth traversing it now just to check) */ - int hasclears = (h->alimit > 0); + int hasclears = (h->asize > 0); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ @@ -458,10 +499,30 @@ static void traverseweakvalue (global_State *g, Table *h) { hasclears = 1; /* table will have to be cleared */ } } - if (g->gcstate == GCSatomic && hasclears) - linkgclist(h, g->weak); /* has to be cleared later */ - else + if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + genlink(g, obj2gco(h)); +} + + +/* +** Traverse the array part of a table. +*/ +static int traversearray (global_State *g, Table *h) { + unsigned asize = h->asize; + int marked = 0; /* true if some object is marked in this traversal */ + unsigned i; + for (i = 0; i < asize; i++) { + GCObject *o = gcvalarr(h, i); + if (o != NULL && iswhite(o)) { + marked = 1; + reallymarkobject(g, o); + } + } + return marked; } @@ -478,19 +539,11 @@ static void traverseweakvalue (global_State *g, Table *h) { ** by 'genlink'. */ static int traverseephemeron (global_State *g, Table *h, int inv) { - int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ int hasww = 0; /* true if table has entry "white-key -> white-value" */ unsigned int i; - unsigned int asize = luaH_realasize(h); unsigned int nsize = sizenode(h); - /* traverse array part */ - for (i = 0; i < asize; i++) { - if (valiswhite(&h->array[i])) { - marked = 1; - reallymarkobject(g, gcvalue(&h->array[i])); - } - } + int marked = traversearray(g, h); /* traverse array part */ /* traverse hash part; if 'inv', traverse descending (see 'convergeephemerons') */ for (i = 0; i < nsize; i++) { @@ -522,10 +575,7 @@ static int traverseephemeron (global_State *g, Table *h, int inv) { static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); - unsigned int i; - unsigned int asize = luaH_realasize(h); - for (i = 0; i < asize; i++) /* traverse array part */ - markvalue(g, &h->array[i]); + traversearray(g, h); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ @@ -539,30 +589,46 @@ static void traversestrongtable (global_State *g, Table *h) { } -static lu_mem traversetable (global_State *g, Table *h) { - const char *weakkey, *weakvalue; +/* +** (result & 1) iff weak values; (result & 2) iff weak keys. +*/ +static int getmode (global_State *g, Table *h) { const TValue *mode = gfasttm(g, h->metatable, TM_MODE); - TString *smode; + if (mode == NULL || !ttisstring(mode)) + return 0; /* ignore non-string modes */ + else { + const char *smode = getstr(tsvalue(mode)); + const char *weakkey = strchr(smode, 'k'); + const char *weakvalue = strchr(smode, 'v'); + return ((weakkey != NULL) << 1) | (weakvalue != NULL); + } +} + + +static l_mem traversetable (global_State *g, Table *h) { markobjectN(g, h->metatable); - if (mode && ttisshrstring(mode) && /* is there a weak mode? */ - (cast_void(smode = tsvalue(mode)), - cast_void(weakkey = strchr(getshrstr(smode), 'k')), - cast_void(weakvalue = strchr(getshrstr(smode), 'v')), - (weakkey || weakvalue))) { /* is really weak? */ - if (!weakkey) /* strong keys? */ + switch (getmode(g, h)) { + case 0: /* not weak */ + traversestrongtable(g, h); + break; + case 1: /* weak values */ traverseweakvalue(g, h); - else if (!weakvalue) /* strong values? */ + break; + case 2: /* weak keys */ traverseephemeron(g, h, 0); - else /* all weak */ - linkgclist(h, g->allweak); /* nothing to traverse now */ + break; + case 3: /* all weak; nothing to traverse */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must visit again its metatable */ + else + linkgclist(h, g->allweak); /* must clear collected entries */ + break; } - else /* not weak */ - traversestrongtable(g, h); - return 1 + h->alimit + 2 * allocsizenode(h); + return cast(l_mem, 1 + 2*sizenode(h) + h->asize); } -static int traverseudata (global_State *g, Udata *u) { +static l_mem traverseudata (global_State *g, Udata *u) { int i; markobjectN(g, u->metatable); /* mark its metatable */ for (i = 0; i < u->nuvalue; i++) @@ -577,7 +643,7 @@ static int traverseudata (global_State *g, Udata *u) { ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ -static int traverseproto (global_State *g, Proto *f) { +static l_mem traverseproto (global_State *g, Proto *f) { int i; markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ @@ -592,7 +658,7 @@ static int traverseproto (global_State *g, Proto *f) { } -static int traverseCclosure (global_State *g, CClosure *cl) { +static l_mem traverseCclosure (global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->upvalue[i]); @@ -603,7 +669,7 @@ static int traverseCclosure (global_State *g, CClosure *cl) { ** Traverse a Lua closure, marking its prototype and its upvalues. ** (Both can be NULL while closure is being created.) */ -static int traverseLclosure (global_State *g, LClosure *cl) { +static l_mem traverseLclosure (global_State *g, LClosure *cl) { int i; markobjectN(g, cl->p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ @@ -626,13 +692,13 @@ static int traverseLclosure (global_State *g, LClosure *cl) { ** (which can only happen in generational mode) or if the traverse is in ** the propagate phase (which can only happen in incremental mode). */ -static int traversethread (global_State *g, lua_State *th) { +static l_mem traversethread (global_State *g, lua_State *th) { UpVal *uv; StkId o = th->stack.p; if (isold(th) || g->gcstate == GCSpropagate) linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) - return 1; /* stack not completely built yet */ + return 0; /* stack not completely built yet */ lua_assert(g->gcstate == GCSatomic || th->openupval == NULL || isintwups(th)); for (; o < th->top.p; o++) /* mark live elements in the stack */ @@ -650,14 +716,15 @@ static int traversethread (global_State *g, lua_State *th) { g->twups = th; } } - return 1 + stacksize(th); + return 1 + (th->top.p - th->stack.p); } /* -** traverse one gray object, turning it to black. +** traverse one gray object, turning it to black. Return an estimate +** of the number of slots traversed. */ -static lu_mem propagatemark (global_State *g) { +static l_mem propagatemark (global_State *g) { GCObject *o = g->gray; nw2black(o); g->gray = *getgclist(o); /* remove from 'gray' list */ @@ -673,11 +740,9 @@ static lu_mem propagatemark (global_State *g) { } -static lu_mem propagateall (global_State *g) { - lu_mem tot = 0; +static void propagateall (global_State *g) { while (g->gray) - tot += propagatemark(g); - return tot; + propagatemark(g); } @@ -686,7 +751,6 @@ static lu_mem propagateall (global_State *g) { ** Repeat until it converges, that is, nothing new is marked. 'dir' ** inverts the direction of the traversals, trying to speed up ** convergence on chains in the same table. -** */ static void convergeephemerons (global_State *g) { int changed; @@ -746,11 +810,11 @@ static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); unsigned int i; - unsigned int asize = luaH_realasize(h); + unsigned int asize = h->asize; for (i = 0; i < asize; i++) { - TValue *o = &h->array[i]; - if (iscleared(g, gcvalueN(o))) /* value was collected? */ - setempty(o); /* remove entry */ + GCObject *o = gcvalarr(h, i); + if (iscleared(g, o)) /* value was collected? */ + *getArrTag(h, i) = LUA_VEMPTY; /* remove entry */ } for (n = gnode(h, 0); n < limit; n++) { if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ @@ -770,6 +834,7 @@ static void freeupval (lua_State *L, UpVal *uv) { static void freeobj (lua_State *L, GCObject *o) { + assert_code(l_mem newmem = gettotalbytes(G(L)) - objsize(o)); switch (o->tt) { case LUA_VPROTO: luaF_freeproto(L, gco2p(o)); @@ -801,46 +866,45 @@ static void freeobj (lua_State *L, GCObject *o) { case LUA_VSHRSTR: { TString *ts = gco2ts(o); luaS_remove(L, ts); /* remove it from hash table */ - luaM_freemem(L, ts, sizelstring(ts->shrlen)); + luaM_freemem(L, ts, sizestrshr(cast_uint(ts->shrlen))); break; } case LUA_VLNGSTR: { TString *ts = gco2ts(o); - luaM_freemem(L, ts, sizelstring(ts->u.lnglen)); + if (ts->shrlen == LSTRMEM) /* must free external string? */ + (*ts->falloc)(ts->ud, ts->contents, ts->u.lnglen + 1, 0); + luaM_freemem(L, ts, luaS_sizelngstr(ts->u.lnglen, ts->shrlen)); break; } default: lua_assert(0); } + lua_assert(gettotalbytes(G(L)) == newmem); } /* ** sweep at most 'countin' elements from a list of GCObjects erasing dead ** objects, where a dead object is one marked with the old (non current) -** white; change all non-dead objects back to white, preparing for next -** collection cycle. Return where to continue the traversal or NULL if -** list is finished. ('*countout' gets the number of elements traversed.) +** white; change all non-dead objects back to white (and new), preparing +** for next collection cycle. Return where to continue the traversal or +** NULL if list is finished. */ -static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, - int *countout) { +static GCObject **sweeplist (lua_State *L, GCObject **p, l_mem countin) { global_State *g = G(L); int ow = otherwhite(g); - int i; int white = luaC_white(g); /* current white */ - for (i = 0; *p != NULL && i < countin; i++) { + while (*p != NULL && countin-- > 0) { GCObject *curr = *p; int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } - else { /* change mark to 'white' */ - curr->marked = cast_byte((marked & ~maskgcbits) | white); + else { /* change mark to 'white' and age to 'new' */ + curr->marked = cast_byte((marked & ~maskgcbits) | white | G_NEW); p = &curr->next; /* go to next element */ } } - if (countout) - *countout = i; /* number of elements traversed */ return (*p == NULL) ? NULL : p; } @@ -851,7 +915,7 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, static GCObject **sweeptolive (lua_State *L, GCObject **p) { GCObject **old = p; do { - p = sweeplist(L, p, 1, NULL); + p = sweeplist(L, p, 1); } while (p == old); return p; } @@ -870,11 +934,8 @@ static GCObject **sweeptolive (lua_State *L, GCObject **p) { */ static void checkSizes (lua_State *L, global_State *g) { if (!g->gcemergency) { - if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ - l_mem olddebt = g->GCdebt; + if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ luaS_resize(L, g->strt.size / 2); - g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ - } } } @@ -912,9 +973,9 @@ static void GCTM (lua_State *L) { setgcovalue(L, &v, udata2finalize(g)); tm = luaT_gettmbyobj(L, &v, TM_GC); if (!notm(tm)) { /* is there a finalizer? */ - int status; + TStatus status; lu_byte oldah = L->allowhook; - int oldgcstp = g->gcstp; + lu_byte oldgcstp = g->gcstp; g->gcstp |= GCSTPGC; /* avoid GC steps */ L->allowhook = 0; /* stop debug hooks during GC metamethod */ setobj2s(L, L->top.p++, tm); /* push finalizer... */ @@ -932,18 +993,6 @@ static void GCTM (lua_State *L) { } -/* -** Call a few finalizers -*/ -static int runafewfinalizers (lua_State *L, int n) { - global_State *g = G(L); - int i; - for (i = 0; i < n && g->tobefnz; i++) - GCTM(L); /* call one finalizer */ - return i; -} - - /* ** call all pending finalizers */ @@ -1049,23 +1098,31 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { ** ======================================================= */ +/* +** Fields 'GCmarked' and 'GCmajorminor' are used to control the pace and +** the mode of the collector. They play several roles, depending on the +** mode of the collector: +** * KGC_INC: +** GCmarked: number of marked bytes during a cycle. +** GCmajorminor: not used. +** * KGC_GENMINOR +** GCmarked: number of bytes that became old since last major collection. +** GCmajorminor: number of bytes marked in last major collection. +** * KGC_GENMAJOR +** GCmarked: number of bytes that became old since last major collection. +** GCmajorminor: number of bytes marked in last major collection. +*/ + /* -** Set the "time" to wait before starting a new GC cycle; cycle will -** start when memory use hits the threshold of ('estimate' * pause / -** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, -** because Lua cannot even start with less than PAUSEADJ bytes). +** Set the "time" to wait before starting a new incremental cycle; +** cycle will start when number of bytes in use hits the threshold of +** approximately (marked * pause / 100). */ static void setpause (global_State *g) { - l_mem threshold, debt; - int pause = getgcparam(g->gcpause); - l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ - lua_assert(estimate > 0); - threshold = (pause < MAX_LMEM / estimate) /* overflow? */ - ? estimate * pause /* no overflow */ - : MAX_LMEM; /* overflow; truncate to maximum */ - debt = gettotalbytes(g) - threshold; - if (debt > 0) debt = 0; + l_mem threshold = applygcparam(g, PAUSE, g->GCmarked); + l_mem debt = threshold - gettotalbytes(g); + if (debt < 0) debt = 0; luaE_setdebt(g, debt); } @@ -1113,7 +1170,8 @@ static void sweep2old (lua_State *L, GCObject **p) { ** will also remove objects turned white here from any gray list. */ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, - GCObject *limit, GCObject **pfirstold1) { + GCObject *limit, GCObject **pfirstold1, + l_mem *paddedold) { static const lu_byte nextage[] = { G_SURVIVAL, /* from G_NEW */ G_OLD1, /* from G_SURVIVAL */ @@ -1123,6 +1181,7 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ }; + l_mem addedold = 0; int white = luaC_white(g); GCObject *curr; while ((curr = *p) != limit) { @@ -1132,42 +1191,38 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, freeobj(L, curr); /* erase 'curr' */ } else { /* correct mark and age */ - if (getage(curr) == G_NEW) { /* new objects go back to white */ + int age = getage(curr); + if (age == G_NEW) { /* new objects go back to white */ int marked = curr->marked & ~maskgcbits; /* erase GC bits */ curr->marked = cast_byte(marked | G_SURVIVAL | white); } else { /* all other objects will be old, and so keep their color */ - setage(curr, nextage[getage(curr)]); - if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) - *pfirstold1 = curr; /* first OLD1 object in the list */ + lua_assert(age != G_OLD1); /* advanced in 'markold' */ + setage(curr, nextage[age]); + if (getage(curr) == G_OLD1) { + addedold += objsize(curr); /* bytes becoming old */ + if (*pfirstold1 == NULL) + *pfirstold1 = curr; /* first OLD1 object in the list */ + } } p = &curr->next; /* go to next element */ } } + *paddedold += addedold; return p; } /* -** Traverse a list making all its elements white and clearing their -** age. In incremental mode, all objects are 'new' all the time, -** except for fixed strings (which are always old). -*/ -static void whitelist (global_State *g, GCObject *p) { - int white = luaC_white(g); - for (; p != NULL; p = p->next) - p->marked = cast_byte((p->marked & ~maskgcbits) | white); -} - - -/* -** Correct a list of gray objects. Return pointer to where rest of the -** list should be linked. +** Correct a list of gray objects. Return a pointer to the last element +** left on the list, so that we can link another list to the end of +** this one. ** Because this correction is done after sweeping, young objects might ** be turned white and still be in the list. They are only removed. ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; -** Non-white threads also remain on the list; 'TOUCHED2' objects become -** regular old; they and anything else are removed from the list. +** Non-white threads also remain on the list. 'TOUCHED2' objects and +** anything else become regular old, are marked black, and are removed +** from the list. */ static GCObject **correctgraylist (GCObject **p) { GCObject *curr; @@ -1178,7 +1233,7 @@ static GCObject **correctgraylist (GCObject **p) { else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ lua_assert(isgray(curr)); nw2black(curr); /* make it black, for next barrier */ - changeage(curr, G_TOUCHED1, G_TOUCHED2); + setage(curr, G_TOUCHED2); goto remain; /* keep it in the list and go to next element */ } else if (curr->tt == LUA_VTHREAD) { @@ -1188,7 +1243,7 @@ static GCObject **correctgraylist (GCObject **p) { else { /* everything else is removed */ lua_assert(isold(curr)); /* young objects should be white here */ if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ - changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */ + setage(curr, G_OLD); /* ... to OLD */ nw2black(curr); /* make object black (to be removed) */ goto remove; } @@ -1215,15 +1270,15 @@ static void correctgraylists (global_State *g) { /* ** Mark black 'OLD1' objects when starting a new young collection. -** Gray objects are already in some gray list, and so will be visited -** in the atomic step. +** Gray objects are already in some gray list, and so will be visited in +** the atomic step. */ static void markold (global_State *g, GCObject *from, GCObject *to) { GCObject *p; for (p = from; p != to; p = p->next) { if (getage(p) == G_OLD1) { lua_assert(!iswhite(p)); - changeage(p, G_OLD1, G_OLD); /* now they are old */ + setage(p, G_OLD); /* now they are old */ if (isblack(p)) reallymarkobject(g, p); } @@ -1238,17 +1293,48 @@ static void finishgencycle (lua_State *L, global_State *g) { correctgraylists(g); checkSizes(L, g); g->gcstate = GCSpropagate; /* skip restart */ - if (!g->gcemergency) + if (!g->gcemergency && luaD_checkminstack(L)) callallpendingfinalizers(L); } +/* +** Shifts from a minor collection to major collections. It starts in +** the "sweep all" state to clear all objects, which are mostly black +** in generational mode. +*/ +static void minor2inc (lua_State *L, global_State *g, lu_byte kind) { + g->GCmajorminor = g->GCmarked; /* number of live bytes */ + g->gckind = kind; + g->reallyold = g->old1 = g->survival = NULL; + g->finobjrold = g->finobjold1 = g->finobjsur = NULL; + entersweep(L); /* continue as an incremental cycle */ + /* set a debt equal to the step size */ + luaE_setdebt(g, applygcparam(g, STEPSIZE, 100)); +} + + +/* +** Decide whether to shift to major mode. It shifts if the accumulated +** number of added old bytes (counted in 'GCmarked') is larger than +** 'minormajor'% of the number of lived bytes after the last major +** collection. (This number is kept in 'GCmajorminor'.) +*/ +static int checkminormajor (global_State *g) { + l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); + if (limit == 0) + return 0; /* special case: 'minormajor' 0 stops major collections */ + return (g->GCmarked >= limit); +} + /* ** Does a young collection. First, mark 'OLD1' objects. Then does the -** atomic step. Then, sweep all lists and advance pointers. Finally, -** finish the collection. +** atomic step. Then, check whether to continue in minor mode. If so, +** sweep all lists and advance pointers. Finally, finish the collection. */ static void youngcollection (lua_State *L, global_State *g) { + l_mem addedold1 = 0; + l_mem marked = g->GCmarked; /* preserve 'g->GCmarked' */ GCObject **psurvival; /* to point to first non-dead survival object */ GCObject *dummy; /* dummy out parameter to 'sweepgen' */ lua_assert(g->gcstate == GCSpropagate); @@ -1258,28 +1344,39 @@ static void youngcollection (lua_State *L, global_State *g) { } markold(g, g->finobj, g->finobjrold); markold(g, g->tobefnz, NULL); - atomic(L); + + atomic(L); /* will lose 'g->marked' */ /* sweep nursery and get a pointer to its last live element */ g->gcstate = GCSswpallgc; - psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); + psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1, &addedold1); /* sweep 'survival' */ - sweepgen(L, g, psurvival, g->old1, &g->firstold1); + sweepgen(L, g, psurvival, g->old1, &g->firstold1, &addedold1); g->reallyold = g->old1; g->old1 = *psurvival; /* 'survival' survivals are old now */ g->survival = g->allgc; /* all news are survivals */ /* repeat for 'finobj' lists */ dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ - psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); + psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy, &addedold1); /* sweep 'survival' */ - sweepgen(L, g, psurvival, g->finobjold1, &dummy); + sweepgen(L, g, psurvival, g->finobjold1, &dummy, &addedold1); g->finobjrold = g->finobjold1; g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ g->finobjsur = g->finobj; /* all news are survivals */ - sweepgen(L, g, &g->tobefnz, NULL, &dummy); - finishgencycle(L, g); + sweepgen(L, g, &g->tobefnz, NULL, &dummy, &addedold1); + + /* keep total number of added old1 bytes */ + g->GCmarked = marked + addedold1; + + /* decide whether to shift to major mode */ + if (checkminormajor(g)) { + minor2inc(L, g, KGC_GENMAJOR); /* go to major mode */ + g->GCmarked = 0; /* avoid pause in first major cycle (see 'setpause') */ + } + else + finishgencycle(L, g); /* still in minor mode; finish it */ } @@ -1304,19 +1401,21 @@ static void atomic2gen (lua_State *L, global_State *g) { sweep2old(L, &g->tobefnz); - g->gckind = KGC_GEN; - g->lastatomic = 0; - g->GCestimate = gettotalbytes(g); /* base for memory control */ + g->gckind = KGC_GENMINOR; + g->GCmajorminor = g->GCmarked; /* "base" for number of bytes */ + g->GCmarked = 0; /* to count the number of added old1 bytes */ finishgencycle(L, g); } /* ** Set debt for the next minor collection, which will happen when -** memory grows 'genminormul'%. +** total number of bytes grows 'genminormul'% in relation to +** the base, GCmajorminor, which is the number of bytes being used +** after the last major collection. */ static void setminordebt (global_State *g) { - luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); + luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor)); } @@ -1326,31 +1425,12 @@ static void setminordebt (global_State *g) { ** are cleared. Then, turn all objects into old and finishes the ** collection. */ -static lu_mem entergen (lua_State *L, global_State *g) { - lu_mem numobjs; - luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ - luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - numobjs = atomic(L); /* propagates all and then do the atomic stuff */ +static void entergen (lua_State *L, global_State *g) { + luaC_runtilstate(L, GCSpause, 1); /* prepare to start a new cycle */ + luaC_runtilstate(L, GCSpropagate, 1); /* start new cycle */ + atomic(L); /* propagates all and then do the atomic stuff */ atomic2gen(L, g); setminordebt(g); /* set debt assuming next cycle will be minor */ - return numobjs; -} - - -/* -** Enter incremental mode. Turn all objects white, make all -** intermediate lists point to NULL (to avoid invalid pointers), -** and go to the pause state. -*/ -static void enterinc (global_State *g) { - whitelist(g, g->allgc); - g->reallyold = g->old1 = g->survival = NULL; - whitelist(g, g->finobj); - whitelist(g, g->tobefnz); - g->finobjrold = g->finobjold1 = g->finobjsur = NULL; - g->gcstate = GCSpause; - g->gckind = KGC_INC; - g->lastatomic = 0; } @@ -1359,111 +1439,49 @@ static void enterinc (global_State *g) { */ void luaC_changemode (lua_State *L, int newmode) { global_State *g = G(L); - if (newmode != g->gckind) { - if (newmode == KGC_GEN) /* entering generational mode? */ + if (g->gckind == KGC_GENMAJOR) /* doing major collections? */ + g->gckind = KGC_INC; /* already incremental but in name */ + if (newmode != g->gckind) { /* does it need to change? */ + if (newmode == KGC_INC) /* entering incremental mode? */ + minor2inc(L, g, KGC_INC); /* entering incremental mode */ + else { + lua_assert(newmode == KGC_GENMINOR); entergen(L, g); - else - enterinc(g); /* entering incremental mode */ + } } - g->lastatomic = 0; } /* ** Does a full collection in generational mode. */ -static lu_mem fullgen (lua_State *L, global_State *g) { - enterinc(g); - return entergen(L, g); -} - - -/* -** Does a major collection after last collection was a "bad collection". -** -** When the program is building a big structure, it allocates lots of -** memory but generates very little garbage. In those scenarios, -** the generational mode just wastes time doing small collections, and -** major collections are frequently what we call a "bad collection", a -** collection that frees too few objects. To avoid the cost of switching -** between generational mode and the incremental mode needed for full -** (major) collections, the collector tries to stay in incremental mode -** after a bad collection, and to switch back to generational mode only -** after a "good" collection (one that traverses less than 9/8 objects -** of the previous one). -** The collector must choose whether to stay in incremental mode or to -** switch back to generational mode before sweeping. At this point, it -** does not know the real memory in use, so it cannot use memory to -** decide whether to return to generational mode. Instead, it uses the -** number of objects traversed (returned by 'atomic') as a proxy. The -** field 'g->lastatomic' keeps this count from the last collection. -** ('g->lastatomic != 0' also means that the last collection was bad.) -*/ -static void stepgenfull (lua_State *L, global_State *g) { - lu_mem newatomic; /* count of traversed objects */ - lu_mem lastatomic = g->lastatomic; /* count from last collection */ - if (g->gckind == KGC_GEN) /* still in generational mode? */ - enterinc(g); /* enter incremental mode */ - luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - newatomic = atomic(L); /* mark everybody */ - if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ - atomic2gen(L, g); /* return to generational mode */ - setminordebt(g); - } - else { /* another bad collection; stay in incremental mode */ - g->GCestimate = gettotalbytes(g); /* first estimate */ - entersweep(L); - luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ - setpause(g); - g->lastatomic = newatomic; - } +static void fullgen (lua_State *L, global_State *g) { + minor2inc(L, g, KGC_INC); + entergen(L, g); } /* -** Does a generational "step". -** Usually, this means doing a minor collection and setting the debt to -** make another collection when memory grows 'genminormul'% larger. -** -** However, there are exceptions. If memory grows 'genmajormul'% -** larger than it was at the end of the last major collection (kept -** in 'g->GCestimate'), the function does a major collection. At the -** end, it checks whether the major collection was able to free a -** decent amount of memory (at least half the growth in memory since -** previous major collection). If so, the collector keeps its state, -** and the next collection will probably be minor again. Otherwise, -** we have what we call a "bad collection". In that case, set the field -** 'g->lastatomic' to signal that fact, so that the next collection will -** go to 'stepgenfull'. -** -** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; -** in that case, do a minor collection. -*/ -static void genstep (lua_State *L, global_State *g) { - if (g->lastatomic != 0) /* last collection was a bad one? */ - stepgenfull(L, g); /* do a full step */ - else { - lu_mem majorbase = g->GCestimate; /* memory after last major collection */ - lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); - if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { - lu_mem numobjs = fullgen(L, g); /* do a major collection */ - if (gettotalbytes(g) < majorbase + (majorinc / 2)) { - /* collected at least half of memory growth since last major - collection; keep doing minor collections. */ - lua_assert(g->lastatomic == 0); - } - else { /* bad collection */ - g->lastatomic = numobjs; /* signal that last collection was bad */ - setpause(g); /* do a long wait for next (major) collection */ - } - } - else { /* regular case; do a minor collection */ - youngcollection(L, g); +** After an atomic incremental step from a major collection, +** check whether collector could return to minor collections. +** It checks whether the number of bytes 'tobecollected' +** is greater than 'majorminor'% of the number of bytes added +** since the last collection ('addedbytes'). +*/ +static int checkmajorminor (lua_State *L, global_State *g) { + if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ + l_mem numbytes = gettotalbytes(g); + l_mem addedbytes = numbytes - g->GCmajorminor; + l_mem limit = applygcparam(g, MAJORMINOR, addedbytes); + l_mem tobecollected = numbytes - g->GCmarked; + if (tobecollected > limit) { + atomic2gen(L, g); /* return to generational mode */ setminordebt(g); - g->GCestimate = majorbase; /* preserve base value */ + return 1; /* exit incremental collection */ } } - lua_assert(isdecGCmodegen(g)); + g->GCmajorminor = g->GCmarked; /* prepare for next collection */ + return 0; /* stay doing incremental collections */ } /* }====================================================== */ @@ -1515,32 +1533,31 @@ void luaC_freeallobjects (lua_State *L) { separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L); - deletelist(L, g->allgc, obj2gco(g->mainthread)); + deletelist(L, g->allgc, obj2gco(mainthread(g))); lua_assert(g->finobj == NULL); /* no new finalizers */ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } -static lu_mem atomic (lua_State *L) { +static void atomic (lua_State *L) { global_State *g = G(L); - lu_mem work = 0; GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ g->grayagain = NULL; lua_assert(g->ephemeron == NULL && g->weak == NULL); - lua_assert(!iswhite(g->mainthread)); + lua_assert(!iswhite(mainthread(g))); g->gcstate = GCSatomic; markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); markmt(g); /* mark global metatables */ - work += propagateall(g); /* empties 'gray' list */ + propagateall(g); /* empties 'gray' list */ /* remark occasional upvalues of (maybe) dead threads */ - work += remarkupvals(g); - work += propagateall(g); /* propagate changes */ + remarkupvals(g); + propagateall(g); /* propagate changes */ g->gray = grayagain; - work += propagateall(g); /* traverse 'grayagain' list */ + propagateall(g); /* traverse 'grayagain' list */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ @@ -1548,154 +1565,197 @@ static lu_mem atomic (lua_State *L) { clearbyvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; separatetobefnz(g, 0); /* separate objects to be finalized */ - work += markbeingfnz(g); /* mark objects that will be finalized */ - work += propagateall(g); /* remark, to propagate 'resurrection' */ + markbeingfnz(g); /* mark objects that will be finalized */ + propagateall(g); /* remark, to propagate 'resurrection' */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ - clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ - clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ /* clear values from resurrected weak tables */ clearbyvalues(g, g->weak, origweak); clearbyvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ lua_assert(g->gray == NULL); - return work; /* estimate of slots marked by 'atomic' */ } -static int sweepstep (lua_State *L, global_State *g, - int nextstate, GCObject **nextlist) { - if (g->sweepgc) { - l_mem olddebt = g->GCdebt; - int count; - g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); - g->GCestimate += g->GCdebt - olddebt; /* update estimate */ - return count; - } +/* +** Do a sweep step. The normal case (not fast) sweeps at most GCSWEEPMAX +** elements. The fast case sweeps the whole list. +*/ +static void sweepstep (lua_State *L, global_State *g, + lu_byte nextstate, GCObject **nextlist, int fast) { + if (g->sweepgc) + g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LMEM : GCSWEEPMAX); else { /* enter next state */ g->gcstate = nextstate; g->sweepgc = nextlist; - return 0; /* no work done */ } } -static lu_mem singlestep (lua_State *L) { +/* +** Performs one incremental "step" in an incremental garbage collection. +** For indivisible work, a step goes to the next state. When marking +** (propagating), a step traverses one object. When sweeping, a step +** sweeps GCSWEEPMAX objects, to avoid a big overhead for sweeping +** objects one by one. (Sweeping is inexpensive, no matter the +** object.) When 'fast' is true, 'singlestep' tries to finish a state +** "as fast as possible". In particular, it skips the propagation +** phase and leaves all objects to be traversed by the atomic phase: +** That avoids traversing twice some objects, such as threads and +** weak tables. +*/ + +#define step2pause -3 /* finished collection; entered pause state */ +#define atomicstep -2 /* atomic step */ +#define step2minor -1 /* moved to minor collections */ + + +static l_mem singlestep (lua_State *L, int fast) { global_State *g = G(L); - lu_mem work; + l_mem stepresult; lua_assert(!g->gcstopem); /* collector is not reentrant */ g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { case GCSpause: { restartcollection(g); g->gcstate = GCSpropagate; - work = 1; + stepresult = 1; break; } case GCSpropagate: { - if (g->gray == NULL) { /* no more gray objects? */ + if (fast || g->gray == NULL) { g->gcstate = GCSenteratomic; /* finish propagate phase */ - work = 0; + stepresult = 1; } else - work = propagatemark(g); /* traverse one gray object */ + stepresult = propagatemark(g); /* traverse one gray object */ break; } case GCSenteratomic: { - work = atomic(L); /* work is what was traversed by 'atomic' */ - entersweep(L); - g->GCestimate = gettotalbytes(g); /* first estimate */ + atomic(L); + if (checkmajorminor(L, g)) + stepresult = step2minor; + else { + entersweep(L); + stepresult = atomicstep; + } break; } case GCSswpallgc: { /* sweep "regular" objects */ - work = sweepstep(L, g, GCSswpfinobj, &g->finobj); + sweepstep(L, g, GCSswpfinobj, &g->finobj, fast); + stepresult = GCSWEEPMAX; break; } case GCSswpfinobj: { /* sweep objects with finalizers */ - work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast); + stepresult = GCSWEEPMAX; break; } case GCSswptobefnz: { /* sweep objects to be finalized */ - work = sweepstep(L, g, GCSswpend, NULL); + sweepstep(L, g, GCSswpend, NULL, fast); + stepresult = GCSWEEPMAX; break; } case GCSswpend: { /* finish sweeps */ checkSizes(L, g); g->gcstate = GCScallfin; - work = 0; + stepresult = GCSWEEPMAX; break; } - case GCScallfin: { /* call remaining finalizers */ - if (g->tobefnz && !g->gcemergency) { + case GCScallfin: { /* call finalizers */ + if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) { g->gcstopem = 0; /* ok collections during finalizers */ - work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; + GCTM(L); /* call one finalizer */ + stepresult = CWUFIN; } - else { /* emergency mode or no more finalizers */ + else { /* no more finalizers or emergency mode or no enough stack + to run finalizers */ g->gcstate = GCSpause; /* finish collection */ - work = 0; + stepresult = step2pause; } break; } default: lua_assert(0); return 0; } g->gcstopem = 0; - return work; + return stepresult; } /* -** advances the garbage collector until it reaches a state allowed -** by 'statemask' +** Advances the garbage collector until it reaches the given state. +** (The option 'fast' is only for testing; in normal code, 'fast' +** here is always true.) */ -void luaC_runtilstate (lua_State *L, int statesmask) { +void luaC_runtilstate (lua_State *L, int state, int fast) { global_State *g = G(L); - while (!testbit(statesmask, g->gcstate)) - singlestep(L); + lua_assert(g->gckind == KGC_INC); + while (state != g->gcstate) + singlestep(L, fast); } /* -** Performs a basic incremental step. The debt and step size are +** Performs a basic incremental step. The step size is ** converted from bytes to "units of work"; then the function loops ** running single steps until adding that many units of work or ** finishing a cycle (pause state). Finally, it sets the debt that ** controls when next step will be performed. */ static void incstep (lua_State *L, global_State *g) { - int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ - l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; - l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) - ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul - : MAX_LMEM; /* overflow; keep maximum value */ - do { /* repeat until pause or enough "credit" (negative debt) */ - lu_mem work = singlestep(L); /* perform one single step */ - debt -= work; - } while (debt > -stepsize && g->gcstate != GCSpause); + l_mem stepsize = applygcparam(g, STEPSIZE, 100); + l_mem work2do = applygcparam(g, STEPMUL, stepsize / cast_int(sizeof(void*))); + l_mem stres; + int fast = (work2do == 0); /* special case: do a full collection */ + do { /* repeat until enough work */ + stres = singlestep(L, fast); /* perform one single step */ + if (stres == step2minor) /* returned to minor collections? */ + return; /* nothing else to be done here */ + else if (stres == step2pause || (stres == atomicstep && !fast)) + break; /* end of cycle or atomic */ + else + work2do -= stres; + } while (fast || work2do > 0); if (g->gcstate == GCSpause) setpause(g); /* pause until next cycle */ - else { - debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ - luaE_setdebt(g, debt); - } + else + luaE_setdebt(g, stepsize); } + +#if !defined(luai_tracegc) +#define luai_tracegc(L,f) ((void)0) +#endif + /* -** Performs a basic GC step if collector is running. (If collector is -** not running, set a reasonable debt to avoid it being called at -** every single check.) +** Performs a basic GC step if collector is running. (If collector was +** stopped by the user, set a reasonable debt to avoid it being called +** at every single check.) */ void luaC_step (lua_State *L) { global_State *g = G(L); - if (!gcrunning(g)) /* not running? */ - luaE_setdebt(g, -2000); + lua_assert(!g->gcemergency); + if (!gcrunning(g)) { /* not running? */ + if (g->gcstp & GCSTPUSR) /* stopped by the user? */ + luaE_setdebt(g, 20000); + } else { - if(isdecGCmodegen(g)) - genstep(L, g); - else - incstep(L, g); + luai_tracegc(L, 1); /* for internal debugging */ + switch (g->gckind) { + case KGC_INC: case KGC_GENMAJOR: + incstep(L, g); + break; + case KGC_GENMINOR: + youngcollection(L, g); + setminordebt(g); + break; + } + luai_tracegc(L, 0); /* for internal debugging */ } } @@ -1711,13 +1771,9 @@ static void fullinc (lua_State *L, global_State *g) { if (keepinvariant(g)) /* black objects? */ entersweep(L); /* sweep everything to turn them back to white */ /* finish any pending sweep phase to start a new cycle */ - luaC_runtilstate(L, bitmask(GCSpause)); - luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ - g->gcstate = GCSenteratomic; /* go straight to atomic phase */ - luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ - /* estimate must be correct after a full GC cycle */ - lua_assert(g->GCestimate == gettotalbytes(g)); - luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ + luaC_runtilstate(L, GCSpause, 1); + luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */ + luaC_runtilstate(L, GCSpause, 1); /* finish collection */ setpause(g); } @@ -1730,11 +1786,16 @@ static void fullinc (lua_State *L, global_State *g) { void luaC_fullgc (lua_State *L, int isemergency) { global_State *g = G(L); lua_assert(!g->gcemergency); - g->gcemergency = isemergency; /* set flag */ - if (g->gckind == KGC_INC) - fullinc(L, g); - else - fullgen(L, g); + g->gcemergency = cast_byte(isemergency); /* set flag */ + switch (g->gckind) { + case KGC_GENMINOR: fullgen(L, g); break; + case KGC_INC: fullinc(L, g); break; + case KGC_GENMAJOR: + g->gckind = KGC_INC; + fullinc(L, g); + g->gckind = KGC_GENMAJOR; + break; + } g->gcemergency = 0; } diff --git a/lua/src/lgc.h b/lua/src/lgc.h index 538f6ed..ee05417 100644 --- a/lua/src/lgc.h +++ b/lua/src/lgc.h @@ -8,6 +8,9 @@ #define lgc_h +#include + + #include "lobject.h" #include "lstate.h" @@ -20,8 +23,9 @@ ** never point to a white one. Moreover, any gray object must be in a ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it ** can be visited again before finishing the collection cycle. (Open -** upvalues are an exception to this rule.) These lists have no meaning -** when the invariant is not being enforced (e.g., sweep phase). +** upvalues are an exception to this rule, as they are attached to +** a corresponding thread.) These lists have no meaning when the +** invariant is not being enforced (e.g., sweep phase). */ @@ -45,10 +49,10 @@ /* ** macro to tell when main invariant (white objects cannot point to black -** ones) must be kept. During a collection, the sweep -** phase may break the invariant, as objects turned white may point to -** still-black objects. The invariant is restored when sweep ends and -** all objects are white again. +** ones) must be kept. During a collection, the sweep phase may break +** the invariant, as objects turned white may point to still-black +** objects. The invariant is restored when sweep ends and all objects +** are white again. */ #define keepinvariant(g) ((g)->gcstate <= GCSatomic) @@ -117,36 +121,90 @@ #define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) #define isold(o) (getage(o) > G_SURVIVAL) -#define changeage(o,f,t) \ - check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) +/* +** In generational mode, objects are created 'new'. After surviving one +** cycle, they become 'survival'. Both 'new' and 'survival' can point +** to any other object, as they are traversed at the end of the cycle. +** We call them both 'young' objects. +** If a survival object survives another cycle, it becomes 'old1'. +** 'old1' objects can still point to survival objects (but not to +** new objects), so they still must be traversed. After another cycle +** (that, being old, 'old1' objects will "survive" no matter what) +** finally the 'old1' object becomes really 'old', and then they +** are no more traversed. +** +** To keep its invariants, the generational mode uses the same barriers +** also used by the incremental mode. If a young object is caught in a +** forward barrier, it cannot become old immediately, because it can +** still point to other young objects. Instead, it becomes 'old0', +** which in the next cycle becomes 'old1'. So, 'old0' objects is +** old but can point to new and survival objects; 'old1' is old +** but cannot point to new objects; and 'old' cannot point to any +** young object. +** +** If any old object ('old0', 'old1', 'old') is caught in a back +** barrier, it becomes 'touched1' and goes into a gray list, to be +** visited at the end of the cycle. There it evolves to 'touched2', +** which can point to survivals but not to new objects. In yet another +** cycle then it becomes 'old' again. +** +** The generational mode must also control the colors of objects, +** because of the barriers. While the mutator is running, young objects +** are kept white. 'old', 'old1', and 'touched2' objects are kept black, +** as they cannot point to new objects; exceptions are threads and open +** upvalues, which age to 'old1' and 'old' but are kept gray. 'old0' +** objects may be gray or black, as in the incremental mode. 'touched1' +** objects are kept gray, as they must be visited again at the end of +** the cycle. +*/ -/* Default Values for GC parameters */ -#define LUAI_GENMAJORMUL 100 -#define LUAI_GENMINORMUL 20 -/* wait memory to double before starting new cycle */ -#define LUAI_GCPAUSE 200 +/* +** {====================================================== +** Default Values for GC parameters +** ======================================================= +*/ /* -** some gc parameters are stored divided by 4 to allow a maximum value -** up to 1023 in a 'lu_byte'. +** Minor collections will shift to major ones after LUAI_MINORMAJOR% +** bytes become old. */ -#define getgcparam(p) ((p) * 4) -#define setgcparam(p,v) ((p) = (v) / 4) +#define LUAI_MINORMAJOR 70 -#define LUAI_GCMUL 100 +/* +** Major collections will shift to minor ones after a collection +** collects at least LUAI_MAJORMINOR% of the new bytes. +*/ +#define LUAI_MAJORMINOR 50 -/* how much to allocate before next GC step (log2) */ -#define LUAI_GCSTEPSIZE 13 /* 8 KB */ +/* +** A young (minor) collection will run after creating LUAI_GENMINORMUL% +** new bytes. +*/ +#define LUAI_GENMINORMUL 20 + + +/* incremental */ +/* Number of bytes must be LUAI_GCPAUSE% before starting new cycle */ +#define LUAI_GCPAUSE 250 /* -** Check whether the declared GC mode is generational. While in -** generational mode, the collector can go temporarily to incremental -** mode to improve performance. This is signaled by 'g->lastatomic != 0'. +** Step multiplier: The collector handles LUAI_GCMUL% work units for +** each new allocated word. (Each "work unit" corresponds roughly to +** sweeping one object or traversing one slot.) */ -#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) +#define LUAI_GCMUL 200 + +/* How many bytes to allocate before next GC step */ +#define LUAI_GCSTEPSIZE (200 * sizeof(Table)) + + +#define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v)) +#define applygcparam(g,p,x) luaO_applyparam(g->gcparams[LUA_GCP##p], x) + +/* }====================================================== */ /* @@ -159,14 +217,22 @@ /* -** Does one step of collection when debt becomes positive. 'pre'/'pos' +** Does one step of collection when debt becomes zero. 'pre'/'pos' ** allows some adjustments to be done only when needed. macro ** 'condchangemem' is used only for heavy tests (forcing a full ** GC cycle on every opportunity) */ + +#if !defined(HARDMEMTESTS) +#define condchangemem(L,pre,pos,emg) ((void)0) +#else +#define condchangemem(L,pre,pos,emg) \ + { if (gcrunning(G(L))) { pre; luaC_fullgc(L, emg); pos; } } +#endif + #define luaC_condGC(L,pre,pos) \ - { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ - condchangemem(L,pre,pos); } + { if (G(L)->GCdebt <= 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos,0); } /* more often than not, 'pre'/'pos' are empty */ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) @@ -188,10 +254,10 @@ LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); -LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); +LUAI_FUNC void luaC_runtilstate (lua_State *L, int state, int fast); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); -LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); -LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, size_t offset); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); diff --git a/lua/src/linit.c b/lua/src/linit.c index 69808f8..00d06f7 100644 --- a/lua/src/linit.c +++ b/lua/src/linit.c @@ -8,21 +8,6 @@ #define linit_c #define LUA_LIB -/* -** If you embed Lua in your program and need to open the standard -** libraries, call luaL_openlibs in your program. If you need a -** different set of libraries, copy this file to your project and edit -** it to suit your needs. -** -** You can also *preload* libraries, so that a later 'require' can -** open the library, which is already linked to the application. -** For that, do the following code: -** -** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); -** lua_pushcfunction(L, luaopen_modname); -** lua_setfield(L, -2, modname); -** lua_pop(L, 1); // remove PRELOAD table -*/ #include "lprefix.h" @@ -33,33 +18,46 @@ #include "lualib.h" #include "lauxlib.h" +#include "llimits.h" /* -** these libs are loaded by lua.c and are readily available to any Lua -** program +** Standard Libraries. (Must be listed in the same ORDER of their +** respective constants LUA_K.) */ -static const luaL_Reg loadedlibs[] = { +static const luaL_Reg stdlibs[] = { {LUA_GNAME, luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, - {LUA_TABLIBNAME, luaopen_table}, + {LUA_DBLIBNAME, luaopen_debug}, {LUA_IOLIBNAME, luaopen_io}, + {LUA_MATHLIBNAME, luaopen_math}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, - {LUA_MATHLIBNAME, luaopen_math}, + {LUA_TABLIBNAME, luaopen_table}, {LUA_UTF8LIBNAME, luaopen_utf8}, - {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; -LUALIB_API void luaL_openlibs (lua_State *L) { +/* +** require and preload selected standard libraries +*/ +LUALIB_API void luaL_openselectedlibs (lua_State *L, int load, int preload) { + int mask; const luaL_Reg *lib; - /* "require" functions from 'loadedlibs' and set results to global table */ - for (lib = loadedlibs; lib->func; lib++) { - luaL_requiref(L, lib->name, lib->func, 1); - lua_pop(L, 1); /* remove lib */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + for (lib = stdlibs, mask = 1; lib->name != NULL; lib++, mask <<= 1) { + if (load & mask) { /* selected? */ + luaL_requiref(L, lib->name, lib->func, 1); /* require library */ + lua_pop(L, 1); /* remove result from the stack */ + } + else if (preload & mask) { /* selected? */ + lua_pushcfunction(L, lib->func); + lua_setfield(L, -2, lib->name); /* add library to PRELOAD table */ + } } + lua_assert((mask >> 1) == LUA_UTF8LIBK); + lua_pop(L, 1); /* remove PRELOAD table */ } diff --git a/lua/src/liolib.c b/lua/src/liolib.c index c5075f3..57615e6 100644 --- a/lua/src/liolib.c +++ b/lua/src/liolib.c @@ -21,8 +21,7 @@ #include "lauxlib.h" #include "lualib.h" - - +#include "llimits.h" /* @@ -115,7 +114,7 @@ static int l_checkmode (const char *mode) { #if !defined(l_fseek) /* { */ -#if defined(LUA_USE_POSIX) /* { */ +#if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */ #include @@ -444,7 +443,7 @@ static int nextc (RN *rn) { return 0; /* fail */ } else { - rn->buff[rn->n++] = rn->c; /* save current char */ + rn->buff[rn->n++] = cast_char(rn->c); /* save current char */ rn->c = l_getc(rn->f); /* read next one */ return 1; } @@ -525,15 +524,15 @@ static int read_line (lua_State *L, FILE *f, int chop) { luaL_buffinit(L, &b); do { /* may need to read several chunks to get whole line */ char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ - int i = 0; + unsigned i = 0; l_lockfile(f); /* no memory errors can happen inside the lock */ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') - buff[i++] = c; /* read up to end of line or buffer limit */ + buff[i++] = cast_char(c); /* read up to end of line or buffer limit */ l_unlockfile(f); luaL_addsize(&b, i); } while (c != EOF && c != '\n'); /* repeat until end of line */ if (!chop && c == '\n') /* want a newline and have one? */ - luaL_addchar(&b, c); /* add ending newline to result */ + luaL_addchar(&b, '\n'); /* add ending newline to result */ luaL_pushresult(&b); /* close buffer */ /* return ok if read something (either a newline or something else) */ return (c == '\n' || lua_rawlen(L, -1) > 0); @@ -663,28 +662,28 @@ static int io_readline (lua_State *L) { static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; - int status = 1; + size_t totalbytes = 0; /* total number of bytes written */ errno = 0; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - int len = lua_isinteger(L, arg) - ? fprintf(f, LUA_INTEGER_FMT, - (LUAI_UACINT)lua_tointeger(L, arg)) - : fprintf(f, LUA_NUMBER_FMT, - (LUAI_UACNUMBER)lua_tonumber(L, arg)); - status = status && (len > 0); + for (; nargs--; arg++) { /* for each argument */ + char buff[LUA_N2SBUFFSZ]; + const char *s; + size_t numbytes; /* bytes written in one call to 'fwrite' */ + size_t len = lua_numbertocstring(L, arg, buff); /* try as a number */ + if (len > 0) { /* did conversion work (value was a number)? */ + s = buff; + len--; } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); + else /* must be a string */ + s = luaL_checklstring(L, arg, &len); + numbytes = fwrite(s, sizeof(char), len, f); + totalbytes += numbytes; + if (numbytes < len) { /* write error? */ + int n = luaL_fileresult(L, 0, NULL); + lua_pushinteger(L, cast_st2S(totalbytes)); + return n + 1; /* return fail, error msg., error code, and counter */ } } - if (l_likely(status)) - return 1; /* file handle already on stack top */ - else - return luaL_fileresult(L, status, NULL); + return 1; /* no errors; file handle already on stack top */ } @@ -733,18 +732,19 @@ static int f_setvbuf (lua_State *L) { } - -static int io_flush (lua_State *L) { - FILE *f = getiofile(L, IO_OUTPUT); +static int aux_flush (lua_State *L, FILE *f) { errno = 0; return luaL_fileresult(L, fflush(f) == 0, NULL); } static int f_flush (lua_State *L) { - FILE *f = tofile(L); - errno = 0; - return luaL_fileresult(L, fflush(f) == 0, NULL); + return aux_flush(L, tofile(L)); +} + + +static int io_flush (lua_State *L) { + return aux_flush(L, getiofile(L, IO_OUTPUT)); } diff --git a/lua/src/ljumptab.h b/lua/src/ljumptab.h index 8306f25..52fa6d7 100644 --- a/lua/src/ljumptab.h +++ b/lua/src/ljumptab.h @@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = { #if 0 ** you can update the following list with this command: ** -** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h +** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h ** #endif @@ -57,8 +57,8 @@ static const void *const disptab[NUM_OPCODES] = { &&L_OP_BANDK, &&L_OP_BORK, &&L_OP_BXORK, -&&L_OP_SHRI, &&L_OP_SHLI, +&&L_OP_SHRI, &&L_OP_ADD, &&L_OP_SUB, &&L_OP_MUL, @@ -106,6 +106,8 @@ static const void *const disptab[NUM_OPCODES] = { &&L_OP_SETLIST, &&L_OP_CLOSURE, &&L_OP_VARARG, +&&L_OP_GETVARG, +&&L_OP_ERRNNIL, &&L_OP_VARARGPREP, &&L_OP_EXTRAARG diff --git a/lua/src/llex.c b/lua/src/llex.c index 5fc39a5..f8bb3ea 100644 --- a/lua/src/llex.c +++ b/lua/src/llex.c @@ -32,6 +32,11 @@ #define next(ls) (ls->current = zgetc(ls->z)) +/* minimum size for string buffer */ +#if !defined(LUA_MINBUFFER) +#define LUA_MINBUFFER 32 +#endif + #define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') @@ -39,7 +44,7 @@ /* ORDER RESERVED */ static const char *const luaX_tokens [] = { "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", "if", + "end", "false", "for", "function", "global", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "//", "..", "...", "==", ">=", "<=", "~=", @@ -57,10 +62,10 @@ static l_noret lexerror (LexState *ls, const char *msg, int token); static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { - size_t newsize; - if (luaZ_sizebuffer(b) >= MAX_SIZE/2) + size_t newsize = luaZ_sizebuffer(b); /* get old size */; + if (newsize >= (MAX_SIZE/3 * 2)) /* larger than MAX_SIZE/1.5 ? */ lexerror(ls, "lexical element too long", 0); - newsize = luaZ_sizebuffer(b) * 2; + newsize += (newsize >> 1); /* new size is 1.5 times the old one */ luaZ_resizebuffer(ls->L, b, newsize); } b->buffer[luaZ_bufflen(b)++] = cast_char(c); @@ -122,30 +127,34 @@ l_noret luaX_syntaxerror (LexState *ls, const char *msg) { /* -** Creates a new string and anchors it in scanner's table so that it -** will not be collected until the end of the compilation; by that time -** it should be anchored somewhere. It also internalizes long strings, -** ensuring there is only one copy of each unique string. The table -** here is used as a set: the string enters as the key, while its value -** is irrelevant. We use the string itself as the value only because it -** is a TValue readily available. Later, the code generation can change -** this value. +** Anchors a string in scanner's table so that it will not be collected +** until the end of the compilation; by that time it should be anchored +** somewhere. It also internalizes long strings, ensuring there is only +** one copy of each unique string. */ -TString *luaX_newstring (LexState *ls, const char *str, size_t l) { +static TString *anchorstr (LexState *ls, TString *ts) { lua_State *L = ls->L; - TString *ts = luaS_newlstr(L, str, l); /* create new string */ - const TValue *o = luaH_getstr(ls->h, ts); - if (!ttisnil(o)) /* string already present? */ - ts = keystrval(nodefromval(o)); /* get saved copy */ - else { /* not in use yet */ + TValue oldts; + int tag = luaH_getstr(ls->h, ts, &oldts); + if (!tagisempty(tag)) /* string already present? */ + return tsvalue(&oldts); /* use stored value */ + else { /* create a new entry */ TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ - setsvalue(L, stv, ts); /* temporarily anchor the string */ - luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ + setsvalue(L, stv, ts); /* push (anchor) the string on the stack */ + luaH_set(L, ls->h, stv, stv); /* t[string] = string */ /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); L->top.p--; /* remove string from stack */ + return ts; } - return ts; +} + + +/* +** Creates a new string and anchors it in scanner's table. +*/ +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + return anchorstr(ls, luaS_newlstr(ls->L, str, l)); } @@ -159,7 +168,7 @@ static void inclinenumber (LexState *ls) { next(ls); /* skip '\n' or '\r' */ if (currIsNewline(ls) && ls->current != old) next(ls); /* skip '\n\r' or '\r\n' */ - if (++ls->linenumber >= MAX_INT) + if (++ls->linenumber >= INT_MAX) lexerror(ls, "chunk has too many lines", 0); } @@ -175,7 +184,15 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, ls->linenumber = 1; ls->lastline = 1; ls->source = source; - ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ + /* all three strings here ("_ENV", "break", "global") were fixed, + so they cannot be collected */ + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */ + ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */ +#if defined(LUA_COMPAT_GLOBAL) + /* compatibility mode: "global" is not a reserved word */ + ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */ + ls->glbn->extra = 0; /* mark it as not reserved */ +#endif luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } @@ -340,12 +357,17 @@ static int readhexaesc (LexState *ls) { } -static unsigned long readutf8esc (LexState *ls) { - unsigned long r; - int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ +/* +** When reading a UTF-8 escape sequence, save everything to the buffer +** for error reporting in case of errors; 'i' counts the number of +** saved characters, so that they can be removed if case of success. +*/ +static l_uint32 readutf8esc (LexState *ls) { + l_uint32 r; + int i = 4; /* number of chars to be removed: start with #"\u{X" */ save_and_next(ls); /* skip 'u' */ esccheck(ls, ls->current == '{', "missing '{'"); - r = gethexa(ls); /* must have at least one digit */ + r = cast_uint(gethexa(ls)); /* must have at least one digit */ while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { i++; esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); @@ -542,12 +564,13 @@ static int llex (LexState *ls, SemInfo *seminfo) { do { save_and_next(ls); } while (lislalnum(ls->current)); - ts = luaX_newstring(ls, luaZ_buffer(ls->buff), - luaZ_bufflen(ls->buff)); - seminfo->ts = ts; - if (isreserved(ts)) /* reserved word? */ + /* find or create string */ + ts = luaS_newlstr(ls->L, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + if (isreserved(ts)) /* reserved word? */ return ts->extra - 1 + FIRST_RESERVED; else { + seminfo->ts = anchorstr(ls, ts); return TK_NAME; } } diff --git a/lua/src/llex.h b/lua/src/llex.h index 389d2f8..37016e8 100644 --- a/lua/src/llex.h +++ b/lua/src/llex.h @@ -33,8 +33,8 @@ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, - TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, - TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, + TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_SHL, TK_SHR, @@ -59,7 +59,7 @@ typedef struct Token { } Token; -/* state of the lexer plus state of the parser when shared by all +/* state of the scanner plus state of the parser when shared by all functions */ typedef struct LexState { int current; /* current character (charint) */ @@ -75,6 +75,8 @@ typedef struct LexState { struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ + TString *brkn; /* "break" name (used as a label) */ + TString *glbn; /* "global" name (when not a reserved word) */ } LexState; diff --git a/lua/src/llimits.h b/lua/src/llimits.h index 1c826f7..fc5cb27 100644 --- a/lua/src/llimits.h +++ b/lua/src/llimits.h @@ -15,50 +15,49 @@ #include "lua.h" +#define l_numbits(t) cast_int(sizeof(t) * CHAR_BIT) + /* -** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count -** the total memory used by Lua (in bytes). Usually, 'size_t' and +** 'l_mem' is a signed integer big enough to count the total memory +** used by Lua. (It is signed due to the use of debt in several +** computations.) 'lu_mem' is a corresponding unsigned type. Usually, ** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. */ #if defined(LUAI_MEM) /* { external definitions? */ -typedef LUAI_UMEM lu_mem; typedef LUAI_MEM l_mem; +typedef LUAI_UMEM lu_mem; #elif LUAI_IS32INT /* }{ */ -typedef size_t lu_mem; typedef ptrdiff_t l_mem; +typedef size_t lu_mem; #else /* 16-bit ints */ /* }{ */ -typedef unsigned long lu_mem; typedef long l_mem; +typedef unsigned long lu_mem; #endif /* } */ +#define MAX_LMEM \ + cast(l_mem, (cast(lu_mem, 1) << (l_numbits(l_mem) - 1)) - 1) + /* chars used as small naturals (so that 'char' is reserved for characters) */ typedef unsigned char lu_byte; typedef signed char ls_byte; -/* maximum value for size_t */ -#define MAX_SIZET ((size_t)(~(size_t)0)) - -/* maximum size visible for Lua (must be representable in a lua_Integer) */ -#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ - : (size_t)(LUA_MAXINTEGER)) - - -#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) - -#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) +/* Type for thread status/error codes */ +typedef lu_byte TStatus; +/* The C API still uses 'int' for status/error codes */ +#define APIstatus(st) cast_int(st) -#define MAX_INT INT_MAX /* maximum value of an int */ - +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) /* -** floor of the log2 of the maximum signed value for integral type 't'. -** (That is, maximum 'n' such that '2^n' fits in the given signed type.) +** Maximum size for strings and userdata visible for Lua; should be +** representable as a lua_Integer and as a size_t. */ -#define log2maxs(t) (sizeof(t) * 8 - 2) - +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : cast_sizet(LUA_MAXINTEGER)) /* ** test whether an unsigned value is a power of 2 (or zero) @@ -88,7 +87,7 @@ typedef signed char ls_byte; #define L_P2I size_t #endif -#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX)) +#define point2uint(p) cast_uint((L_P2I)(p) & UINT_MAX) @@ -104,26 +103,18 @@ typedef LUAI_UACINT l_uacInt; #undef NDEBUG #include #define lua_assert(c) assert(c) +#define assert_code(c) c #endif #if defined(lua_assert) -#define check_exp(c,e) (lua_assert(c), (e)) -/* to avoid problems with conditions too long */ -#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else #define lua_assert(c) ((void)0) -#define check_exp(c,e) (e) -#define lua_longassert(c) ((void)0) +#define assert_code(c) ((void)0) #endif -/* -** assertion for checking API calls -*/ -#if !defined(luai_apicheck) -#define luai_apicheck(l,e) ((void)l, lua_assert(e)) -#endif - -#define api_check(l,e,msg) luai_apicheck(l,(e) && msg) +#define check_exp(c,e) (lua_assert(c), (e)) +/* to avoid problems with conditions too long */ +#define lua_longassert(c) assert_code((c) ? (void)0 : lua_assert(0)) /* macro to avoid warnings about unused variables */ @@ -139,12 +130,15 @@ typedef LUAI_UACINT l_uacInt; #define cast_voidp(i) cast(void *, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) +#define cast_short(i) cast(short, (i)) #define cast_uint(i) cast(unsigned int, (i)) #define cast_byte(i) cast(lu_byte, (i)) #define cast_uchar(i) cast(unsigned char, (i)) #define cast_char(i) cast(char, (i)) #define cast_charp(i) cast(char *, (i)) #define cast_sizet(i) cast(size_t, (i)) +#define cast_Integer(i) cast(lua_Integer, (i)) +#define cast_Inst(i) cast(Instruction, (i)) /* cast a signed lua_Integer to lua_Unsigned */ @@ -161,6 +155,38 @@ typedef LUAI_UACINT l_uacInt; #define l_castU2S(i) ((lua_Integer)(i)) #endif +/* +** cast a size_t to lua_Integer: These casts are always valid for +** sizes of Lua objects (see MAX_SIZE) +*/ +#define cast_st2S(sz) ((lua_Integer)(sz)) + +/* Cast a ptrdiff_t to size_t, when it is known that the minuend +** comes from the subtrahend (the base) +*/ +#define ct_diff2sz(df) ((size_t)(df)) + +/* ptrdiff_t to lua_Integer */ +#define ct_diff2S(df) cast_st2S(ct_diff2sz(df)) + +/* +** Special type equivalent to '(void*)' for functions (to suppress some +** warnings when converting function pointers) +*/ +typedef void (*voidf)(void); + +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (voidf)(p)) +#else +#define cast_func(p) ((voidf)(p)) +#endif + + /* ** non-return type @@ -193,8 +219,7 @@ typedef LUAI_UACINT l_uacInt; /* -** type for virtual-machine instructions; -** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +** An unsigned with (at least) 4 bytes */ #if LUAI_IS32INT typedef unsigned int l_uint32; @@ -202,107 +227,6 @@ typedef unsigned int l_uint32; typedef unsigned long l_uint32; #endif -typedef l_uint32 Instruction; - - - -/* -** Maximum length for short strings, that is, strings that are -** internalized. (Cannot be smaller than reserved words or tags for -** metamethods, as these strings must be internalized; -** #("function") = 8, #("__newindex") = 10.) -*/ -#if !defined(LUAI_MAXSHORTLEN) -#define LUAI_MAXSHORTLEN 40 -#endif - - -/* -** Initial size for the string table (must be power of 2). -** The Lua core alone registers ~50 strings (reserved words + -** metaevent keys + a few others). Libraries would typically add -** a few dozens more. -*/ -#if !defined(MINSTRTABSIZE) -#define MINSTRTABSIZE 128 -#endif - - -/* -** Size of cache for strings in the API. 'N' is the number of -** sets (better be a prime) and "M" is the size of each set (M == 1 -** makes a direct cache.) -*/ -#if !defined(STRCACHE_N) -#define STRCACHE_N 53 -#define STRCACHE_M 2 -#endif - - -/* minimum size for string buffer */ -#if !defined(LUA_MINBUFFER) -#define LUA_MINBUFFER 32 -#endif - - -/* -** Maximum depth for nested C calls, syntactical nested non-terminals, -** and other features implemented through recursion in C. (Value must -** fit in a 16-bit unsigned integer. It must also be compatible with -** the size of the C stack.) -*/ -#if !defined(LUAI_MAXCCALLS) -#define LUAI_MAXCCALLS 200 -#endif - - -/* -** macros that are executed whenever program enters the Lua core -** ('lua_lock') and leaves the core ('lua_unlock') -*/ -#if !defined(lua_lock) -#define lua_lock(L) ((void) 0) -#define lua_unlock(L) ((void) 0) -#endif - -/* -** macro executed during Lua functions at points where the -** function can yield. -*/ -#if !defined(luai_threadyield) -#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} -#endif - - -/* -** these macros allow user-specific actions when a thread is -** created/deleted/resumed/yielded. -*/ -#if !defined(luai_userstateopen) -#define luai_userstateopen(L) ((void)L) -#endif - -#if !defined(luai_userstateclose) -#define luai_userstateclose(L) ((void)L) -#endif - -#if !defined(luai_userstatethread) -#define luai_userstatethread(L,L1) ((void)L) -#endif - -#if !defined(luai_userstatefree) -#define luai_userstatefree(L,L1) ((void)L) -#endif - -#if !defined(luai_userstateresume) -#define luai_userstateresume(L,n) ((void)L) -#endif - -#if !defined(luai_userstateyield) -#define luai_userstateyield(L,n) ((void)L) -#endif - - /* ** The luai_num* macros define the primitive operations over numbers. @@ -357,24 +281,77 @@ typedef l_uint32 Instruction; +/* +** lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if the float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + /* -** macro to control inclusion of some hard tests on stack reallocation +** LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +** LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** Elf and MACH/gcc (versions 3.2 and later) mark them as "hidden" to +** optimize access when Lua is compiled as a shared library. Not all elf +** targets support this attribute. Unfortunately, gcc does not offer +** a way to check whether the target offers that support, and those +** without support give a warning about it. To avoid these warnings, +** change to the default definition. */ -#if !defined(HARDSTACKTESTS) -#define condmovestack(L,pre,pos) ((void)0) +#if !defined(LUAI_FUNC) + +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + (defined(__ELF__) || defined(__MACH__)) +#define LUAI_FUNC __attribute__((visibility("internal"))) extern #else -/* realloc stack keeping its size */ -#define condmovestack(L,pre,pos) \ - { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } +#define LUAI_FUNC extern #endif -#if !defined(HARDMEMTESTS) -#define condchangemem(L,pre,pos) ((void)0) -#else -#define condchangemem(L,pre,pos) \ - { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } } +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ + +#endif + + +/* Give these macros simpler names for internal use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) #endif +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) #endif + +/* }================================================================== */ + +#endif + diff --git a/lua/src/lmathlib.c b/lua/src/lmathlib.c index 4381063..a6b13f9 100644 --- a/lua/src/lmathlib.c +++ b/lua/src/lmathlib.c @@ -20,6 +20,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" #undef PI @@ -37,31 +38,37 @@ static int math_abs (lua_State *L) { return 1; } + static int math_sin (lua_State *L) { lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); return 1; } + static int math_cos (lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } + static int math_tan (lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } + static int math_asin (lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; } + static int math_acos (lua_State *L) { lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); return 1; } + static int math_atan (lua_State *L) { lua_Number y = luaL_checknumber(L, 1); lua_Number x = luaL_optnumber(L, 2, 1); @@ -105,7 +112,7 @@ static int math_floor (lua_State *L) { static int math_ceil (lua_State *L) { if (lua_isinteger(L, 1)) - lua_settop(L, 1); /* integer is its own ceil */ + lua_settop(L, 1); /* integer is its own ceiling */ else { lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); pushnumint(L, d); @@ -166,6 +173,7 @@ static int math_ult (lua_State *L) { return 1; } + static int math_log (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number res; @@ -187,22 +195,42 @@ static int math_log (lua_State *L) { return 1; } + static int math_exp (lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } + static int math_deg (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } + static int math_rad (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); return 1; } +static int math_frexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep; + lua_pushnumber(L, l_mathop(frexp)(x, &ep)); + lua_pushinteger(L, ep); + return 2; +} + + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + + static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imin = 1; /* index of current minimum value */ @@ -250,7 +278,7 @@ static int math_type (lua_State *L) { */ /* -** This code uses lots of shifts. ANSI C does not allow shifts greater +** This code uses lots of shifts. ISO C does not allow shifts greater ** than or equal to the width of the type being shifted, so some shifts ** are written in convoluted ways to match that restriction. For ** preprocessor tests, it assumes a width of 32 bits, so the maximum @@ -366,25 +394,17 @@ static lua_Number I2d (Rand64 x) { #else /* no 'Rand64' }{ */ -/* get an integer with at least 32 bits */ -#if LUAI_IS32INT -typedef unsigned int lu_int32; -#else -typedef unsigned long lu_int32; -#endif - - /* ** Use two 32-bit integers to represent a 64-bit quantity. */ typedef struct Rand64 { - lu_int32 h; /* higher half */ - lu_int32 l; /* lower half */ + l_uint32 h; /* higher half */ + l_uint32 l; /* lower half */ } Rand64; /* -** If 'lu_int32' has more than 32 bits, the extra bits do not interfere +** If 'l_uint32' has more than 32 bits, the extra bits do not interfere ** with the 32 initial bits, except in a right shift and comparisons. ** Moreover, the final result has to discard the extra bits. */ @@ -398,7 +418,7 @@ typedef struct Rand64 { */ /* build a new Rand64 value */ -static Rand64 packI (lu_int32 h, lu_int32 l) { +static Rand64 packI (l_uint32 h, l_uint32 l) { Rand64 result; result.h = h; result.l = l; @@ -471,7 +491,7 @@ static Rand64 nextrand (Rand64 *state) { */ /* an unsigned 1 with proper type */ -#define UONE ((lu_int32)1) +#define UONE ((l_uint32)1) #if FIGS <= 32 @@ -522,7 +542,7 @@ static lua_Unsigned I2UInt (Rand64 x) { /* convert a 'lua_Unsigned' to a 'Rand64' */ static Rand64 Int2I (lua_Unsigned n) { - return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n); + return packI((l_uint32)((n >> 31) >> 1), (l_uint32)n); } #endif /* } */ @@ -540,7 +560,7 @@ typedef struct { ** Project the random integer 'ran' into the interval [0, n]. ** Because 'ran' has 2^B possible values, the projection can only be ** uniform when the size of the interval is a power of 2 (exact -** division). Otherwise, to get a uniform projection into [0, n], we +** division). So, to get a uniform projection into [0, n], we ** first compute 'lim', the smallest Mersenne number not smaller than ** 'n'. We then project 'ran' into the interval [0, lim]. If the result ** is inside [0, n], we are done. Otherwise, we try with another 'ran', @@ -548,26 +568,14 @@ typedef struct { */ static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, RanState *state) { - if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ - return ran & n; /* no bias */ - else { - lua_Unsigned lim = n; - /* compute the smallest (2^b - 1) not smaller than 'n' */ - lim |= (lim >> 1); - lim |= (lim >> 2); - lim |= (lim >> 4); - lim |= (lim >> 8); - lim |= (lim >> 16); -#if (LUA_MAXUNSIGNED >> 31) >= 3 - lim |= (lim >> 32); /* integer type has more than 32 bits */ -#endif - lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ - && lim >= n /* not smaller than 'n', */ - && (lim >> 1) < n); /* and it is the smallest one */ - while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ - ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ - return ran; - } + lua_Unsigned lim = n; /* to compute the Mersenne number */ + int sh; /* how much to spread bits to the right in 'lim' */ + /* spread '1' bits in 'lim' until it becomes a Mersenne number */ + for (sh = 1; (lim & (lim + 1)) != 0; sh *= 2) + lim |= (lim >> sh); /* spread '1's to the right */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] and test */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; } @@ -585,7 +593,7 @@ static int math_random (lua_State *L) { low = 1; up = luaL_checkinteger(L, 1); if (up == 0) { /* single 0 as argument? */ - lua_pushinteger(L, I2UInt(rv)); /* full random integer */ + lua_pushinteger(L, l_castU2S(I2UInt(rv))); /* full random integer */ return 1; } break; @@ -600,8 +608,8 @@ static int math_random (lua_State *L) { /* random integer in the interval [low, up] */ luaL_argcheck(L, low <= up, 1, "interval is empty"); /* project random integer into the interval [0, up - low] */ - p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); - lua_pushinteger(L, p + (lua_Unsigned)low); + p = project(I2UInt(rv), l_castS2U(up) - l_castS2U(low), state); + lua_pushinteger(L, l_castU2S(p + l_castS2U(low))); return 1; } @@ -615,33 +623,23 @@ static void setseed (lua_State *L, Rand64 *state, state[3] = Int2I(0); for (i = 0; i < 16; i++) nextrand(state); /* discard initial values to "spread" seed */ - lua_pushinteger(L, n1); - lua_pushinteger(L, n2); -} - - -/* -** Set a "random" seed. To get some randomness, use the current time -** and the address of 'L' (in case the machine does address space layout -** randomization). -*/ -static void randseed (lua_State *L, RanState *state) { - lua_Unsigned seed1 = (lua_Unsigned)time(NULL); - lua_Unsigned seed2 = (lua_Unsigned)(size_t)L; - setseed(L, state->s, seed1, seed2); + lua_pushinteger(L, l_castU2S(n1)); + lua_pushinteger(L, l_castU2S(n2)); } static int math_randomseed (lua_State *L) { RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + lua_Unsigned n1, n2; if (lua_isnone(L, 1)) { - randseed(L, state); + n1 = luaL_makeseed(L); /* "random" seed */ + n2 = I2UInt(nextrand(state->s)); /* in case seed is not that random... */ } else { - lua_Integer n1 = luaL_checkinteger(L, 1); - lua_Integer n2 = luaL_optinteger(L, 2, 0); - setseed(L, state->s, n1, n2); + n1 = l_castS2U(luaL_checkinteger(L, 1)); + n2 = l_castS2U(luaL_optinteger(L, 2, 0)); } + setseed(L, state->s, n1, n2); return 2; /* return seeds */ } @@ -658,7 +656,7 @@ static const luaL_Reg randfuncs[] = { */ static void setrandfunc (lua_State *L) { RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); - randseed(L, state); /* initialize with a "random" seed */ + setseed(L, state->s, luaL_makeseed(L), 0); /* initialize with random seed */ lua_pop(L, 2); /* remove pushed seeds */ luaL_setfuncs(L, randfuncs, 1); } @@ -695,20 +693,6 @@ static int math_pow (lua_State *L) { return 1; } -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); - lua_pushinteger(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_Number x = luaL_checknumber(L, 1); - int ep = (int)luaL_checkinteger(L, 2); - lua_pushnumber(L, l_mathop(ldexp)(x, ep)); - return 1; -} - static int math_log10 (lua_State *L) { lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); return 1; @@ -731,7 +715,9 @@ static const luaL_Reg mathlib[] = { {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, + {"frexp", math_frexp}, {"ult", math_ult}, + {"ldexp", math_ldexp}, {"log", math_log}, {"max", math_max}, {"min", math_min}, @@ -747,8 +733,6 @@ static const luaL_Reg mathlib[] = { {"sinh", math_sinh}, {"tanh", math_tanh}, {"pow", math_pow}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, {"log10", math_log10}, #endif /* placeholders */ diff --git a/lua/src/lmem.c b/lua/src/lmem.c index 9800a86..de8503d 100644 --- a/lua/src/lmem.c +++ b/lua/src/lmem.c @@ -95,7 +95,7 @@ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, - int size_elems, int limit, const char *what) { + unsigned size_elems, int limit, const char *what) { void *newblock; int size = *psize; if (nelems + 1 <= size) /* does one extra element still fit? */ @@ -126,10 +126,10 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, ** error. */ void *luaM_shrinkvector_ (lua_State *L, void *block, int *size, - int final_n, int size_elem) { + int final_n, unsigned size_elem) { void *newblock; - size_t oldsize = cast_sizet((*size) * size_elem); - size_t newsize = cast_sizet(final_n * size_elem); + size_t oldsize = cast_sizet(*size) * size_elem; + size_t newsize = cast_sizet(final_n) * size_elem; lua_assert(newsize <= oldsize); newblock = luaM_saferealloc_(L, block, oldsize, newsize); *size = final_n; @@ -151,7 +151,7 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); callfrealloc(g, block, osize, 0); - g->GCdebt -= osize; + g->GCdebt += cast(l_mem, osize); } @@ -184,7 +184,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { return NULL; /* do not update 'GCdebt' */ } lua_assert((nsize == 0) == (newblock == NULL)); - g->GCdebt = (g->GCdebt + nsize) - osize; + g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize); return newblock; } @@ -203,13 +203,13 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) { return NULL; /* that's all */ else { global_State *g = G(L); - void *newblock = firsttry(g, NULL, tag, size); + void *newblock = firsttry(g, NULL, cast_sizet(tag), size); if (l_unlikely(newblock == NULL)) { - newblock = tryagain(L, NULL, tag, size); + newblock = tryagain(L, NULL, cast_sizet(tag), size); if (newblock == NULL) luaM_error(L); } - g->GCdebt += size; + g->GCdebt -= cast(l_mem, size); return newblock; } } diff --git a/lua/src/lmem.h b/lua/src/lmem.h index 8c75a44..dc714fb 100644 --- a/lua/src/lmem.h +++ b/lua/src/lmem.h @@ -39,11 +39,11 @@ ** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that ** the result is not larger than 'n' and cannot overflow a 'size_t' ** when multiplied by the size of type 't'. (Assumes that 'n' is an -** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) +** 'int' and that 'int' is not larger than 'size_t'.) */ #define luaM_limitN(n,t) \ ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ - cast_uint((MAX_SIZET/sizeof(t)))) + cast_int((MAX_SIZET/sizeof(t)))) /* @@ -57,12 +57,15 @@ #define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) #define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) -#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) +#define luaM_newvector(L,n,t) \ + cast(t*, luaM_malloc_(L, cast_sizet(n)*sizeof(t), 0)) #define luaM_newvectorchecked(L,n,t) \ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) +#define luaM_newblock(L, size) luaM_newvector(L, size, char) + #define luaM_growvector(L,v,nelems,size,t,limit,e) \ ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ luaM_limitN(limit,t),e))) @@ -83,10 +86,10 @@ LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, size_t size); LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, - int *size, int size_elem, int limit, + int *size, unsigned size_elem, int limit, const char *what); LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, - int final_n, int size_elem); + int final_n, unsigned size_elem); LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); #endif diff --git a/lua/src/loadlib.c b/lua/src/loadlib.c index 6d289fc..8d2e68e 100644 --- a/lua/src/loadlib.c +++ b/lua/src/loadlib.c @@ -22,6 +22,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" /* @@ -58,11 +59,8 @@ static const char *const CLIBS = "_CLIBS"; #define setprogdir(L) ((void)0) -/* -** Special type equivalent to '(void*)' for functions in gcc -** (to suppress warnings when converting function pointers) -*/ -typedef void (*voidf)(void); +/* cast void* to a Lua function */ +#define cast_Lfunc(p) cast(lua_CFunction, cast_func(p)) /* @@ -95,26 +93,13 @@ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); #if defined(LUA_USE_DLOPEN) /* { */ /* ** {======================================================================== -** This is an implementation of loadlib based on the dlfcn interface. -** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -** as an emulation layer on top of native functions. +** This is an implementation of loadlib based on the dlfcn interface, +** which is available in all POSIX systems. ** ========================================================================= */ #include -/* -** Macro to convert pointer-to-void* to pointer-to-function. This cast -** is undefined according to ISO C, but POSIX assumes that it works. -** (The '__extension__' in gnu compilers is only to avoid warnings.) -*/ -#if defined(__GNUC__) -#define cast_func(p) (__extension__ (lua_CFunction)(p)) -#else -#define cast_func(p) ((lua_CFunction)(p)) -#endif - static void lsys_unloadlib (void *lib) { dlclose(lib); @@ -130,7 +115,7 @@ static void *lsys_load (lua_State *L, const char *path, int seeglb) { static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { - lua_CFunction f = cast_func(dlsym(lib, sym)); + lua_CFunction f = cast_Lfunc(dlsym(lib, sym)); if (l_unlikely(f == NULL)) lua_pushstring(L, dlerror()); return f; @@ -206,7 +191,7 @@ static void *lsys_load (lua_State *L, const char *path, int seeglb) { static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { - lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym); + lua_CFunction f = cast_Lfunc(GetProcAddress((HMODULE)lib, sym)); if (f == NULL) pusherror(L); return f; } @@ -283,7 +268,8 @@ static int noenv (lua_State *L) { /* -** Set a path +** Set a path. (If using the default path, assume it is a string +** literal in C and create it as an external string.) */ static void setpath (lua_State *L, const char *fieldname, const char *envname, @@ -294,7 +280,7 @@ static void setpath (lua_State *L, const char *fieldname, if (path == NULL) /* no versioned environment variable? */ path = getenv(envname); /* try unversioned name */ if (path == NULL || noenv(L)) /* no environment variable? */ - lua_pushstring(L, dft); /* use default */ + lua_pushexternalstring(L, dft, strlen(dft), NULL, NULL); /* use default */ else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) lua_pushstring(L, path); /* nothing to change */ else { /* path contains a ";;": insert default path in its place */ @@ -302,13 +288,13 @@ static void setpath (lua_State *L, const char *fieldname, luaL_Buffer b; luaL_buffinit(L, &b); if (path < dftmark) { /* is there a prefix before ';;'? */ - luaL_addlstring(&b, path, dftmark - path); /* add it */ + luaL_addlstring(&b, path, ct_diff2sz(dftmark - path)); /* add it */ luaL_addchar(&b, *LUA_PATH_SEP); } luaL_addstring(&b, dft); /* add default */ if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ luaL_addchar(&b, *LUA_PATH_SEP); - luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark); + luaL_addlstring(&b, dftmark + 2, ct_diff2sz((path + len - 2) - dftmark)); } luaL_pushresult(&b); } @@ -320,6 +306,16 @@ static void setpath (lua_State *L, const char *fieldname, /* }================================================================== */ +/* +** External strings created by DLLs may need the DLL code to be +** deallocated. This implies that a DLL can only be unloaded after all +** its strings were deallocated. To ensure that, we create a 'library +** string' to represent each DLL, and when this string is deallocated +** it closes its corresponding DLL. +** (The string itself is irrelevant; its userdata is the DLL pointer.) +*/ + + /* ** return registry.CLIBS[path] */ @@ -334,34 +330,41 @@ static void *checkclib (lua_State *L, const char *path) { /* -** registry.CLIBS[path] = plib -- for queries -** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries +** Deallocate function for library strings. +** Unload the DLL associated with the string being deallocated. */ -static void addtoclib (lua_State *L, const char *path, void *plib) { - lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); - lua_pushlightuserdata(L, plib); - lua_pushvalue(L, -1); - lua_setfield(L, -3, path); /* CLIBS[path] = plib */ - lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ - lua_pop(L, 1); /* pop CLIBS table */ +static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) { + /* string itself is irrelevant and static */ + (void)ptr; (void)osize; (void)nsize; + lsys_unloadlib(ud); /* unload library represented by the string */ + return NULL; } /* -** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib -** handles in list CLIBS +** Create a library string that, when deallocated, will unload 'plib' */ -static int gctm (lua_State *L) { - lua_Integer n = luaL_len(L, 1); - for (; n >= 1; n--) { /* for each handle, in reverse order */ - lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ - lsys_unloadlib(lua_touserdata(L, -1)); - lua_pop(L, 1); /* pop handle */ - } - return 0; +static void createlibstr (lua_State *L, void *plib) { + /* common content for all library strings */ + static const char dummy[] = "01234567890"; + lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib); } +/* +** registry.CLIBS[path] = plib -- for queries. +** Also create a reference to strlib, so that the library string will +** only be collected when registry.CLIBS is collected. +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_pushlightuserdata(L, plib); + lua_setfield(L, -2, path); /* CLIBS[path] = plib */ + createlibstr(L, plib); + luaL_ref(L, -2); /* keep library string in CLIBS */ + lua_pop(L, 1); /* pop CLIBS table */ +} + /* error codes for 'lookforfunc' */ #define ERRLIB 1 @@ -375,8 +378,8 @@ static int gctm (lua_State *L) { ** Then, if 'sym' is '*', return true (as library has been loaded). ** Otherwise, look for symbol 'sym' in the library and push a ** C function with that symbol. -** Return 0 and 'true' or a function in the stack; in case of -** errors, return an error code and an error message in the stack. +** Return 0 with 'true' or a function in the stack; in case of +** errors, return an error code with an error message in the stack. */ static int lookforfunc (lua_State *L, const char *path, const char *sym) { void *reg = checkclib(L, path); /* check loaded C libraries */ @@ -557,7 +560,7 @@ static int loadfunc (lua_State *L, const char *filename, const char *modname) { mark = strchr(modname, *LUA_IGMARK); if (mark) { int stat; - openfunc = lua_pushlstring(L, modname, mark - modname); + openfunc = lua_pushlstring(L, modname, ct_diff2sz(mark - modname)); openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); stat = lookforfunc(L, filename, openfunc); if (stat != ERRFUNC) return stat; @@ -582,7 +585,7 @@ static int searcher_Croot (lua_State *L) { const char *p = strchr(name, '.'); int stat; if (p == NULL) return 0; /* is root */ - lua_pushlstring(L, name, p - name); + lua_pushlstring(L, name, ct_diff2sz(p - name)); filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); if (filename == NULL) return 1; /* root not found */ if ((stat = loadfunc(L, filename, name)) != 0) { @@ -620,12 +623,12 @@ static void findloader (lua_State *L, const char *name) { != LUA_TTABLE)) luaL_error(L, "'package.searchers' must be a table"); luaL_buffinit(L, &msg); + luaL_addstring(&msg, "\n\t"); /* error-message prefix for first message */ /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { - luaL_addstring(&msg, "\n\t"); /* error-message prefix */ if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ - luaL_buffsub(&msg, 2); /* remove prefix */ + luaL_buffsub(&msg, 2); /* remove last prefix */ luaL_pushresult(&msg); /* create error message */ luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); } @@ -636,11 +639,10 @@ static void findloader (lua_State *L, const char *name) { else if (lua_isstring(L, -2)) { /* searcher returned error message? */ lua_pop(L, 1); /* remove extra return */ luaL_addvalue(&msg); /* concatenate error message */ + luaL_addstring(&msg, "\n\t"); /* prefix for next message */ } - else { /* no error message */ + else /* no error message */ lua_pop(L, 2); /* remove both returns */ - luaL_buffsub(&msg, 2); /* remove prefix */ - } } } @@ -719,21 +721,9 @@ static void createsearcherstable (lua_State *L) { } -/* -** create table CLIBS to keep track of loaded C libraries, -** setting a finalizer to close all libraries when closing state. -*/ -static void createclibstable (lua_State *L) { - luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ - lua_createtable(L, 0, 1); /* create metatable for CLIBS */ - lua_pushcfunction(L, gctm); - lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ - lua_setmetatable(L, -2); -} - - LUAMOD_API int luaopen_package (lua_State *L) { - createclibstable(L); + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_pop(L, 1); /* will not use it now */ luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); /* set paths */ diff --git a/lua/src/lobject.c b/lua/src/lobject.c index 9cfa522..763b484 100644 --- a/lua/src/lobject.c +++ b/lua/src/lobject.c @@ -10,6 +10,7 @@ #include "lprefix.h" +#include #include #include #include @@ -30,10 +31,11 @@ /* -** Computes ceil(log2(x)) +** Computes ceil(log2(x)), which is the smallest integer n such that +** x <= (1 << n). */ -int luaO_ceillog2 (unsigned int x) { - static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ +lu_byte luaO_ceillog2 (unsigned int x) { + static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, @@ -46,7 +48,67 @@ int luaO_ceillog2 (unsigned int x) { int l = 0; x--; while (x >= 256) { l += 8; x >>= 8; } - return l + log_2[x]; + return cast_byte(l + log_2[x]); +} + +/* +** Encodes 'p'% as a floating-point byte, represented as (eeeexxxx). +** The exponent is represented using excess-7. Mimicking IEEE 754, the +** representation normalizes the number when possible, assuming an extra +** 1 before the mantissa (xxxx) and adding one to the exponent (eeee) +** to signal that. So, the real value is (1xxxx) * 2^(eeee - 7 - 1) if +** eeee != 0, and (xxxx) * 2^-7 otherwise (subnormal numbers). +*/ +lu_byte luaO_codeparam (unsigned int p) { + if (p >= (cast(lu_mem, 0x1F) << (0xF - 7 - 1)) * 100u) /* overflow? */ + return 0xFF; /* return maximum value */ + else { + p = (cast(l_uint32, p) * 128 + 99) / 100; /* round up the division */ + if (p < 0x10) { /* subnormal number? */ + /* exponent bits are already zero; nothing else to do */ + return cast_byte(p); + } + else { /* p >= 0x10 implies ceil(log2(p + 1)) >= 5 */ + /* preserve 5 bits in 'p' */ + unsigned log = luaO_ceillog2(p + 1) - 5u; + return cast_byte(((p >> log) - 0x10) | ((log + 1) << 4)); + } + } +} + + +/* +** Computes 'p' times 'x', where 'p' is a floating-point byte. Roughly, +** we have to multiply 'x' by the mantissa and then shift accordingly to +** the exponent. If the exponent is positive, both the multiplication +** and the shift increase 'x', so we have to care only about overflows. +** For negative exponents, however, multiplying before the shift keeps +** more significant bits, as long as the multiplication does not +** overflow, so we check which order is best. +*/ +l_mem luaO_applyparam (lu_byte p, l_mem x) { + int m = p & 0xF; /* mantissa */ + int e = (p >> 4); /* exponent */ + if (e > 0) { /* normalized? */ + e--; /* correct exponent */ + m += 0x10; /* correct mantissa; maximum value is 0x1F */ + } + e -= 7; /* correct excess-7 */ + if (e >= 0) { + if (x < (MAX_LMEM / 0x1F) >> e) /* no overflow? */ + return (x * m) << e; /* order doesn't matter here */ + else /* real overflow */ + return MAX_LMEM; + } + else { /* negative exponent */ + e = -e; + if (x < MAX_LMEM / 0x1F) /* multiplication cannot overflow? */ + return (x * m) >> e; /* multiplying first gives more precision */ + else if ((x >> e) < MAX_LMEM / 0x1F) /* cannot overflow after shift? */ + return (x >> e) * m; + else /* real overflow */ + return MAX_LMEM; + } } @@ -132,9 +194,10 @@ void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, } -int luaO_hexavalue (int c) { - if (lisdigit(c)) return c - '0'; - else return (ltolower(c) - 'a') + 10; +lu_byte luaO_hexavalue (int c) { + lua_assert(lisxdigit(c)); + if (lisdigit(c)) return cast_byte(c - '0'); + else return cast_byte((ltolower(c) - 'a') + 10); } @@ -185,7 +248,7 @@ static lua_Number lua_strx2number (const char *s, char **endptr) { nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); - else e++; /* too many digits; ignore, but still count for exponent */ + else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ @@ -292,7 +355,7 @@ static const char *l_str2int (const char *s, lua_Integer *result) { int d = *s - '0'; if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ return NULL; /* do not accept it (as integer) */ - a = a * 10 + d; + a = a * 10 + cast_uint(d); empty = 0; } } @@ -316,14 +379,14 @@ size_t luaO_str2num (const char *s, TValue *o) { } else return 0; /* conversion failed */ - return (e - s) + 1; /* success; return string size */ + return ct_diff2sz(e - s) + 1; /* success; return string size */ } -int luaO_utf8esc (char *buff, unsigned long x) { +int luaO_utf8esc (char *buff, l_uint32 x) { int n = 1; /* number of bytes put in buffer (backwards) */ lua_assert(x <= 0x7FFFFFFFu); - if (x < 0x80) /* ascii? */ + if (x < 0x80) /* ASCII? */ buff[UTF8BUFFSZ - 1] = cast_char(x); else { /* need continuation bytes */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */ @@ -339,32 +402,59 @@ int luaO_utf8esc (char *buff, unsigned long x) { /* -** Maximum length of the conversion of a number to a string. Must be -** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. -** (For a long long int, this is 19 digits plus a sign and a final '\0', -** adding to 21. For a long double, it can go to a sign, 33 digits, -** the dot, an exponent letter, an exponent sign, 5 exponent digits, -** and a final '\0', adding to 43.) +** The size of the buffer for the conversion of a number to a string +** 'LUA_N2SBUFFSZ' must be enough to accommodate both LUA_INTEGER_FMT +** and LUA_NUMBER_FMT. For a long long int, this is 19 digits plus a +** sign and a final '\0', adding to 21. For a long double, it can go to +** a sign, the dot, an exponent letter, an exponent sign, 4 exponent +** digits, the final '\0', plus the significant digits, which are +** approximately the *_DIG attribute. */ -#define MAXNUMBER2STR 44 +#if LUA_N2SBUFFSZ < (20 + l_floatatt(DIG)) +#error "invalid value for LUA_N2SBUFFSZ" +#endif /* -** Convert a number object to a string, adding it to a buffer +** Convert a float to a string, adding it to a buffer. First try with +** a not too large number of digits, to avoid noise (for instance, +** 1.1 going to "1.1000000000000001"). If that lose precision, so +** that reading the result back gives a different number, then do the +** conversion again with extra precision. Moreover, if the numeral looks +** like an integer (without a decimal point or an exponent), add ".0" to +** its end. */ -static int tostringbuff (TValue *obj, char *buff) { +static int tostringbuffFloat (lua_Number n, char *buff) { + /* first conversion */ + int len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT, + (LUAI_UACNUMBER)n); + lua_Number check = lua_str2number(buff, NULL); /* read it back */ + if (check != n) { /* not enough precision? */ + /* convert again with more precision */ + len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT_N, + (LUAI_UACNUMBER)n); + } + /* looks like an integer? */ + if (buff[strspn(buff, "-0123456789")] == '\0') { + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } + return len; +} + + +/* +** Convert a number object to a string, adding it to a buffer. +*/ +unsigned luaO_tostringbuff (const TValue *obj, char *buff) { int len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) - len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); - else { - len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); - if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ - buff[len++] = lua_getlocaledecpoint(); - buff[len++] = '0'; /* adds '.0' to result */ - } - } - return len; + len = lua_integer2str(buff, LUA_N2SBUFFSZ, ivalue(obj)); + else + len = tostringbuffFloat(fltvalue(obj), buff); + lua_assert(len < LUA_N2SBUFFSZ); + return cast_uint(len); } @@ -372,8 +462,8 @@ static int tostringbuff (TValue *obj, char *buff) { ** Convert a number object to a Lua string, replacing the value at 'obj' */ void luaO_tostring (lua_State *L, TValue *obj) { - char buff[MAXNUMBER2STR]; - int len = tostringbuff(obj, buff); + char buff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(obj, buff); setsvalue(L, obj, luaS_newlstr(L, buff, len)); } @@ -388,78 +478,104 @@ void luaO_tostring (lua_State *L, TValue *obj) { /* ** Size for buffer space used by 'luaO_pushvfstring'. It should be -** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages, -** so that 'luaG_addinfo' can work directly on the buffer. +** (LUA_IDSIZE + LUA_N2SBUFFSZ) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the static buffer. */ -#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95) +#define BUFVFS cast_uint(LUA_IDSIZE + LUA_N2SBUFFSZ + 95) -/* buffer used by 'luaO_pushvfstring' */ +/* +** Buffer used by 'luaO_pushvfstring'. 'err' signals an error while +** building result (memory error [1] or buffer overflow [2]). +*/ typedef struct BuffFS { lua_State *L; - int pushed; /* true if there is a part of the result on the stack */ - int blen; /* length of partial string in 'space' */ - char space[BUFVFS]; /* holds last part of the result */ + char *b; + size_t buffsize; + size_t blen; /* length of string in 'buff' */ + int err; + char space[BUFVFS]; /* initial buffer */ } BuffFS; -/* -** Push given string to the stack, as part of the result, and -** join it to previous partial result if there is one. -** It may call 'luaV_concat' while using one slot from EXTRA_STACK. -** This call cannot invoke metamethods, as both operands must be -** strings. It can, however, raise an error if the result is too -** long. In that case, 'luaV_concat' frees the extra slot before -** raising the error. -*/ -static void pushstr (BuffFS *buff, const char *str, size_t lstr) { - lua_State *L = buff->L; - setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr)); - L->top.p++; /* may use one slot from EXTRA_STACK */ - if (!buff->pushed) /* no previous string on the stack? */ - buff->pushed = 1; /* now there is one */ - else /* join previous string with new one */ - luaV_concat(L, 2); +static void initbuff (lua_State *L, BuffFS *buff) { + buff->L = L; + buff->b = buff->space; + buff->buffsize = sizeof(buff->space); + buff->blen = 0; + buff->err = 0; } /* -** empty the buffer space into the stack +** Push final result from 'luaO_pushvfstring'. This function may raise +** errors explicitly or through memory errors, so it must run protected. */ -static void clearbuff (BuffFS *buff) { - pushstr(buff, buff->space, buff->blen); /* push buffer contents */ - buff->blen = 0; /* space now is empty */ +static void pushbuff (lua_State *L, void *ud) { + BuffFS *buff = cast(BuffFS*, ud); + switch (buff->err) { + case 1: /* memory error */ + luaD_throw(L, LUA_ERRMEM); + break; + case 2: /* length overflow: Add "..." at the end of result */ + if (buff->buffsize - buff->blen < 3) + strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */ + else { /* there is enough space left for the "..." */ + strcpy(buff->b + buff->blen, "..."); + buff->blen += 3; + } + /* FALLTHROUGH */ + default: { /* no errors, but it can raise one creating the new string */ + TString *ts = luaS_newlstr(L, buff->b, buff->blen); + setsvalue2s(L, L->top.p, ts); + L->top.p++; + } + } } -/* -** Get a space of size 'sz' in the buffer. If buffer has not enough -** space, empty it. 'sz' must fit in an empty buffer. -*/ -static char *getbuff (BuffFS *buff, int sz) { - lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); - if (sz > BUFVFS - buff->blen) /* not enough space? */ - clearbuff(buff); - return buff->space + buff->blen; +static const char *clearbuff (BuffFS *buff) { + lua_State *L = buff->L; + const char *res; + if (luaD_rawrunprotected(L, pushbuff, buff) != LUA_OK) /* errors? */ + res = NULL; /* error message is on the top of the stack */ + else + res = getstr(tsvalue(s2v(L->top.p - 1))); + if (buff->b != buff->space) /* using dynamic buffer? */ + luaM_freearray(L, buff->b, buff->buffsize); /* free it */ + return res; } -#define addsize(b,sz) ((b)->blen += (sz)) - - -/* -** Add 'str' to the buffer. If string is larger than the buffer space, -** push the string directly to the stack. -*/ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { - if (slen <= BUFVFS) { /* does string fit into buffer? */ - char *bf = getbuff(buff, cast_int(slen)); - memcpy(bf, str, slen); /* add string to buffer */ - addsize(buff, cast_int(slen)); - } - else { /* string larger than buffer */ - clearbuff(buff); /* string comes after buffer's content */ - pushstr(buff, str, slen); /* push string */ + size_t left = buff->buffsize - buff->blen; /* space left in the buffer */ + if (buff->err) /* do nothing else after an error */ + return; + if (slen > left) { /* new string doesn't fit into current buffer? */ + if (slen > ((MAX_SIZE/2) - buff->blen)) { /* overflow? */ + memcpy(buff->b + buff->blen, str, left); /* copy what it can */ + buff->blen = buff->buffsize; + buff->err = 2; /* doesn't add anything else */ + return; + } + else { + size_t newsize = buff->buffsize + slen; /* limited to MAX_SIZE/2 */ + char *newb = + (buff->b == buff->space) /* still using static space? */ + ? luaM_reallocvector(buff->L, NULL, 0, newsize, char) + : luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize, + char); + if (newb == NULL) { /* allocation error? */ + buff->err = 1; /* signal a memory error */ + return; + } + if (buff->b == buff->space) /* new buffer (not reallocated)? */ + memcpy(newb, buff->b, buff->blen); /* copy previous content */ + buff->b = newb; /* set new (larger) buffer... */ + buff->buffsize = newsize; /* ...and its new size */ + } } + memcpy(buff->b + buff->blen, str, slen); /* copy new content */ + buff->blen += slen; } @@ -467,9 +583,9 @@ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { ** Add a numeral to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { - char *numbuff = getbuff(buff, MAXNUMBER2STR); - int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ - addsize(buff, len); + char numbuff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(num, numbuff); + addstr2buff(buff, numbuff, len); } @@ -480,10 +596,9 @@ static void addnum2buff (BuffFS *buff, TValue *num) { const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { BuffFS buff; /* holds last part of the result */ const char *e; /* points to next '%' */ - buff.pushed = buff.blen = 0; - buff.L = L; + initbuff(L, &buff); while ((e = strchr(fmt, '%')) != NULL) { - addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ + addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */ switch (*(e + 1)) { /* conversion specifier */ case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); @@ -492,7 +607,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } case 'c': { /* an 'int' as a character */ - char c = cast_uchar(va_arg(argp, int)); + char c = cast_char(va_arg(argp, int)); addstr2buff(&buff, &c, sizeof(char)); break; } @@ -504,7 +619,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { } case 'I': { /* a 'lua_Integer' */ TValue num; - setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); + setivalue(&num, cast_Integer(va_arg(argp, l_uacInt))); addnum2buff(&buff, &num); break; } @@ -515,17 +630,17 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } case 'p': { /* a pointer */ - const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ - char *bf = getbuff(&buff, sz); + char bf[LUA_N2SBUFFSZ]; /* enough space for '%p' */ void *p = va_arg(argp, void *); - int len = lua_pointer2str(bf, sz, p); - addsize(&buff, len); + int len = lua_pointer2str(bf, LUA_N2SBUFFSZ, p); + addstr2buff(&buff, bf, cast_uint(len)); break; } - case 'U': { /* a 'long' as a UTF-8 sequence */ + case 'U': { /* an 'unsigned long' as a UTF-8 sequence */ char bf[UTF8BUFFSZ]; - int len = luaO_utf8esc(bf, va_arg(argp, long)); - addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); + unsigned long arg = va_arg(argp, unsigned long); + int len = luaO_utf8esc(bf, cast(l_uint32, arg)); + addstr2buff(&buff, bf + UTF8BUFFSZ - len, cast_uint(len)); break; } case '%': { @@ -533,16 +648,14 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { break; } default: { - luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", - *(e + 1)); + addstr2buff(&buff, e, 2); /* keep unknown format in the result */ + break; } } fmt = e + 2; /* skip '%' and the specifier */ } addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ - clearbuff(&buff); /* empty buffer into the stack */ - lua_assert(buff.pushed == 1); - return getstr(tsvalue(s2v(L->top.p - 1))); + return clearbuff(&buff); /* empty buffer into a new string */ } @@ -552,6 +665,8 @@ const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); va_end(argp); + if (msg == NULL) /* error? */ + luaD_throw(L, LUA_ERRMEM); return msg; } @@ -591,7 +706,8 @@ void luaO_chunkid (char *out, const char *source, size_t srclen) { addstr(out, source, srclen); /* keep it */ } else { - if (nl != NULL) srclen = nl - source; /* stop at first newline */ + if (nl != NULL) + srclen = ct_diff2sz(nl - source); /* stop at first newline */ if (srclen > bufflen) srclen = bufflen; addstr(out, source, srclen); addstr(out, RETS, LL(RETS)); diff --git a/lua/src/lobject.h b/lua/src/lobject.h index 980e42f..156c942 100644 --- a/lua/src/lobject.h +++ b/lua/src/lobject.h @@ -188,10 +188,21 @@ typedef union { /* Value returned for a key not found in a table (absent key) */ #define LUA_VABSTKEY makevariant(LUA_TNIL, 2) +/* Special variant to signal that a fast get is accessing a non-table */ +#define LUA_VNOTABLE makevariant(LUA_TNIL, 3) + /* macro to test for (any kind of) nil */ #define ttisnil(v) checktype((v), LUA_TNIL) +/* +** Macro to test the result of a table access. Formally, it should +** distinguish between LUA_VEMPTY/LUA_VABSTKEY/LUA_VNOTABLE and +** other tags. As currently nil is equivalent to LUA_VEMPTY, it is +** simpler to just test whether the value is nil. +*/ +#define tagisempty(tag) (novariant(tag) == LUA_TNIL) + /* macro to test for a standard nil */ #define ttisstrictnil(o) checktag((o), LUA_VNIL) @@ -245,6 +256,8 @@ typedef union { #define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) +#define tagisfalse(t) ((t) == LUA_VFALSE || novariant(t) == LUA_TNIL) + #define setbfvalue(obj) settt_(obj, LUA_VFALSE) @@ -380,35 +393,54 @@ typedef struct GCObject { #define setsvalue2n setsvalue +/* Kinds of long strings (stored in 'shrlen') */ +#define LSTRREG -1 /* regular long string */ +#define LSTRFIX -2 /* fixed external long string */ +#define LSTRMEM -3 /* external long string with deallocation */ + + /* ** Header for a string value. */ typedef struct TString { CommonHeader; lu_byte extra; /* reserved words for short strings; "has hash" for longs */ - lu_byte shrlen; /* length for short strings, 0xFF for long strings */ + ls_byte shrlen; /* length for short strings, negative for long strings */ unsigned int hash; union { size_t lnglen; /* length for long strings */ struct TString *hnext; /* linked list for hash table */ } u; - char contents[1]; + char *contents; /* pointer to content in long strings */ + lua_Alloc falloc; /* deallocation function for external strings */ + void *ud; /* user data for external strings */ } TString; +#define strisshr(ts) ((ts)->shrlen >= 0) +#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG) + /* ** Get the actual string (array of bytes) from a 'TString'. (Generic ** version and specialized versions for long and short strings.) */ -#define getstr(ts) ((ts)->contents) -#define getlngstr(ts) check_exp((ts)->shrlen == 0xFF, (ts)->contents) -#define getshrstr(ts) check_exp((ts)->shrlen != 0xFF, (ts)->contents) +#define rawgetshrstr(ts) (cast_charp(&(ts)->contents)) +#define getshrstr(ts) check_exp(strisshr(ts), rawgetshrstr(ts)) +#define getlngstr(ts) check_exp(!strisshr(ts), (ts)->contents) +#define getstr(ts) (strisshr(ts) ? rawgetshrstr(ts) : (ts)->contents) -/* get string length from 'TString *s' */ -#define tsslen(s) \ - ((s)->shrlen != 0xFF ? (s)->shrlen : (s)->u.lnglen) +/* get string length from 'TString *ts' */ +#define tsslen(ts) \ + (strisshr(ts) ? cast_sizet((ts)->shrlen) : (ts)->u.lnglen) + +/* +** Get string and length */ +#define getlstr(ts, len) \ + (strisshr(ts) \ + ? (cast_void((len) = cast_sizet((ts)->shrlen)), rawgetshrstr(ts)) \ + : (cast_void((len) = (ts)->u.lnglen), (ts)->contents)) /* }================================================================== */ @@ -486,8 +518,8 @@ typedef struct Udata0 { /* compute the offset of the memory area of a userdata */ #define udatamemoffset(nuv) \ - ((nuv) == 0 ? offsetof(Udata0, bindata) \ - : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) + ((nuv) == 0 ? offsetof(Udata0, bindata) \ + : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) /* get the address of the memory block inside 'Udata' */ #define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) @@ -507,6 +539,9 @@ typedef struct Udata0 { #define LUA_VPROTO makevariant(LUA_TPROTO, 0) +typedef l_uint32 Instruction; + + /* ** Description of an upvalue for function prototypes */ @@ -544,13 +579,30 @@ typedef struct AbsLineInfo { int line; } AbsLineInfo; + +/* +** Flags in Prototypes +*/ +#define PF_VAHID 1 /* function has hidden vararg arguments */ +#define PF_VATAB 2 /* function has vararg table */ +#define PF_FIXED 4 /* prototype has parts in fixed memory */ + +/* a vararg function either has hidden args. or a vararg table */ +#define isvararg(p) ((p)->flag & (PF_VAHID | PF_VATAB)) + +/* +** mark that a function needs a vararg table. (The flag PF_VAHID will +** be cleared later.) +*/ +#define needvatab(p) ((p)->flag |= PF_VATAB) + /* ** Function Prototypes */ typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed (named) parameters */ - lu_byte is_vararg; + lu_byte flag; lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ int sizek; /* size of 'k' */ @@ -708,10 +760,9 @@ typedef union Node { /* copy a value into a key */ -#define setnodekey(L,node,obj) \ +#define setnodekey(node,obj) \ { Node *n_=(node); const TValue *io_=(obj); \ - n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \ - checkliveness(L,io_); } + n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; } /* copy a value from a key */ @@ -721,27 +772,14 @@ typedef union Node { checkliveness(L,io_); } -/* -** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the -** real size of 'array'. Otherwise, the real size of 'array' is the -** smallest power of two not smaller than 'alimit' (or zero iff 'alimit' -** is zero); 'alimit' is then used as a hint for #t. -*/ - -#define BITRAS (1 << 7) -#define isrealasize(t) (!((t)->flags & BITRAS)) -#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS)) -#define setnorealasize(t) ((t)->flags |= BITRAS) - typedef struct Table { CommonHeader; lu_byte flags; /* 1<

lsizenode)) /* size of buffer for 'luaO_utf8esc' function */ #define UTF8BUFFSZ 8 -LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); -LUAI_FUNC int luaO_ceillog2 (unsigned int x); + +/* macro to call 'luaO_pushvfstring' correctly */ +#define pushvfstring(L, argp, fmt, msg) \ + { va_start(argp, fmt); \ + msg = luaO_pushvfstring(L, fmt, argp); \ + va_end(argp); \ + if (msg == NULL) luaD_throw(L, LUA_ERRMEM); /* only after 'va_end' */ } + + +LUAI_FUNC int luaO_utf8esc (char *buff, l_uint32 x); +LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x); +LUAI_FUNC lu_byte luaO_codeparam (unsigned int p); +LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x); + LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res); LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, StkId res); LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); -LUAI_FUNC int luaO_hexavalue (int c); +LUAI_FUNC unsigned luaO_tostringbuff (const TValue *obj, char *buff); +LUAI_FUNC lu_byte luaO_hexavalue (int c); LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp); diff --git a/lua/src/lopcodes.c b/lua/src/lopcodes.c index c67aa22..7e18231 100644 --- a/lua/src/lopcodes.c +++ b/lua/src/lopcodes.c @@ -13,6 +13,10 @@ #include "lopcodes.h" +#define opmode(mm,ot,it,t,a,m) \ + (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) + + /* ORDER OP */ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { @@ -36,7 +40,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ - ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */ + ,opmode(0, 0, 0, 0, 1, ivABC) /* OP_NEWTABLE */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ @@ -49,8 +53,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ - ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ @@ -64,8 +68,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ - ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/ - ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ @@ -95,10 +99,42 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ - ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ + ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */ ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ }; + + +/* +** Check whether instruction sets top for next instruction, that is, +** it results in multiple values. +*/ +int luaP_isOT (Instruction i) { + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_TAILCALL: return 1; + default: + return testOTMode(op) && GETARG_C(i) == 0; + } +} + + +/* +** Check whether instruction uses top from previous instruction, that is, +** it accepts multiple results. +*/ +int luaP_isIT (Instruction i) { + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_SETLIST: + return testITMode(GET_OPCODE(i)) && GETARG_vB(i) == 0; + default: + return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0; + } +} + diff --git a/lua/src/lopcodes.h b/lua/src/lopcodes.h index 46911ca..b6bd182 100644 --- a/lua/src/lopcodes.h +++ b/lua/src/lopcodes.h @@ -8,6 +8,7 @@ #define lopcodes_h #include "llimits.h" +#include "lobject.h" /*=========================================================================== @@ -18,25 +19,30 @@ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 iABC C(8) | B(8) |k| A(8) | Op(7) | +ivABC vC(10) | vB(6) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) | iAx Ax(25) | Op(7) | isJ sJ (signed)(25) | Op(7) | - A signed argument is represented in excess K: the represented value is - the written unsigned value minus K, where K is half the maximum for the - corresponding unsigned argument. + ('v' stands for "variant", 's' for "signed", 'x' for "extended".) + A signed argument is represented in excess K: The represented value is + the written unsigned value minus K, where K is half (rounded down) the + maximum value for the corresponding unsigned argument. ===========================================================================*/ -enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ +/* basic instruction formats */ +enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ}; /* ** size and position of opcode arguments. */ #define SIZE_C 8 +#define SIZE_vC 10 #define SIZE_B 8 +#define SIZE_vB 6 #define SIZE_Bx (SIZE_C + SIZE_B + 1) #define SIZE_A 8 #define SIZE_Ax (SIZE_Bx + SIZE_A) @@ -49,7 +55,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ #define POS_A (POS_OP + SIZE_OP) #define POS_k (POS_A + SIZE_A) #define POS_B (POS_k + 1) +#define POS_vB (POS_k + 1) #define POS_C (POS_B + SIZE_B) +#define POS_vC (POS_vB + SIZE_vB) #define POS_Bx POS_k @@ -64,14 +72,17 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ ** so they must fit in ints. */ -/* Check whether type 'int' has at least 'b' bits ('b' < 32) */ -#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1) +/* +** Check whether type 'int' has at least 'b' + 1 bits. +** 'b' < 32; +1 for the sign bit. +*/ +#define L_INTHASBITS(b) ((UINT_MAX >> (b)) >= 1) #if L_INTHASBITS(SIZE_Bx) #define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ @@ -80,13 +91,13 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ #if L_INTHASBITS(SIZE_Ax) #define MAXARG_Ax ((1<> 1) @@ -94,7 +105,9 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ #define MAXARG_A ((1<> 1) #define int2sC(i) ((i) + OFFSET_sC) @@ -113,28 +126,36 @@ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ #define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ - ((cast(Instruction, o)<>(pos)) & MASK1(size,0))) #define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ - ((cast(Instruction, v)<> sC */ OP_SHLI,/* A B sC R[A] := sC << R[B] */ +OP_SHRI,/* A B sC R[A] := R[B] >> sC */ OP_ADD,/* A B C R[A] := R[B] + R[C] */ OP_SUB,/* A B C R[A] := R[B] - R[C] */ @@ -255,7 +289,7 @@ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ OP_SHL,/* A B C R[A] := R[B] << R[C] */ OP_SHR,/* A B C R[A] := R[B] >> R[C] */ -OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ @@ -281,12 +315,12 @@ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ OP_TEST,/* A k if (not R[A] == k) then pc++ */ -OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ -OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] */ OP_RETURN0,/* return */ OP_RETURN1,/* A return R[A] */ @@ -298,13 +332,17 @@ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ -OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ +OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ -OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ +OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */ + +OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ + +OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx - 1] is global name)*/ -OP_VARARGPREP,/*A (adjust vararg parameters) */ +OP_VARARGPREP,/* (adjust varargs) */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ } OpCode; @@ -333,7 +371,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ OP_RETURN*, OP_SETLIST) may use 'top'. (*) In OP_VARARG, if (C == 0) then use actual number of varargs and - set top (like in OP_CALL with C == 0). + set top (like in OP_CALL with C == 0). 'k' means function has a + vararg table, which is in R[B]. (*) In OP_RETURN, if (B == 0) then return up to 'top'. @@ -344,22 +383,27 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the bits of C). - (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a + (*) In OP_NEWTABLE, vB is log2 of the hash size (which is always a power of 2) plus 1, or zero for size zero. If not k, the array size - is C. Otherwise, the array size is EXTRAARG _ C. + is vC. Otherwise, the array size is EXTRAARG _ vC. + + (*) In OP_ERRNNIL, (Bx == 0) means index of global name doesn't + fit in Bx. (So, that name is not available for the error message.) (*) For comparisons, k specifies what condition the test should accept (true or false). (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped - (the constant is the first operand). + (the constant is the first operand). - (*) All 'skips' (pc++) assume that next instruction is a jump. + (*) All comparison and test instructions assume that the instruction + being skipped (pc++) is a jump. (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the function builds upvalues, which may need to be closed. C > 0 means - the function is vararg, so that its 'func' must be corrected before - returning; in this case, (C - 1) is its number of fixed parameters. + the function has hidden vararg arguments, so that its 'func' must be + corrected before returning; in this case, (C - 1) is its number of + fixed parameters. (*) In comparisons with an immediate operand, C signals whether the original operand was a float. (It must be corrected in case of @@ -387,19 +431,9 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) #define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) #define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) -/* "out top" (set top for next instruction) */ -#define isOT(i) \ - ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \ - GET_OPCODE(i) == OP_TAILCALL) - -/* "in top" (uses top from previous instruction) */ -#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0) - -#define opmode(mm,ot,it,t,a,m) \ - (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) +LUAI_FUNC int luaP_isOT (Instruction i); +LUAI_FUNC int luaP_isIT (Instruction i); -/* number of list items to accumulate before a SETLIST instruction */ -#define LFIELDS_PER_FLUSH 50 #endif diff --git a/lua/src/lopnames.h b/lua/src/lopnames.h index 965cec9..0554a2e 100644 --- a/lua/src/lopnames.h +++ b/lua/src/lopnames.h @@ -45,8 +45,8 @@ static const char *const opnames[] = { "BANDK", "BORK", "BXORK", - "SHRI", "SHLI", + "SHRI", "ADD", "SUB", "MUL", @@ -94,6 +94,8 @@ static const char *const opnames[] = { "SETLIST", "CLOSURE", "VARARG", + "GETVARG", + "ERRNNIL", "VARARGPREP", "EXTRAARG", NULL diff --git a/lua/src/loslib.c b/lua/src/loslib.c index ba80d72..b7a2b0d 100644 --- a/lua/src/loslib.c +++ b/lua/src/loslib.c @@ -20,6 +20,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" /* @@ -33,7 +34,7 @@ #if defined(LUA_USE_WINDOWS) #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ -#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */ +#elif defined(LUA_USE_C89) /* C89 (only 1-char options) */ #define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" #else /* C99 specification */ #define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ @@ -272,9 +273,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { static const char *checkoption (lua_State *L, const char *conv, - ptrdiff_t convlen, char *buff) { + size_t convlen, char *buff) { const char *option = LUA_STRFTIMEOPTIONS; - int oplen = 1; /* length of options being checked */ + unsigned oplen = 1; /* length of options being checked */ for (; *option != '\0' && oplen <= convlen; option += oplen) { if (*option == '|') /* next block? */ oplen++; /* will check options with next length (+1) */ @@ -332,7 +333,8 @@ static int os_date (lua_State *L) { size_t reslen; char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s++; /* skip '%' */ - s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ + /* copy specifier to 'cc' */ + s = checkoption(L, s, ct_diff2sz(se - s), cc + 1); reslen = strftime(buff, SIZETIMEFMT, cc, stm); luaL_addsize(&b, reslen); } diff --git a/lua/src/lparser.c b/lua/src/lparser.c index 2b888c7..b3855d4 100644 --- a/lua/src/lparser.c +++ b/lua/src/lparser.c @@ -30,8 +30,8 @@ -/* maximum number of local variables per function (must be smaller - than 250, due to the bytecode format) */ +/* maximum number of variable declarations per function (must be + smaller than 250, due to the bytecode format) */ #define MAXVARS 200 @@ -50,9 +50,9 @@ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int firstlabel; /* index of first label in this block */ int firstgoto; /* index of first pending goto in this block */ - lu_byte nactvar; /* # active locals outside the block */ + short nactvar; /* number of active declarations at block entry */ lu_byte upval; /* true if some variable in the block is an upvalue */ - lu_byte isloop; /* true if 'block' is a loop */ + lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ } BlockCnt; @@ -84,8 +84,8 @@ static l_noret errorlimit (FuncState *fs, int limit, const char *what) { } -static void checklimit (FuncState *fs, int v, int l, const char *what) { - if (v > l) errorlimit(fs, l, what); +void luaY_checklimit (FuncState *fs, int v, int l, const char *what) { + if (l_unlikely(v > l)) errorlimit(fs, l, what); } @@ -172,7 +172,8 @@ static void codename (LexState *ls, expdesc *e) { ** Register a new local variable in the active 'Proto' (for debug ** information). */ -static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { +static short registerlocalvar (LexState *ls, FuncState *fs, + TString *varname) { Proto *f = fs->f; int oldsize = f->sizelocvars; luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, @@ -187,24 +188,30 @@ static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { /* -** Create a new local variable with the given 'name'. Return its index -** in the function. +** Create a new variable with the given 'name' and given 'kind'. +** Return its index in the function. */ -static int new_localvar (LexState *ls, TString *name) { +static int new_varkind (LexState *ls, TString *name, lu_byte kind) { lua_State *L = ls->L; FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; Vardesc *var; - checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, - MAXVARS, "local variables"); luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, - dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); + dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations"); var = &dyd->actvar.arr[dyd->actvar.n++]; - var->vd.kind = VDKREG; /* default */ + var->vd.kind = kind; /* default */ var->vd.name = name; return dyd->actvar.n - 1 - fs->firstlocal; } + +/* +** Create a new local variable with the given 'name' and regular kind. +*/ +static int new_localvar (LexState *ls, TString *name) { + return new_varkind(ls, name, VDKREG); +} + #define new_localvarliteral(ls,v) \ new_localvar(ls, \ luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); @@ -226,11 +233,11 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { ** register. For that, search for the highest variable below that level ** that is in a register and uses its register index ('ridx') plus one. */ -static int reglevel (FuncState *fs, int nvar) { +static lu_byte reglevel (FuncState *fs, int nvar) { while (nvar-- > 0) { Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ - if (vd->vd.kind != RDKCTC) /* is in a register? */ - return vd->vd.ridx + 1; + if (varinreg(vd)) /* is in a register? */ + return cast_byte(vd->vd.ridx + 1); } return 0; /* no variables in registers */ } @@ -240,7 +247,7 @@ static int reglevel (FuncState *fs, int nvar) { ** Return the number of variables in the register stack for the given ** function. */ -int luaY_nvarstack (FuncState *fs) { +lu_byte luaY_nvarstack (FuncState *fs) { return reglevel(fs, fs->nactvar); } @@ -250,7 +257,7 @@ int luaY_nvarstack (FuncState *fs) { */ static LocVar *localdebuginfo (FuncState *fs, int vidx) { Vardesc *vd = getlocalvardesc(fs, vidx); - if (vd->vd.kind == RDKCTC) + if (!varinreg(vd)) return NULL; /* no debug info. for constants */ else { int idx = vd->vd.pidx; @@ -266,13 +273,15 @@ static LocVar *localdebuginfo (FuncState *fs, int vidx) { static void init_var (FuncState *fs, expdesc *e, int vidx) { e->f = e->t = NO_JUMP; e->k = VLOCAL; - e->u.var.vidx = vidx; + e->u.var.vidx = cast_short(vidx); e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; } /* -** Raises an error if variable described by 'e' is read only +** Raises an error if variable described by 'e' is read only; moreover, +** if 'e' is t[exp] where t is the vararg parameter, change it to index +** a real table. (Virtual vararg tables cannot be changed.) */ static void check_readonly (LexState *ls, expdesc *e) { FuncState *fs = ls->fs; @@ -282,7 +291,7 @@ static void check_readonly (LexState *ls, expdesc *e) { varname = ls->dyd->actvar.arr[e->u.info].vd.name; break; } - case VLOCAL: { + case VLOCAL: case VVARGVAR: { Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ varname = vardesc->vd.name; @@ -294,14 +303,22 @@ static void check_readonly (LexState *ls, expdesc *e) { varname = up->name; break; } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + e->k = VINDEXED; + } /* FALLTHROUGH */ + case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ + if (e->u.ind.ro) /* read-only? */ + varname = tsvalue(&fs->f->k[e->u.ind.keystr]); + break; + } default: - return; /* other cases cannot be read-only */ - } - if (varname) { - const char *msg = luaO_pushfstring(ls->L, - "attempt to assign to const variable '%s'", getstr(varname)); - luaK_semerror(ls, msg); /* error */ + lua_assert(e->k == VINDEXI); /* this one doesn't need any check */ + return; /* integer index cannot be read-only */ } + if (varname) + luaK_semerror(ls, "attempt to assign to const variable '%s'", + getstr(varname)); } @@ -315,8 +332,9 @@ static void adjustlocalvars (LexState *ls, int nvars) { for (i = 0; i < nvars; i++) { int vidx = fs->nactvar++; Vardesc *var = getlocalvardesc(fs, vidx); - var->vd.ridx = reglevel++; + var->vd.ridx = cast_byte(reglevel++); var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + luaY_checklimit(fs, reglevel, MAXVARS, "local variables"); } } @@ -352,7 +370,7 @@ static int searchupvalue (FuncState *fs, TString *name) { static Upvaldesc *allocupvalue (FuncState *fs) { Proto *f = fs->f; int oldsize = f->sizeupvalues; - checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); + luaY_checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, Upvaldesc, MAXUPVAL, "upvalues"); while (oldsize < f->sizeupvalues) @@ -383,20 +401,43 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { /* -** Look for an active local variable with the name 'n' in the +** Look for an active variable with the name 'n' in the ** function 'fs'. If found, initialize 'var' with it and return -** its expression kind; otherwise return -1. +** its expression kind; otherwise return -1. While searching, +** var->u.info==-1 means that the preambular global declaration is +** active (the default while there is no other global declaration); +** var->u.info==-2 means there is no active collective declaration +** (some previous global declaration but no collective declaration); +** and var->u.info>=0 points to the inner-most (the first one found) +** collective declaration, if there is one. */ static int searchvar (FuncState *fs, TString *n, expdesc *var) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { Vardesc *vd = getlocalvardesc(fs, i); - if (eqstr(n, vd->vd.name)) { /* found? */ + if (varglobal(vd)) { /* global declaration? */ + if (vd->vd.name == NULL) { /* collective declaration? */ + if (var->u.info < 0) /* no previous collective declaration? */ + var->u.info = fs->firstlocal + i; /* this is the first one */ + } + else { /* global name */ + if (eqstr(n, vd->vd.name)) { /* found? */ + init_exp(var, VGLOBAL, fs->firstlocal + i); + return VGLOBAL; + } + else if (var->u.info == -1) /* active preambular declaration? */ + var->u.info = -2; /* invalidate preambular declaration */ + } + } + else if (eqstr(n, vd->vd.name)) { /* found? */ if (vd->vd.kind == RDKCTC) /* compile-time constant? */ init_exp(var, VCONST, fs->firstlocal + i); - else /* real variable */ + else { /* local variable */ init_var(fs, var, i); - return var->k; + if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */ + var->k = VVARGVAR; + } + return cast_int(var->k); } } return -1; /* not found */ @@ -433,48 +474,72 @@ static void marktobeclosed (FuncState *fs) { ** 'var' as 'void' as a flag. */ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { - if (fs == NULL) /* no more levels? */ - init_exp(var, VVOID, 0); /* default is global */ - else { - int v = searchvar(fs, n, var); /* look up locals at current level */ - if (v >= 0) { /* found? */ - if (v == VLOCAL && !base) - markupval(fs, var->u.var.vidx); /* local will be used as an upval */ + int v = searchvar(fs, n, var); /* look up variables at current level */ + if (v >= 0) { /* found? */ + if (!base) { + if (var->k == VVARGVAR) /* vararg parameter? */ + luaK_vapar2local(fs, var); /* change it to a regular local */ + if (var->k == VLOCAL) + markupval(fs, var->u.var.vidx); /* will be used as an upvalue */ } - else { /* not found as local at current level; try upvalues */ - int idx = searchupvalue(fs, n); /* try existing upvalues */ - if (idx < 0) { /* not found? */ + /* else nothing else to be done */ + } + else { /* not found at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + if (fs->prev != NULL) /* more levels? */ singlevaraux(fs->prev, n, var, 0); /* try upper levels */ - if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ - idx = newupvalue(fs, n, var); /* will be a new upvalue */ - else /* it is a global or a constant */ - return; /* don't need to do anything at this level */ - } - init_exp(var, VUPVAL, idx); /* new or old upvalue */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ } + init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } +static void buildglobal (LexState *ls, TString *varname, expdesc *var) { + FuncState *fs = ls->fs; + expdesc key; + init_exp(var, VGLOBAL, -1); /* global by default */ + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + if (var->k == VGLOBAL) + luaK_semerror(ls, "%s is global when accessing variable '%s'", + LUA_ENV, getstr(varname)); + luaK_exp2anyregup(fs, var); /* _ENV could be a constant */ + codestring(&key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* 'var' represents _ENV[varname] */ +} + + /* ** Find a variable with the given name 'n', handling global variables ** too. */ -static void singlevar (LexState *ls, expdesc *var) { - TString *varname = str_checkname(ls); +static void buildvar (LexState *ls, TString *varname, expdesc *var) { FuncState *fs = ls->fs; + init_exp(var, VGLOBAL, -1); /* global by default */ singlevaraux(fs, varname, var, 1); - if (var->k == VVOID) { /* global name? */ - expdesc key; - singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ - lua_assert(var->k != VVOID); /* this one must exist */ - luaK_exp2anyregup(fs, var); /* but could be a constant */ - codestring(&key, varname); /* key is variable name */ - luaK_indexed(fs, var, &key); /* env[varname] */ + if (var->k == VGLOBAL) { /* global name? */ + int info = var->u.info; + /* global by default in the scope of a global declaration? */ + if (info == -2) + luaK_semerror(ls, "variable '%s' not declared", getstr(varname)); + buildglobal(ls, varname, var); + if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST) + var->u.ind.ro = 1; /* mark variable as read-only */ + else /* anyway must be a global */ + lua_assert(info == -1 || ls->dyd->actvar.arr[info].vd.kind == GDKREG); } } +static void singlevar (LexState *ls, expdesc *var) { + buildvar(ls, str_checkname(ls), var); +} + + /* ** Adjust the number of results from an expression list 'e' with 'nexps' ** expressions to 'nvars' values. @@ -482,6 +547,7 @@ static void singlevar (LexState *ls, expdesc *var) { static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; int needed = nvars - nexps; /* extra values needed */ + luaK_checkstack(fs, needed); if (hasmultret(e->k)) { /* last expression has multiple returns? */ int extra = needed + 1; /* discount last expression itself */ if (extra < 0) @@ -497,7 +563,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { if (needed > 0) luaK_reserveregs(fs, needed); /* registers for extra values */ else /* adding 'needed' is actually a subtraction */ - fs->freereg += needed; /* remove extra values */ + fs->freereg = cast_byte(fs->freereg + needed); /* remove extra values */ } @@ -509,29 +575,43 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { /* ** Generates an error that a goto jumps into the scope of some -** local variable. +** variable declaration. */ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { - const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); - const char *msg = " at line %d jumps into the scope of local '%s'"; - msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); - luaK_semerror(ls, msg); /* raise the error */ + TString *tsname = getlocalvardesc(ls->fs, gt->nactvar)->vd.name; + const char *varname = (tsname != NULL) ? getstr(tsname) : "*"; + luaK_semerror(ls, + " at line %d jumps into the scope of '%s'", + getstr(gt->name), gt->line, varname); /* raise the error */ } /* -** Solves the goto at index 'g' to given 'label' and removes it +** Closes the goto at index 'g' to given 'label' and removes it ** from the list of pending gotos. ** If it jumps into the scope of some variable, raises an error. +** The goto needs a CLOSE if it jumps out of a block with upvalues, +** or out of the scope of some variable and the block has upvalues +** (signaled by parameter 'bup'). */ -static void solvegoto (LexState *ls, int g, Labeldesc *label) { +static void closegoto (LexState *ls, int g, Labeldesc *label, int bup) { int i; + FuncState *fs = ls->fs; Labellist *gl = &ls->dyd->gt; /* list of gotos */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ lua_assert(eqstr(gt->name, label->name)); if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ jumpscopeerror(ls, gt); - luaK_patchlist(ls->fs, gt->pc, label->pc); + if (gt->close || + (label->nactvar < gt->nactvar && bup)) { /* needs close? */ + lu_byte stklevel = reglevel(fs, label->nactvar); + /* move jump to CLOSE position */ + fs->f->code[gt->pc + 1] = fs->f->code[gt->pc]; + /* put CLOSE instruction at original position */ + fs->f->code[gt->pc] = CREATE_ABCk(OP_CLOSE, stklevel, 0, 0, 0); + gt->pc++; /* must point to jump instruction */ + } + luaK_patchlist(ls->fs, gt->pc, label->pc); /* goto jumps to label */ for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ gl->arr[i] = gl->arr[i + 1]; gl->n--; @@ -539,14 +619,14 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) { /* -** Search for an active label with the given name. +** Search for an active label with the given name, starting at +** index 'ilb' (so that it can search for all labels in current block +** or all labels in current function). */ -static Labeldesc *findlabel (LexState *ls, TString *name) { - int i; +static Labeldesc *findlabel (LexState *ls, TString *name, int ilb) { Dyndata *dyd = ls->dyd; - /* check labels in current function for a match */ - for (i = ls->fs->firstlabel; i < dyd->label.n; i++) { - Labeldesc *lb = &dyd->label.arr[i]; + for (; ilb < dyd->label.n; ilb++) { + Labeldesc *lb = &dyd->label.arr[ilb]; if (eqstr(lb->name, name)) /* correct label? */ return lb; } @@ -572,29 +652,19 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name, } -static int newgotoentry (LexState *ls, TString *name, int line, int pc) { - return newlabelentry(ls, &ls->dyd->gt, name, line, pc); -} - - /* -** Solves forward jumps. Check whether new label 'lb' matches any -** pending gotos in current block and solves them. Return true -** if any of the gotos need to close upvalues. +** Create an entry for the goto and the code for it. As it is not known +** at this point whether the goto may need a CLOSE, the code has a jump +** followed by an CLOSE. (As the CLOSE comes after the jump, it is a +** dead instruction; it works as a placeholder.) When the goto is closed +** against a label, if it needs a CLOSE, the two instructions swap +** positions, so that the CLOSE comes before the jump. */ -static int solvegotos (LexState *ls, Labeldesc *lb) { - Labellist *gl = &ls->dyd->gt; - int i = ls->fs->bl->firstgoto; - int needsclose = 0; - while (i < gl->n) { - if (eqstr(gl->arr[i].name, lb->name)) { - needsclose |= gl->arr[i].close; - solvegoto(ls, i, lb); /* will remove 'i' from the list */ - } - else - i++; - } - return needsclose; +static int newgotoentry (LexState *ls, TString *name, int line) { + FuncState *fs = ls->fs; + int pc = luaK_jump(fs); /* create jump */ + luaK_codeABC(fs, OP_CLOSE, 0, 1, 0); /* spaceholder, marked as dead */ + return newlabelentry(ls, &ls->dyd->gt, name, line, pc); } @@ -605,8 +675,7 @@ static int solvegotos (LexState *ls, Labeldesc *lb) { ** a close instruction if necessary. ** Returns true iff it added a close instruction. */ -static int createlabel (LexState *ls, TString *name, int line, - int last) { +static void createlabel (LexState *ls, TString *name, int line, int last) { FuncState *fs = ls->fs; Labellist *ll = &ls->dyd->label; int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); @@ -614,28 +683,37 @@ static int createlabel (LexState *ls, TString *name, int line, /* assume that locals are already out of scope */ ll->arr[l].nactvar = fs->bl->nactvar; } - if (solvegotos(ls, &ll->arr[l])) { /* need close? */ - luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0); - return 1; - } - return 0; } /* -** Adjust pending gotos to outer level of a block. +** Traverse the pending gotos of the finishing block checking whether +** each match some label of that block. Those that do not match are +** "exported" to the outer block, to be solved there. In particular, +** its 'nactvar' is updated with the level of the inner block, +** as the variables of the inner block are now out of scope. */ -static void movegotosout (FuncState *fs, BlockCnt *bl) { - int i; - Labellist *gl = &fs->ls->dyd->gt; - /* correct pending gotos to current block */ - for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */ - Labeldesc *gt = &gl->arr[i]; - /* leaving a variable scope? */ - if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar)) - gt->close |= bl->upval; /* jump may need a close */ - gt->nactvar = bl->nactvar; /* update goto level */ +static void solvegotos (FuncState *fs, BlockCnt *bl) { + LexState *ls = fs->ls; + Labellist *gl = &ls->dyd->gt; + int outlevel = reglevel(fs, bl->nactvar); /* level outside the block */ + int igt = bl->firstgoto; /* first goto in the finishing block */ + while (igt < gl->n) { /* for each pending goto */ + Labeldesc *gt = &gl->arr[igt]; + /* search for a matching label in the current block */ + Labeldesc *lb = findlabel(ls, gt->name, bl->firstlabel); + if (lb != NULL) /* found a match? */ + closegoto(ls, igt, lb, bl->upval); /* close and remove goto */ + else { /* adjust 'goto' for outer block */ + /* block has variables to be closed and goto escapes the scope of + some variable? */ + if (bl->upval && reglevel(fs, gt->nactvar) > outlevel) + gt->close = 1; /* jump may need a close */ + gt->nactvar = bl->nactvar; /* correct level for outer block */ + igt++; /* go to next goto */ + } } + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ } @@ -645,8 +723,9 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { bl->firstlabel = fs->ls->dyd->label.n; bl->firstgoto = fs->ls->dyd->gt.n; bl->upval = 0; + /* inherit 'insidetbc' from enclosing block */ bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); - bl->previous = fs->bl; + bl->previous = fs->bl; /* link block in function's block list */ fs->bl = bl; lua_assert(fs->freereg == luaY_nvarstack(fs)); } @@ -656,39 +735,30 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { ** generates an error for an undefined 'goto'. */ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { - const char *msg; - if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) { - msg = "break outside loop at line %d"; - msg = luaO_pushfstring(ls->L, msg, gt->line); - } - else { - msg = "no visible label '%s' for at line %d"; - msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); - } - luaK_semerror(ls, msg); + /* breaks are checked when created, cannot be undefined */ + lua_assert(!eqstr(gt->name, ls->brkn)); + luaK_semerror(ls, "no visible label '%s' for at line %d", + getstr(gt->name), gt->line); } static void leaveblock (FuncState *fs) { BlockCnt *bl = fs->bl; LexState *ls = fs->ls; - int hasclose = 0; - int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ - removevars(fs, bl->nactvar); /* remove block locals */ - lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ - if (bl->isloop) /* has to fix pending breaks? */ - hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); - if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */ + lu_byte stklevel = reglevel(fs, bl->nactvar); /* level outside block */ + if (bl->previous && bl->upval) /* need a 'close'? */ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); fs->freereg = stklevel; /* free registers */ - ls->dyd->label.n = bl->firstlabel; /* remove local labels */ - fs->bl = bl->previous; /* current block now is previous one */ - if (bl->previous) /* was it a nested block? */ - movegotosout(fs, bl); /* update pending gotos to enclosing block */ - else { + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop == 2) /* has to fix pending breaks? */ + createlabel(ls, ls->brkn, 0, 0); + solvegotos(fs, bl); + if (bl->previous == NULL) { /* was it the last block? */ if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } + fs->bl = bl->previous; /* current block now is previous one */ } @@ -727,6 +797,7 @@ static void codeclosure (LexState *ls, expdesc *v) { static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { + lua_State *L = ls->L; Proto *f = fs->f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; @@ -747,8 +818,11 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { fs->firstlabel = ls->dyd->label.n; fs->bl = NULL; f->source = ls->source; - luaC_objbarrier(ls->L, f, f->source); + luaC_objbarrier(L, f, f->source); f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->kcache = luaH_new(L); /* create table for function */ + sethvalue2s(L, L->top.p, fs->kcache); /* anchor it */ + luaD_inctop(L); enterblock(fs, bl, 0); } @@ -770,14 +844,16 @@ static void close_func (LexState *ls) { luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); ls->fs = fs->prev; + L->top.p--; /* pop kcache table */ luaC_checkGC(L); } - -/*============================================================*/ -/* GRAMMAR RULES */ -/*============================================================*/ +/* +** {====================================================================== +** GRAMMAR RULES +** ======================================================================= +*/ /* @@ -834,25 +910,36 @@ static void yindex (LexState *ls, expdesc *v) { ** ======================================================================= */ - typedef struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ int nh; /* total number of 'record' elements */ int na; /* number of array elements already stored */ int tostore; /* number of array elements pending to be stored */ + int maxtostore; /* maximum number of pending elements */ } ConsControl; +/* +** Maximum number of elements in a constructor, to control the following: +** * counter overflows; +** * overflows in 'extra' for OP_NEWTABLE and OP_SETLIST; +** * overflows when adding multiple returns in OP_SETLIST. +*/ +#define MAX_CNST (INT_MAX/2) +#if MAX_CNST/(MAXARG_vC + 1) > MAXARG_Ax +#undef MAX_CNST +#define MAX_CNST (MAXARG_Ax * (MAXARG_vC + 1)) +#endif + + static void recfield (LexState *ls, ConsControl *cc) { /* recfield -> (NAME | '['exp']') = exp */ FuncState *fs = ls->fs; - int reg = ls->fs->freereg; + lu_byte reg = ls->fs->freereg; expdesc tab, key, val; - if (ls->t.token == TK_NAME) { - checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + if (ls->t.token == TK_NAME) codename(ls, &key); - } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; @@ -866,10 +953,10 @@ static void recfield (LexState *ls, ConsControl *cc) { static void closelistfield (FuncState *fs, ConsControl *cc) { - if (cc->v.k == VVOID) return; /* there is no list item */ + lua_assert(cc->tostore > 0); luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; - if (cc->tostore == LFIELDS_PER_FLUSH) { + if (cc->tostore >= cc->maxtostore) { luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ cc->na += cc->tostore; cc->tostore = 0; /* no more items pending */ @@ -922,12 +1009,28 @@ static void field (LexState *ls, ConsControl *cc) { } +/* +** Compute a limit for how many registers a constructor can use before +** emitting a 'SETLIST' instruction, based on how many registers are +** available. +*/ +static int maxtostore (FuncState *fs) { + int numfreeregs = MAX_FSTACK - fs->freereg; + if (numfreeregs >= 160) /* "lots" of registers? */ + return numfreeregs / 5; /* use up to 1/5 of them */ + else if (numfreeregs >= 80) /* still "enough" registers? */ + return 10; /* one 'SETLIST' instruction for each 10 values */ + else /* save registers for potential more nesting */ + return 1; +} + + static void constructor (LexState *ls, expdesc *t) { /* constructor -> '{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ FuncState *fs = ls->fs; int line = ls->linenumber; - int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + int pc = luaK_codevABCk(fs, OP_NEWTABLE, 0, 0, 0, 0); ConsControl cc; luaK_code(fs, 0); /* space for extra arg. */ cc.na = cc.nh = cc.tostore = 0; @@ -935,14 +1038,17 @@ static void constructor (LexState *ls, expdesc *t) { init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ luaK_reserveregs(fs, 1); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ - checknext(ls, '{'); + checknext(ls, '{' /*}*/); + cc.maxtostore = maxtostore(fs); do { - lua_assert(cc.v.k == VVOID || cc.tostore > 0); - if (ls->t.token == '}') break; - closelistfield(fs, &cc); + if (ls->t.token == /*{*/ '}') break; + if (cc.v.k != VVOID) /* is there a previous list item? */ + closelistfield(fs, &cc); /* close it */ field(ls, &cc); + luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST, + "items in a constructor"); } while (testnext(ls, ',') || testnext(ls, ';')); - check_match(ls, '}', '{', line); + check_match(ls, /*{*/ '}', '{' /*}*/, line); lastlistfield(fs, &cc); luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); } @@ -950,9 +1056,9 @@ static void constructor (LexState *ls, expdesc *t) { /* }====================================================================== */ -static void setvararg (FuncState *fs, int nparams) { - fs->f->is_vararg = 1; - luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); +static void setvararg (FuncState *fs) { + fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */ + luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0); } @@ -961,7 +1067,7 @@ static void parlist (LexState *ls) { FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; - int isvararg = 0; + int varargk = 0; if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { @@ -971,19 +1077,26 @@ static void parlist (LexState *ls) { break; } case TK_DOTS: { - luaX_next(ls); - isvararg = 1; + varargk = 1; + luaX_next(ls); /* skip '...' */ + if (ls->t.token == TK_NAME) + new_varkind(ls, str_checkname(ls), RDKVAVAR); + else + new_localvarliteral(ls, "(vararg table)"); break; } default: luaX_syntaxerror(ls, " or '...' expected"); } - } while (!isvararg && testnext(ls, ',')); + } while (!varargk && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); - if (isvararg) - setvararg(fs, f->numparams); /* declared vararg */ - luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ + if (varargk) { + setvararg(fs); /* declared vararg */ + adjustlocalvars(ls, 1); /* vararg parameter */ + } + /* reserve registers for parameters (plus vararg parameter, if present) */ + luaK_reserveregs(fs, fs->nactvar); } @@ -1040,7 +1153,7 @@ static void funcargs (LexState *ls, expdesc *f) { check_match(ls, ')', '(', line); break; } - case '{': { /* funcargs -> constructor */ + case '{' /*}*/: { /* funcargs -> constructor */ constructor(ls, &args); break; } @@ -1064,8 +1177,9 @@ static void funcargs (LexState *ls, expdesc *f) { } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); luaK_fixline(fs, line); - fs->freereg = base+1; /* call removes function and arguments and leaves - one result (unless changed later) */ + /* call removes function and arguments and leaves one result (unless + changed later) */ + fs->freereg = cast_byte(base + 1); } @@ -1126,7 +1240,7 @@ static void suffixedexp (LexState *ls, expdesc *v) { funcargs(ls, v); break; } - case '(': case TK_STRING: case '{': { /* funcargs */ + case '(': case TK_STRING: case '{' /*}*/: { /* funcargs */ luaK_exp2nextreg(fs, v); funcargs(ls, v); break; @@ -1169,12 +1283,12 @@ static void simpleexp (LexState *ls, expdesc *v) { } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; - check_condition(ls, fs->f->is_vararg, + check_condition(ls, isvararg(fs->f), "cannot use '...' outside a vararg function"); - init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1)); break; } - case '{': { /* constructor */ + case '{' /*}*/: { /* constructor */ constructor(ls, v); return; } @@ -1330,7 +1444,7 @@ struct LHS_assign { */ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { FuncState *fs = ls->fs; - int extra = fs->freereg; /* eventual position to save local variable */ + lu_byte extra = fs->freereg; /* eventual position to save local variable */ int conflict = 0; for (; lh; lh = lh->prev) { /* check all previous assignments */ if (vkisindexed(lh->v.k)) { /* assignment to table field? */ @@ -1365,6 +1479,15 @@ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { } } + +/* Create code to store the "top" register in 'var' */ +static void storevartop (FuncState *fs, expdesc *var) { + expdesc e; + init_exp(&e, VNONRELOC, fs->freereg - 1); + luaK_storevar(fs, var, &e); /* will also free the top register */ +} + + /* ** Parse and compile a multiple assignment. The first "variable" ** (a 'suffixedexp') was already read by the caller. @@ -1398,8 +1521,7 @@ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { return; /* avoid default */ } } - init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ - luaK_storevar(ls->fs, &lh->v, &e); + storevartop(ls->fs, &lh->v); /* default assignment */ } @@ -1413,45 +1535,38 @@ static int cond (LexState *ls) { } -static void gotostat (LexState *ls) { - FuncState *fs = ls->fs; - int line = ls->linenumber; +static void gotostat (LexState *ls, int line) { TString *name = str_checkname(ls); /* label's name */ - Labeldesc *lb = findlabel(ls, name); - if (lb == NULL) /* no label? */ - /* forward jump; will be resolved when the label is declared */ - newgotoentry(ls, name, line, luaK_jump(fs)); - else { /* found a label */ - /* backward jump; will be resolved here */ - int lblevel = reglevel(fs, lb->nactvar); /* label level */ - if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */ - luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0); - /* create jump and link it to the label */ - luaK_patchlist(fs, luaK_jump(fs), lb->pc); - } + newgotoentry(ls, name, line); } /* ** Break statement. Semantically equivalent to "goto break". */ -static void breakstat (LexState *ls) { - int line = ls->linenumber; +static void breakstat (LexState *ls, int line) { + BlockCnt *bl; /* to look for an enclosing loop */ + for (bl = ls->fs->bl; bl != NULL; bl = bl->previous) { + if (bl->isloop) /* found one? */ + goto ok; + } + luaX_syntaxerror(ls, "break outside loop"); + ok: + bl->isloop = 2; /* signal that block has pending breaks */ luaX_next(ls); /* skip break */ - newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs)); + newgotoentry(ls, ls->brkn, line); } /* -** Check whether there is already a label with the given 'name'. +** Check whether there is already a label with the given 'name' at +** current function. */ static void checkrepeated (LexState *ls, TString *name) { - Labeldesc *lb = findlabel(ls, name); - if (l_unlikely(lb != NULL)) { /* already defined? */ - const char *msg = "label '%s' already defined on line %d"; - msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); - luaK_semerror(ls, msg); /* error */ - } + Labeldesc *lb = findlabel(ls, name, ls->fs->firstlabel); + if (l_unlikely(lb != NULL)) /* already defined? */ + luaK_semerror(ls, "label '%s' already defined on line %d", + getstr(name), lb->line); /* error */ } @@ -1550,6 +1665,7 @@ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { int prep, endfor; checknext(ls, TK_DO); prep = luaK_codeABx(fs, forprep[isgen], base, 0); + fs->freereg--; /* both 'forprep' remove one register from the stack */ enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); @@ -1572,8 +1688,7 @@ static void fornum (LexState *ls, TString *varname, int line) { int base = fs->freereg; new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); - new_localvarliteral(ls, "(for state)"); - new_localvar(ls, varname); + new_varkind(ls, varname, RDKCONST); /* control variable */ checknext(ls, '='); exp1(ls); /* initial value */ checknext(ls, ','); @@ -1584,7 +1699,7 @@ static void fornum (LexState *ls, TString *varname, int line) { luaK_int(fs, fs->freereg, 1); luaK_reserveregs(fs, 1); } - adjustlocalvars(ls, 3); /* control variables */ + adjustlocalvars(ls, 2); /* start scope for internal variables */ forbody(ls, base, line, 1, 0); } @@ -1593,16 +1708,15 @@ static void forlist (LexState *ls, TString *indexname) { /* forlist -> NAME {,NAME} IN explist forbody */ FuncState *fs = ls->fs; expdesc e; - int nvars = 5; /* gen, state, control, toclose, 'indexname' */ + int nvars = 4; /* function, state, closing, control */ int line; int base = fs->freereg; - /* create control variables */ - new_localvarliteral(ls, "(for state)"); - new_localvarliteral(ls, "(for state)"); - new_localvarliteral(ls, "(for state)"); - new_localvarliteral(ls, "(for state)"); - /* create declared variables */ - new_localvar(ls, indexname); + /* create internal variables */ + new_localvarliteral(ls, "(for state)"); /* iterator function */ + new_localvarliteral(ls, "(for state)"); /* state */ + new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */ + new_varkind(ls, indexname, RDKCONST); /* control variable */ + /* other declared variables */ while (testnext(ls, ',')) { new_localvar(ls, str_checkname(ls)); nvars++; @@ -1610,10 +1724,10 @@ static void forlist (LexState *ls, TString *indexname) { checknext(ls, TK_IN); line = ls->linenumber; adjust_assign(ls, 4, explist(ls, &e), &e); - adjustlocalvars(ls, 4); /* control variables */ - marktobeclosed(fs); /* last control var. must be closed */ - luaK_checkstack(fs, 3); /* extra space to call generator */ - forbody(ls, base, line, nvars - 4, 1); + adjustlocalvars(ls, 3); /* start scope for internal variables */ + marktobeclosed(fs); /* last internal var. must be closed */ + luaK_checkstack(fs, 2); /* extra space to call iterator */ + forbody(ls, base, line, nvars - 3, 1); } @@ -1637,38 +1751,16 @@ static void forstat (LexState *ls, int line) { static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ - BlockCnt bl; FuncState *fs = ls->fs; - expdesc v; - int jf; /* instruction to skip 'then' code (if condition is false) */ + int condtrue; luaX_next(ls); /* skip IF or ELSEIF */ - expr(ls, &v); /* read condition */ + condtrue = cond(ls); /* read condition */ checknext(ls, TK_THEN); - if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */ - int line = ls->linenumber; - luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */ - luaX_next(ls); /* skip 'break' */ - enterblock(fs, &bl, 0); /* must enter block before 'goto' */ - newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t); - while (testnext(ls, ';')) {} /* skip semicolons */ - if (block_follow(ls, 0)) { /* jump is the entire block? */ - leaveblock(fs); - return; /* and that is it */ - } - else /* must skip over 'then' part if condition is false */ - jf = luaK_jump(fs); - } - else { /* regular case (not a break) */ - luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ - enterblock(fs, &bl, 0); - jf = v.f; - } - statlist(ls); /* 'then' part */ - leaveblock(fs); + block(ls); /* 'then' part */ if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ - luaK_patchtohere(fs, jf); + luaK_patchtohere(fs, condtrue); } @@ -1698,20 +1790,20 @@ static void localfunc (LexState *ls) { } -static int getlocalattribute (LexState *ls) { - /* ATTRIB -> ['<' Name '>'] */ +static lu_byte getvarattribute (LexState *ls, lu_byte df) { + /* attrib -> ['<' NAME '>'] */ if (testnext(ls, '<')) { - const char *attr = getstr(str_checkname(ls)); + TString *ts = str_checkname(ls); + const char *attr = getstr(ts); checknext(ls, '>'); if (strcmp(attr, "const") == 0) return RDKCONST; /* read-only variable */ else if (strcmp(attr, "close") == 0) return RDKTOCLOSE; /* to-be-closed variable */ else - luaK_semerror(ls, - luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); + luaK_semerror(ls, "unknown attribute '%s'", attr); } - return VDKREG; /* regular variable */ + return df; /* return default value */ } @@ -1724,18 +1816,20 @@ static void checktoclose (FuncState *fs, int level) { static void localstat (LexState *ls) { - /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ + /* stat -> LOCAL NAME attrib { ',' NAME attrib } ['=' explist] */ FuncState *fs = ls->fs; int toclose = -1; /* index of to-be-closed variable (if any) */ Vardesc *var; /* last variable */ - int vidx, kind; /* index and kind of last variable */ + int vidx; /* index of last variable */ int nvars = 0; int nexps; expdesc e; - do { - vidx = new_localvar(ls, str_checkname(ls)); - kind = getlocalattribute(ls); - getlocalvardesc(fs, vidx)->vd.kind = kind; + /* get prefixed attribute (if any); default is regular local variable */ + lu_byte defkind = getvarattribute(ls, VDKREG); + do { /* for each variable */ + TString *vname = str_checkname(ls); /* get its name */ + lu_byte kind = getvarattribute(ls, defkind); /* postfixed attribute */ + vidx = new_varkind(ls, vname, kind); /* predeclare it */ if (kind == RDKTOCLOSE) { /* to-be-closed? */ if (toclose != -1) /* one already present? */ luaK_semerror(ls, "multiple to-be-closed variables in local list"); @@ -1743,13 +1837,13 @@ static void localstat (LexState *ls) { } nvars++; } while (testnext(ls, ',')); - if (testnext(ls, '=')) + if (testnext(ls, '=')) /* initialization? */ nexps = explist(ls, &e); else { e.k = VVOID; nexps = 0; } - var = getlocalvardesc(fs, vidx); /* get last variable */ + var = getlocalvardesc(fs, vidx); /* retrieve last variable */ if (nvars == nexps && /* no adjustments? */ var->vd.kind == RDKCONST && /* last variable is const? */ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ @@ -1765,6 +1859,116 @@ static void localstat (LexState *ls) { } +static lu_byte getglobalattribute (LexState *ls, lu_byte df) { + lu_byte kind = getvarattribute(ls, df); + switch (kind) { + case RDKTOCLOSE: + luaK_semerror(ls, "global variables cannot be to-be-closed"); + return kind; /* to avoid warnings */ + case RDKCONST: + return GDKCONST; /* adjust kind for global variable */ + default: + return kind; + } +} + + +static void checkglobal (LexState *ls, TString *varname, int line) { + FuncState *fs = ls->fs; + expdesc var; + int k; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + k = var.u.ind.keystr; /* index of global name in 'k' */ + luaK_codecheckglobal(fs, &var, k, line); +} + + +/* +** Recursively traverse list of globals to be initalized. When +** going, generate table description for the global. In the end, +** after all indices have been generated, read list of initializing +** expressions. When returning, generate the assignment of the value on +** the stack to the corresponding table description. 'n' is the variable +** being handled, range [0, nvars - 1]. +*/ +static void initglobal (LexState *ls, int nvars, int firstidx, int n, + int line) { + if (n == nvars) { /* traversed all variables? */ + expdesc e; + int nexps = explist(ls, &e); /* read list of expressions */ + adjust_assign(ls, nvars, nexps, &e); + } + else { /* handle variable 'n' */ + FuncState *fs = ls->fs; + expdesc var; + TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + enterlevel(ls); /* control recursion depth */ + initglobal(ls, nvars, firstidx, n + 1, line); + leavelevel(ls); + checkglobal(ls, varname, line); + storevartop(fs, &var); + } +} + + +static void globalnames (LexState *ls, lu_byte defkind) { + FuncState *fs = ls->fs; + int nvars = 0; + int lastidx; /* index of last registered variable */ + do { /* for each name */ + TString *vname = str_checkname(ls); + lu_byte kind = getglobalattribute(ls, defkind); + lastidx = new_varkind(ls, vname, kind); + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) /* initialization? */ + initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber); + fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */ +} + + +static void globalstat (LexState *ls) { + /* globalstat -> (GLOBAL) attrib '*' + globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */ + FuncState *fs = ls->fs; + /* get prefixed attribute (if any); default is regular global variable */ + lu_byte defkind = getglobalattribute(ls, GDKREG); + if (!testnext(ls, '*')) + globalnames(ls, defkind); + else { + /* use NULL as name to represent '*' entries */ + new_varkind(ls, NULL, defkind); + fs->nactvar++; /* activate declaration */ + } +} + + +static void globalfunc (LexState *ls, int line) { + /* globalfunc -> (GLOBAL FUNCTION) NAME body */ + expdesc var, b; + FuncState *fs = ls->fs; + TString *fname = str_checkname(ls); + new_varkind(ls, fname, GDKREG); /* declare global variable */ + fs->nactvar++; /* enter its scope */ + buildglobal(ls, fname, &var); + body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ + checkglobal(ls, fname, line); + luaK_storevar(fs, &var, &b); + luaK_fixline(fs, line); /* definition "happens" in the first line */ +} + + +static void globalstatfunc (LexState *ls, int line) { + /* stat -> GLOBAL globalfunc | GLOBAL globalstat */ + luaX_next(ls); /* skip 'global' */ + if (testnext(ls, TK_FUNCTION)) + globalfunc(ls, line); + else + globalstat(ls); +} + + static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; @@ -1785,8 +1989,8 @@ static void funcstat (LexState *ls, int line) { expdesc v, b; luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); - body(ls, &b, ismethod, line); check_readonly(ls, &v); + body(ls, &b, ismethod, line); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } @@ -1884,6 +2088,10 @@ static void statement (LexState *ls) { localstat(ls); break; } + case TK_GLOBAL: { /* stat -> globalstatfunc */ + globalstatfunc(ls, line); + break; + } case TK_DBCOLON: { /* stat -> label */ luaX_next(ls); /* skip double colon */ labelstat(ls, str_checkname(ls), line); @@ -1895,14 +2103,30 @@ static void statement (LexState *ls) { break; } case TK_BREAK: { /* stat -> breakstat */ - breakstat(ls); + breakstat(ls, line); break; } case TK_GOTO: { /* stat -> 'goto' NAME */ luaX_next(ls); /* skip 'goto' */ - gotostat(ls); + gotostat(ls, line); break; } +#if defined(LUA_COMPAT_GLOBAL) + case TK_NAME: { + /* compatibility code to parse global keyword when "global" + is not reserved */ + if (ls->t.seminfo.ts == ls->glbn) { /* current = "global"? */ + int lk = luaX_lookahead(ls); + if (lk == '<' || lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) { + /* 'global ' or 'global name' or 'global *' or + 'global function' */ + globalstatfunc(ls, line); + break; + } + } /* else... */ + } +#endif + /* FALLTHROUGH */ default: { /* stat -> func | assignment */ exprstat(ls); break; @@ -1916,6 +2140,8 @@ static void statement (LexState *ls) { /* }====================================================================== */ +/* }====================================================================== */ + /* ** compiles the main function, which is a regular vararg function with an @@ -1925,7 +2151,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; Upvaldesc *env; open_func(ls, fs, &bl); - setvararg(fs, 0); /* main function is always declared vararg */ + setvararg(fs); /* main function is always vararg */ env = allocupvalue(fs); /* ...set environment upvalue */ env->instack = 1; env->idx = 0; diff --git a/lua/src/lparser.h b/lua/src/lparser.h index 5e4500f..a30df04 100644 --- a/lua/src/lparser.h +++ b/lua/src/lparser.h @@ -32,26 +32,36 @@ typedef enum { VKFLT, /* floating constant; nval = numerical float value */ VKINT, /* integer constant; ival = numerical integer value */ VKSTR, /* string constant; strval = TString address; - (string is fixed by the lexer) */ + (string is fixed by the scanner) */ VNONRELOC, /* expression has its value in a fixed register; info = result register */ VLOCAL, /* local variable; var.ridx = register index; var.vidx = relative index in 'actvar.arr' */ + VVARGVAR, /* vararg parameter; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VGLOBAL, /* global variable; + info = relative index in 'actvar.arr' (or -1 for + implicit declaration) */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VCONST, /* compile-time variable; info = absolute index in 'actvar.arr' */ VINDEXED, /* indexed variable; ind.t = table register; - ind.idx = key's R index */ + ind.idx = key's R index; + ind.ro = true if it represents a read-only global; + ind.keystr = if key is a string, index in 'k' of that string; + -1 if key is not a string */ + VVARGIND, /* indexed vararg parameter; + ind.* as in VINDEXED */ VINDEXUP, /* indexed upvalue; - ind.t = table upvalue; - ind.idx = key's K index */ + ind.idx = key's K index; + ind.* as in VINDEXED */ VINDEXI, /* indexed variable with constant integer; ind.t = table register; ind.idx = key's value */ VINDEXSTR, /* indexed variable with literal string; - ind.t = table register; - ind.idx = key's K index */ + ind.idx = key's K index; + ind.* as in VINDEXED */ VJMP, /* expression is a test/comparison; info = pc of corresponding jump instruction */ VRELOC, /* expression can put result in any register; @@ -75,10 +85,12 @@ typedef struct expdesc { struct { /* for indexed variables */ short idx; /* index (R or "long" K) */ lu_byte t; /* table (register or upvalue) */ + lu_byte ro; /* true if variable is read-only */ + int keystr; /* index in 'k' of string key, or -1 if not a string */ } ind; struct { /* for local variables */ lu_byte ridx; /* register holding the variable */ - unsigned short vidx; /* compiler index (in 'actvar.arr') */ + short vidx; /* index in 'actvar.arr' */ } var; } u; int t; /* patch list of 'exit when true' */ @@ -87,12 +99,22 @@ typedef struct expdesc { /* kinds of variables */ -#define VDKREG 0 /* regular */ -#define RDKCONST 1 /* constant */ -#define RDKTOCLOSE 2 /* to-be-closed */ -#define RDKCTC 3 /* compile-time constant */ +#define VDKREG 0 /* regular local */ +#define RDKCONST 1 /* local constant */ +#define RDKVAVAR 2 /* vararg parameter */ +#define RDKTOCLOSE 3 /* to-be-closed */ +#define RDKCTC 4 /* local compile-time constant */ +#define GDKREG 5 /* regular global */ +#define GDKCONST 6 /* global constant */ + +/* variables that live in registers */ +#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE) + +/* test for global variables */ +#define varglobal(v) ((v)->vd.kind >= GDKREG) + -/* description of an active local variable */ +/* description of an active variable */ typedef union Vardesc { struct { TValuefields; /* constant value (if it is a compile-time constant) */ @@ -111,8 +133,8 @@ typedef struct Labeldesc { TString *name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ - lu_byte nactvar; /* number of active variables in that position */ - lu_byte close; /* goto that escapes upvalues */ + short nactvar; /* number of active variables in that position */ + lu_byte close; /* true for goto that escapes upvalues */ } Labeldesc; @@ -146,6 +168,7 @@ typedef struct FuncState { struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ + Table *kcache; /* cache for reusing constants */ int pc; /* next position to code (equivalent to 'ncode') */ int lasttarget; /* 'label' of last 'jump label' */ int previousline; /* last line that was saved in 'lineinfo' */ @@ -155,7 +178,7 @@ typedef struct FuncState { int firstlocal; /* index of first local var (in Dyndata array) */ int firstlabel; /* index of first label (in 'dyd->label->arr') */ short ndebugvars; /* number of elements in 'f->locvars' */ - lu_byte nactvar; /* number of active local variables */ + short nactvar; /* number of active variable declarations */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ lu_byte iwthabs; /* instructions issued since last absolute line info */ @@ -163,7 +186,9 @@ typedef struct FuncState { } FuncState; -LUAI_FUNC int luaY_nvarstack (FuncState *fs); +LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs); +LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l, + const char *what); LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar); diff --git a/lua/src/lstate.c b/lua/src/lstate.c index 7fefacb..70a11aa 100644 --- a/lua/src/lstate.c +++ b/lua/src/lstate.c @@ -29,79 +29,45 @@ -/* -** thread state + extra space -*/ -typedef struct LX { - lu_byte extra_[LUA_EXTRASPACE]; - lua_State l; -} LX; - - -/* -** Main thread combines a thread state and the global state -*/ -typedef struct LG { - LX l; - global_State g; -} LG; - - - #define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) /* -** A macro to create a "random" seed when a state is created; -** the seed is used to randomize string hashes. +** these macros allow user-specific actions when a thread is +** created/deleted */ -#if !defined(luai_makeseed) +#if !defined(luai_userstateopen) +#define luai_userstateopen(L) ((void)L) +#endif -#include +#if !defined(luai_userstateclose) +#define luai_userstateclose(L) ((void)L) +#endif -/* -** Compute an initial seed with some level of randomness. -** Rely on Address Space Layout Randomization (if present) and -** current time. -*/ -#define addbuff(b,p,e) \ - { size_t t = cast_sizet(e); \ - memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } - -static unsigned int luai_makeseed (lua_State *L) { - char buff[3 * sizeof(size_t)]; - unsigned int h = cast_uint(time(NULL)); - int p = 0; - addbuff(buff, p, L); /* heap variable */ - addbuff(buff, p, &h); /* local variable */ - addbuff(buff, p, &lua_newstate); /* public function */ - lua_assert(p == sizeof(buff)); - return luaS_hash(buff, p, h); -} +#if !defined(luai_userstatethread) +#define luai_userstatethread(L,L1) ((void)L) +#endif +#if !defined(luai_userstatefree) +#define luai_userstatefree(L,L1) ((void)L) #endif /* -** set GCdebt to a new value keeping the value (totalbytes + GCdebt) -** invariant (and avoiding underflows in 'totalbytes') +** set GCdebt to a new value keeping the real number of allocated +** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in +** 'GCtotalobjs'. */ void luaE_setdebt (global_State *g, l_mem debt) { l_mem tb = gettotalbytes(g); lua_assert(tb > 0); - if (debt < tb - MAX_LMEM) - debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ - g->totalbytes = tb - debt; + if (debt > MAX_LMEM - tb) + debt = MAX_LMEM - tb; /* will make GCtotalbytes == MAX_LMEM */ + g->GCtotalbytes = tb + debt; g->GCdebt = debt; } -LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { - UNUSED(L); UNUSED(limit); - return LUAI_MAXCCALLS; /* warning?? */ -} - - CallInfo *luaE_extendCI (lua_State *L) { CallInfo *ci; lua_assert(L->ci->next == NULL); @@ -166,7 +132,7 @@ void luaE_checkcstack (lua_State *L) { if (getCcalls(L) == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) - luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ + luaD_errerr(L); /* error while handling stack error */ } @@ -177,26 +143,29 @@ LUAI_FUNC void luaE_incCstack (lua_State *L) { } +static void resetCI (lua_State *L) { + CallInfo *ci = L->ci = &L->base_ci; + ci->func.p = L->stack.p; + setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */ + ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ + ci->u.c.k = NULL; + ci->callstatus = CIST_C; + L->status = LUA_OK; + L->errfunc = 0; /* stack unwind can "throw away" the error function */ +} + + static void stack_init (lua_State *L1, lua_State *L) { - int i; CallInfo *ci; + int i; /* initialize stack array */ L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->tbclist.p = L1->stack.p; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ - L1->top.p = L1->stack.p; L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; /* initialize first ci */ - ci = &L1->base_ci; - ci->next = ci->previous = NULL; - ci->callstatus = CIST_C; - ci->func.p = L1->top.p; - ci->u.c.k = NULL; - ci->nresults = 0; - setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */ - L1->top.p++; - ci->top.p = L1->top.p + LUA_MINSTACK; - L1->ci = ci; + resetCI(L1); + L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */ } @@ -206,7 +175,8 @@ static void freestack (lua_State *L) { L->ci = &L->base_ci; /* free the entire 'ci' list */ freeCI(L); lua_assert(L->nci == 0); - luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */ + /* free stack */ + luaM_freearray(L, L->stack.p, cast_sizet(stacksize(L) + EXTRA_STACK)); } @@ -215,13 +185,19 @@ static void freestack (lua_State *L) { */ static void init_registry (lua_State *L, global_State *g) { /* create registry */ + TValue aux; Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[1] = false */ + setbfvalue(&aux); + luaH_setint(L, registry, 1, &aux); /* registry[LUA_RIDX_MAINTHREAD] = L */ - setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L); + setthvalue(L, &aux, L); + luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux); /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */ - sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L)); + sethvalue(L, &aux, luaH_new(L)); + luaH_setint(L, registry, LUA_RIDX_GLOBALS, &aux); } @@ -263,6 +239,16 @@ static void preinit_thread (lua_State *L, global_State *g) { L->status = LUA_OK; L->errfunc = 0; L->oldpc = 0; + L->base_ci.previous = L->base_ci.next = NULL; +} + + +lu_mem luaE_threadsize (lua_State *L) { + lu_mem sz = cast(lu_mem, sizeof(LX)) + + cast_uint(L->nci) * sizeof(CallInfo); + if (L->stack.p != NULL) + sz += cast_uint(stacksize(L) + EXTRA_STACK) * sizeof(StackValue); + return sz; } @@ -271,15 +257,16 @@ static void close_state (lua_State *L) { if (!completestate(g)) /* closing a partially built state? */ luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ - L->ci = &L->base_ci; /* unwind CallInfo list */ + resetCI(L); luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); } - luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); + luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size)); freestack(L); - lua_assert(gettotalbytes(g) == sizeof(LG)); - (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ + lua_assert(gettotalbytes(g) == sizeof(global_State)); + (*g->frealloc)(g->ud, g, sizeof(global_State), 0); /* free main block */ } @@ -301,7 +288,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { L1->hook = L->hook; resethookcount(L1); /* initialize L1 extra space */ - memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), + memcpy(lua_getextraspace(L1), lua_getextraspace(mainthread(g)), LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ @@ -320,51 +307,39 @@ void luaE_freethread (lua_State *L, lua_State *L1) { } -int luaE_resetthread (lua_State *L, int status) { - CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ - setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */ - ci->func.p = L->stack.p; - ci->callstatus = CIST_C; +TStatus luaE_resetthread (lua_State *L, TStatus status) { + resetCI(L); if (status == LUA_YIELD) status = LUA_OK; - L->status = LUA_OK; /* so it can run __close metamethods */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ luaD_seterrorobj(L, status, L->stack.p + 1); else L->top.p = L->stack.p + 1; - ci->top.p = L->top.p + LUA_MINSTACK; - luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0); + luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0); return status; } LUA_API int lua_closethread (lua_State *L, lua_State *from) { - int status; + TStatus status; lua_lock(L); L->nCcalls = (from) ? getCcalls(from) : 0; status = luaE_resetthread(L, L->status); + if (L == from) /* closing itself? */ + luaD_throwbaselevel(L, status); lua_unlock(L); - return status; -} - - -/* -** Deprecated! Use 'lua_closethread' instead. -*/ -LUA_API int lua_resetthread (lua_State *L) { - return lua_closethread(L, NULL); + return APIstatus(status); } -LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { int i; lua_State *L; - global_State *g; - LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); - if (l == NULL) return NULL; - L = &l->l.l; - g = &l->g; + global_State *g = cast(global_State*, + (*f)(ud, NULL, LUA_TTHREAD, sizeof(global_State))); + if (g == NULL) return NULL; + L = &g->mainth.l; L->tt = LUA_VTHREAD; g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); @@ -376,8 +351,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->ud = ud; g->warnf = NULL; g->ud_warn = NULL; - g->mainthread = L; - g->seed = luai_makeseed(L); + g->seed = seed; g->gcstp = GCSTPGC; /* no GC while building state */ g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; @@ -394,16 +368,17 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; g->twups = NULL; - g->totalbytes = sizeof(LG); + g->GCtotalbytes = sizeof(global_State); + g->GCmarked = 0; g->GCdebt = 0; - g->lastatomic = 0; setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ - setgcparam(g->gcpause, LUAI_GCPAUSE); - setgcparam(g->gcstepmul, LUAI_GCMUL); - g->gcstepsize = LUAI_GCSTEPSIZE; - setgcparam(g->genmajormul, LUAI_GENMAJORMUL); - g->genminormul = LUAI_GENMINORMUL; - for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; + setgcparam(g, PAUSE, LUAI_GCPAUSE); + setgcparam(g, STEPMUL, LUAI_GCMUL); + setgcparam(g, STEPSIZE, LUAI_GCSTEPSIZE); + setgcparam(g, MINORMUL, LUAI_GENMINORMUL); + setgcparam(g, MINORMAJOR, LUAI_MINORMAJOR); + setgcparam(g, MAJORMINOR, LUAI_MAJORMINOR); + for (i=0; i < LUA_NUMTYPES; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ close_state(L); @@ -415,7 +390,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { LUA_API void lua_close (lua_State *L) { lua_lock(L); - L = G(L)->mainthread; /* only the main thread can be closed */ + L = mainthread(G(L)); /* only the main thread can be closed */ close_state(L); } diff --git a/lua/src/lstate.h b/lua/src/lstate.h index 007704c..20dc4d2 100644 --- a/lua/src/lstate.h +++ b/lua/src/lstate.h @@ -85,7 +85,7 @@ typedef struct CallInfo CallInfo; ** they must be visited again at the end of the cycle), but they are ** marked black because assignments to them must activate barriers (to ** move them back to TOUCHED1). -** - Open upvales are kept gray to avoid barriers, but they stay out +** - Open upvalues are kept gray to avoid barriers, but they stay out ** of gray lists. (They don't even have a 'gclist' field.) */ @@ -142,6 +142,17 @@ struct lua_longjmp; /* defined in ldo.c */ #define EXTRA_STACK 5 +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set. +** (M == 1 makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 +#endif + + #define BASIC_STACK_SIZE (2*LUA_MINSTACK) #define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) @@ -149,13 +160,14 @@ struct lua_longjmp; /* defined in ldo.c */ /* kinds of Garbage Collection */ #define KGC_INC 0 /* incremental gc */ -#define KGC_GEN 1 /* generational gc */ +#define KGC_GENMINOR 1 /* generational gc in minor (regular) mode */ +#define KGC_GENMAJOR 2 /* generational in major mode */ typedef struct stringtable { - TString **hash; + TString **hash; /* array of buckets (linked lists of strings) */ int nuse; /* number of elements */ - int size; + int size; /* number of buckets */ } stringtable; @@ -171,12 +183,10 @@ typedef struct stringtable { ** yield (from the yield until the next resume); ** - field 'nres' is used only while closing tbc variables when ** returning from a function; -** - field 'transferinfo' is used only during call/returnhooks, -** before the function starts or after it ends. */ struct CallInfo { StkIdRel func; /* function index in the stack */ - StkIdRel top; /* top for this function */ + StkIdRel top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ @@ -194,36 +204,55 @@ struct CallInfo { int funcidx; /* called-function index */ int nyield; /* number of values yielded */ int nres; /* number of values returned */ - struct { /* info about transferred values (for call/return hooks) */ - unsigned short ftransfer; /* offset of first value transferred */ - unsigned short ntransfer; /* number of values transferred */ - } transferinfo; } u2; - short nresults; /* expected number of results from this function */ - unsigned short callstatus; + l_uint32 callstatus; }; /* -** Bits in CallInfo status +** Maximum expected number of results from a function +** (must fit in CIST_NRESULTS). */ -#define CIST_OAH (1<<0) /* original value of 'allowhook' */ -#define CIST_C (1<<1) /* call is running a C function */ -#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ -#define CIST_HOOKED (1<<3) /* call is running a debug hook */ -#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ -#define CIST_TAIL (1<<5) /* call was tail called */ -#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ -#define CIST_FIN (1<<7) /* function "called" a finalizer */ -#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ -#define CIST_CLSRET (1<<9) /* function is closing tbc variables */ -/* Bits 10-12 are used for CIST_RECST (see below) */ -#define CIST_RECST 10 -#if defined(LUA_COMPAT_LT_LE) -#define CIST_LEQ (1<<13) /* using __lt for __le */ -#endif +#define MAXRESULTS 250 +/* +** Bits in CallInfo status +*/ +/* bits 0-7 are the expected number of results from this function + 1 */ +#define CIST_NRESULTS 0xffu + +/* bits 8-11 count call metamethods (and their extra arguments) */ +#define CIST_CCMT 8 /* the offset, not the mask */ +#define MAX_CCMT (0xfu << CIST_CCMT) + +/* Bits 12-14 are used for CIST_RECST (see below) */ +#define CIST_RECST 12 /* the offset, not the mask */ + +/* call is running a C function (still in first 16 bits) */ +#define CIST_C (1u << (CIST_RECST + 3)) +/* call is on a fresh "luaV_execute" frame */ +#define CIST_FRESH (cast(l_uint32, CIST_C) << 1) +/* function is closing tbc variables */ +#define CIST_CLSRET (CIST_FRESH << 1) +/* function has tbc variables to close */ +#define CIST_TBC (CIST_CLSRET << 1) +/* original value of 'allowhook' */ +#define CIST_OAH (CIST_TBC << 1) +/* call is running a debug hook */ +#define CIST_HOOKED (CIST_OAH << 1) +/* doing a yieldable protected call */ +#define CIST_YPCALL (CIST_HOOKED << 1) +/* call was tail called */ +#define CIST_TAIL (CIST_YPCALL << 1) +/* last hook called yielded */ +#define CIST_HOOKYIELD (CIST_TAIL << 1) +/* function "called" a finalizer */ +#define CIST_FIN (CIST_HOOKYIELD << 1) + + +#define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1) + /* ** Field CIST_RECST stores the "recover status", used to keep the error ** status while closing to-be-closed variables in coroutines, so that @@ -233,8 +262,8 @@ struct CallInfo { #define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7) #define setcistrecst(ci,st) \ check_exp(((st) & 7) == (st), /* status must fit in three bits */ \ - ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \ - | ((st) << CIST_RECST))) + ((ci)->callstatus = ((ci)->callstatus & ~(7u << CIST_RECST)) \ + | (cast(l_uint32, st) << CIST_RECST))) /* active function is a Lua function */ @@ -243,9 +272,53 @@ struct CallInfo { /* call is running Lua code (not a hook) */ #define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) -/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ -#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) -#define getoah(st) ((st) & CIST_OAH) + +#define setoah(ci,v) \ + ((ci)->callstatus = ((v) ? (ci)->callstatus | CIST_OAH \ + : (ci)->callstatus & ~CIST_OAH)) +#define getoah(ci) (((ci)->callstatus & CIST_OAH) ? 1 : 0) + + +/* +** 'per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte allowhook; + TStatus status; + StkIdRel top; /* first free slot in the stack */ + struct global_State *l_G; + CallInfo *ci; /* call info for current function */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + StkIdRel tbclist; /* list of to-be-closed variables */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C host) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of nested non-yieldable or C calls */ + int oldpc; /* last pc traced */ + int nci; /* number of items in 'ci' list */ + int basehookcount; + int hookcount; + volatile l_signalT hookmask; + struct { /* info about transferred values (for call/return hooks) */ + int ftransfer; /* offset of first value transferred */ + int ntransfer; /* number of values transferred */ + } transferinfo; +}; + + +/* +** thread state + extra space +*/ +typedef struct LX { + lu_byte extra_[LUA_EXTRASPACE]; + lua_State l; +} LX; /* @@ -254,25 +327,21 @@ struct CallInfo { typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ - l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ - l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ - lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ - lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ + l_mem GCtotalbytes; /* number of bytes currently allocated + debt */ + l_mem GCdebt; /* bytes counted but not yet allocated */ + l_mem GCmarked; /* number of objects marked in a GC cycle */ + l_mem GCmajorminor; /* auxiliary counter to control major-minor shifts */ stringtable strt; /* hash table for strings */ TValue l_registry; TValue nilvalue; /* a nil value */ unsigned int seed; /* randomized seed for hashes */ + lu_byte gcparams[LUA_GCPN]; lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcstopem; /* stops emergency collections */ - lu_byte genminormul; /* control for minor generational collections */ - lu_byte genmajormul; /* control for major generational collections */ lu_byte gcstp; /* control whether GC is running */ lu_byte gcemergency; /* true if this is an emergency collection */ - lu_byte gcpause; /* size of pause between successive GCs */ - lu_byte gcstepmul; /* GC "speed" */ - lu_byte gcstepsize; /* (log2 of) GC granularity */ GCObject *allgc; /* list of all collectable objects */ GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ @@ -293,46 +362,18 @@ typedef struct global_State { GCObject *finobjrold; /* list of really old objects with finalizers */ struct lua_State *twups; /* list of threads with open upvalues */ lua_CFunction panic; /* to be called in unprotected errors */ - struct lua_State *mainthread; TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ + LX mainth; /* main thread of this state */ } global_State; -/* -** 'per thread' state -*/ -struct lua_State { - CommonHeader; - lu_byte status; - lu_byte allowhook; - unsigned short nci; /* number of items in 'ci' list */ - StkIdRel top; /* first free slot in the stack */ - global_State *l_G; - CallInfo *ci; /* call info for current function */ - StkIdRel stack_last; /* end of stack (last element + 1) */ - StkIdRel stack; /* stack base */ - UpVal *openupval; /* list of open upvalues in this stack */ - StkIdRel tbclist; /* list of to-be-closed variables */ - GCObject *gclist; - struct lua_State *twups; /* list of threads with open upvalues */ - struct lua_longjmp *errorJmp; /* current error recover point */ - CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ - volatile lua_Hook hook; - ptrdiff_t errfunc; /* current error handling function (stack index) */ - l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ - int oldpc; /* last pc traced */ - int basehookcount; - int hookcount; - volatile l_signalT hookmask; -}; - - #define G(L) (L->l_G) +#define mainthread(G) (&(G)->mainth.l) /* ** 'g->nilvalue' being a nil value flags that the state was completely @@ -385,23 +426,25 @@ union GCUnion { /* ** macro to convert a Lua object into a GCObject -** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) */ -#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) +#define obj2gco(v) \ + check_exp(novariant((v)->tt) >= LUA_TSTRING, &(cast_u(v)->gc)) + +/* actual number of total memory allocated */ +#define gettotalbytes(g) ((g)->GCtotalbytes - (g)->GCdebt) -/* actual number of total bytes allocated */ -#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); +LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); -LUAI_FUNC int luaE_resetthread (lua_State *L, int status); +LUAI_FUNC TStatus luaE_resetthread (lua_State *L, TStatus status); #endif diff --git a/lua/src/lstring.c b/lua/src/lstring.c index 9775735..7563514 100644 --- a/lua/src/lstring.c +++ b/lua/src/lstring.c @@ -25,22 +25,32 @@ /* ** Maximum size for string table. */ -#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*)) +#define MAXSTRTB cast_int(luaM_limitN(INT_MAX, TString*)) + +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ +#if !defined(MINSTRTABSIZE) +#define MINSTRTABSIZE 128 +#endif /* -** equality for long strings +** generic equality for strings */ -int luaS_eqlngstr (TString *a, TString *b) { - size_t len = a->u.lnglen; - lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); - return (a == b) || /* same instance or... */ - ((len == b->u.lnglen) && /* equal length and ... */ - (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */ +int luaS_eqstr (TString *a, TString *b) { + size_t len1, len2; + const char *s1 = getlstr(a, len1); + const char *s2 = getlstr(b, len2); + return ((len1 == len2) && /* equal length and ... */ + (memcmp(s1, s2, len1) == 0)); /* equal contents */ } -unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { +static unsigned luaS_hash (const char *str, size_t l, unsigned seed) { unsigned int h = seed ^ cast_uint(l); for (; l > 0; l--) h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); @@ -48,7 +58,7 @@ unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { } -unsigned int luaS_hashlongstr (TString *ts) { +unsigned luaS_hashlongstr (TString *ts) { lua_assert(ts->tt == LUA_VLNGSTR); if (ts->extra == 0) { /* no hash? */ size_t len = ts->u.lnglen; @@ -136,28 +146,43 @@ void luaS_init (lua_State *L) { } +size_t luaS_sizelngstr (size_t len, int kind) { + switch (kind) { + case LSTRREG: /* regular long string */ + /* don't need 'falloc'/'ud', but need space for content */ + return offsetof(TString, falloc) + (len + 1) * sizeof(char); + case LSTRFIX: /* fixed external long string */ + /* don't need 'falloc'/'ud' */ + return offsetof(TString, falloc); + default: /* external long string with deallocation */ + lua_assert(kind == LSTRMEM); + return sizeof(TString); + } +} + /* ** creates a new string object */ -static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { +static TString *createstrobj (lua_State *L, size_t totalsize, lu_byte tag, + unsigned h) { TString *ts; GCObject *o; - size_t totalsize; /* total size of TString object */ - totalsize = sizelstring(l); o = luaC_newobj(L, tag, totalsize); ts = gco2ts(o); ts->hash = h; ts->extra = 0; - getstr(ts)[l] = '\0'; /* ending 0 */ return ts; } TString *luaS_createlngstrobj (lua_State *L, size_t l) { - TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); + size_t totalsize = luaS_sizelngstr(l, LSTRREG); + TString *ts = createstrobj(L, totalsize, LUA_VLNGSTR, G(L)->seed); ts->u.lnglen = l; - ts->shrlen = 0xFF; /* signals that it is a long string */ + ts->shrlen = LSTRREG; /* signals that it is a regular long string */ + ts->contents = cast_charp(ts) + offsetof(TString, falloc); + ts->contents[l] = '\0'; /* ending 0 */ return ts; } @@ -173,9 +198,9 @@ void luaS_remove (lua_State *L, TString *ts) { static void growstrtab (lua_State *L, stringtable *tb) { - if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ + if (l_unlikely(tb->nuse == INT_MAX)) { /* too many strings? */ luaC_fullgc(L, 1); /* try to free some... */ - if (tb->nuse == MAX_INT) /* still too many? */ + if (tb->nuse == INT_MAX) /* still too many? */ luaM_error(L); /* cannot even create a message... */ } if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ @@ -194,7 +219,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { TString **list = &tb->hash[lmod(h, tb->size)]; lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ for (ts = *list; ts != NULL; ts = ts->u.hnext) { - if (l == ts->shrlen && (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) { + if (l == cast_uint(ts->shrlen) && + (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) { /* found! */ if (isdead(g, ts)) /* dead (but not collected yet)? */ changewhite(ts); /* resurrect it */ @@ -206,8 +232,9 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) { growstrtab(L, tb); list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ } - ts = createstrobj(L, l, LUA_VSHRSTR, h); - ts->shrlen = cast_byte(l); + ts = createstrobj(L, sizestrshr(l), LUA_VSHRSTR, h); + ts->shrlen = cast(ls_byte, l); + getshrstr(ts)[l] = '\0'; /* ending 0 */ memcpy(getshrstr(ts), str, l * sizeof(char)); ts->u.hnext = *list; *list = ts; @@ -256,7 +283,7 @@ TString *luaS_new (lua_State *L, const char *str) { } -Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { +Udata *luaS_newudata (lua_State *L, size_t s, unsigned short nuvalue) { Udata *u; int i; GCObject *o; @@ -272,3 +299,55 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { return u; } + +struct NewExt { + ls_byte kind; + const char *s; + size_t len; + TString *ts; /* output */ +}; + + +static void f_newext (lua_State *L, void *ud) { + struct NewExt *ne = cast(struct NewExt *, ud); + size_t size = luaS_sizelngstr(0, ne->kind); + ne->ts = createstrobj(L, size, LUA_VLNGSTR, G(L)->seed); +} + + +TString *luaS_newextlstr (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud) { + struct NewExt ne; + if (!falloc) { + ne.kind = LSTRFIX; + f_newext(L, &ne); /* just create header */ + } + else { + ne.kind = LSTRMEM; + if (luaD_rawrunprotected(L, f_newext, &ne) != LUA_OK) { /* mem. error? */ + (*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */ + luaM_error(L); /* re-raise memory error */ + } + ne.ts->falloc = falloc; + ne.ts->ud = ud; + } + ne.ts->shrlen = ne.kind; + ne.ts->u.lnglen = len; + ne.ts->contents = cast_charp(s); + return ne.ts; +} + + +/* +** Normalize an external string: If it is short, internalize it. +*/ +TString *luaS_normstr (lua_State *L, TString *ts) { + size_t len = ts->u.lnglen; + if (len > LUAI_MAXSHORTLEN) + return ts; /* long string; keep the original */ + else { + const char *str = getlngstr(ts); + return internshrstr(L, str, len); + } +} + diff --git a/lua/src/lstring.h b/lua/src/lstring.h index 450c239..1643c3d 100644 --- a/lua/src/lstring.h +++ b/lua/src/lstring.h @@ -20,10 +20,23 @@ /* -** Size of a TString: Size of the header plus space for the string +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif + + +/* +** Size of a short TString: Size of the header plus space for the string ** itself (including final '\0'). */ -#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) +#define sizestrshr(l) \ + (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) + #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ (sizeof(s)/sizeof(char))-1)) @@ -32,7 +45,7 @@ /* ** test whether a string is a reserved word */ -#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0) +#define isreserved(s) (strisshr(s) && (s)->extra > 0) /* @@ -41,17 +54,20 @@ #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) -LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); -LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); -LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); +LUAI_FUNC unsigned luaS_hashlongstr (TString *ts); +LUAI_FUNC int luaS_eqstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_init (lua_State *L); LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); -LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, + unsigned short nuvalue); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); - +LUAI_FUNC TString *luaS_newextlstr (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud); +LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind); +LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts); #endif diff --git a/lua/src/lstrlib.c b/lua/src/lstrlib.c index 0316716..23df839 100644 --- a/lua/src/lstrlib.c +++ b/lua/src/lstrlib.c @@ -24,6 +24,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" /* @@ -36,22 +37,6 @@ #endif -/* macro to 'unsign' a character */ -#define uchar(c) ((unsigned char)(c)) - - -/* -** Some sizes are better limited to fit in 'int', but must also fit in -** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) -*/ -#define MAX_SIZET ((size_t)(~(size_t)0)) - -#define MAXSIZE \ - (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) - - - - static int str_len (lua_State *L) { size_t l; luaL_checklstring(L, 1, &l); @@ -128,7 +113,7 @@ static int str_lower (lua_State *L) { const char *s = luaL_checklstring(L, 1, &l); char *p = luaL_buffinitsize(L, &b, l); for (i=0; i MAXSIZE / n)) + else if (l_unlikely(len > MAX_SIZE - lsep || + cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n)) return luaL_error(L, "resulting string too large"); else { - size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; + size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ - memcpy(p, s, l * sizeof(char)); p += l; + memcpy(p, s, len * sizeof(char)); p += len; if (lsep > 0) { /* empty 'memcpy' is not that cheap */ - memcpy(p, sep, lsep * sizeof(char)); - p += lsep; + memcpy(p, sep, lsep * sizeof(char)); p += lsep; } } - memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ + memcpy(p, s, len * sizeof(char)); /* last copy without separator */ luaL_pushresultsize(&b, totallen); } return 1; @@ -187,7 +176,7 @@ static int str_byte (lua_State *L) { n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); for (i=0; iinit = 1; luaL_buffinit(L, &state->B); } - luaL_addlstring(&state->B, (const char *)b, size); + if (b == NULL) { /* finishing dump? */ + luaL_pushresult(&state->B); /* push result */ + lua_replace(L, 1); /* move it to reserved slot */ + } + else + luaL_addlstring(&state->B, (const char *)b, size); return 0; } @@ -233,12 +227,13 @@ static int writer (lua_State *L, const void *b, size_t size, void *ud) { static int str_dump (lua_State *L) { struct str_Writer state; int strip = lua_toboolean(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); - lua_settop(L, 1); /* ensure function is on the top of the stack */ + luaL_argcheck(L, lua_type(L, 1) == LUA_TFUNCTION && !lua_iscfunction(L, 1), + 1, "Lua function expected"); + /* ensure function is on the top of the stack and vacate slot 1 */ + lua_pushvalue(L, 1); state.init = 0; - if (l_unlikely(lua_dump(L, writer, &state, strip) != 0)) - return luaL_error(L, "unable to dump given function"); - luaL_pushresult(&state.B); + lua_dump(L, writer, &state, strip); + lua_settop(L, 1); /* leave final result on top */ return 1; } @@ -274,11 +269,18 @@ static int tonum (lua_State *L, int arg) { } -static void trymt (lua_State *L, const char *mtname) { +/* +** To be here, either the first operand was a string or the first +** operand didn't have a corresponding metamethod. (Otherwise, that +** other metamethod would have been called.) So, if this metamethod +** doesn't work, the only other option would be for the second +** operand to have a different metamethod. +*/ +static void trymt (lua_State *L, const char *mtkey, const char *opname) { lua_settop(L, 2); /* back to the original arguments */ if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || - !luaL_getmetafield(L, 2, mtname))) - luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, + !luaL_getmetafield(L, 2, mtkey))) + luaL_error(L, "attempt to %s a '%s' with a '%s'", opname, luaL_typename(L, -2), luaL_typename(L, -1)); lua_insert(L, -3); /* put metamethod before arguments */ lua_call(L, 2, 1); /* call metamethod */ @@ -289,7 +291,7 @@ static int arith (lua_State *L, int op, const char *mtname) { if (tonum(L, 1) && tonum(L, 2)) lua_arith(L, op); /* result will be on the top */ else - trymt(L, mtname); + trymt(L, mtname, mtname + 2); return 1; } @@ -361,10 +363,10 @@ typedef struct MatchState { const char *p_end; /* end ('\0') of pattern */ lua_State *L; int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ - unsigned char level; /* total number of captures (finished or unfinished) */ + int level; /* total number of captures (finished or unfinished) */ struct { const char *init; - ptrdiff_t len; + ptrdiff_t len; /* length or special value (CAP_*) */ } capture[LUA_MAXCAPTURES]; } MatchState; @@ -453,15 +455,15 @@ static int matchbracketclass (int c, const char *p, const char *ec) { while (++p < ec) { if (*p == L_ESC) { p++; - if (match_class(c, uchar(*p))) + if (match_class(c, cast_uchar(*p))) return sig; } else if ((*(p+1) == '-') && (p+2 < ec)) { p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) + if (cast_uchar(*(p-2)) <= c && c <= cast_uchar(*p)) return sig; } - else if (uchar(*p) == c) return sig; + else if (cast_uchar(*p) == c) return sig; } return !sig; } @@ -472,12 +474,12 @@ static int singlematch (MatchState *ms, const char *s, const char *p, if (s >= ms->src_end) return 0; else { - int c = uchar(*s); + int c = cast_uchar(*s); switch (*p) { case '.': return 1; /* matches any char */ - case L_ESC: return match_class(c, uchar(*(p+1))); + case L_ESC: return match_class(c, cast_uchar(*(p+1))); case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); + default: return (cast_uchar(*p) == c); } } } @@ -559,7 +561,7 @@ static const char *end_capture (MatchState *ms, const char *s, static const char *match_capture (MatchState *ms, const char *s, int l) { size_t len; l = check_capture(ms, l); - len = ms->capture[l].len; + len = cast_sizet(ms->capture[l].len); if ((size_t)(ms->src_end-s) >= len && memcmp(ms->capture[l].init, s, len) == 0) return s+len; @@ -606,8 +608,8 @@ static const char *match (MatchState *ms, const char *s, const char *p) { luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); - if (!matchbracketclass(uchar(previous), p, ep - 1) && - matchbracketclass(uchar(*s), p, ep - 1)) { + if (!matchbracketclass(cast_uchar(previous), p, ep - 1) && + matchbracketclass(cast_uchar(*s), p, ep - 1)) { p = ep; goto init; /* return match(ms, s, ep); */ } s = NULL; /* match failed */ @@ -616,7 +618,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* capture results (%0-%9)? */ - s = match_capture(ms, s, uchar(*(p + 1))); + s = match_capture(ms, s, cast_uchar(*(p + 1))); if (s != NULL) { p += 2; goto init; /* return match(ms, s, p + 2) */ } @@ -683,7 +685,7 @@ static const char *lmemfind (const char *s1, size_t l1, if (memcmp(init, s2+1, l2) == 0) return init-1; else { /* correct 'l1' and 's1' to try again */ - l1 -= init-s1; + l1 -= ct_diff2sz(init - s1); s1 = init; } } @@ -699,13 +701,13 @@ static const char *lmemfind (const char *s1, size_t l1, ** its length and put its address in '*cap'. If it is an integer ** (a position), push it on the stack and return CAP_POSITION. */ -static size_t get_onecapture (MatchState *ms, int i, const char *s, +static ptrdiff_t get_onecapture (MatchState *ms, int i, const char *s, const char *e, const char **cap) { if (i >= ms->level) { if (l_unlikely(i != 0)) luaL_error(ms->L, "invalid capture index %%%d", i + 1); *cap = s; - return e - s; + return (e - s); } else { ptrdiff_t capl = ms->capture[i].len; @@ -713,7 +715,8 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s, if (l_unlikely(capl == CAP_UNFINISHED)) luaL_error(ms->L, "unfinished capture"); else if (capl == CAP_POSITION) - lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); + lua_pushinteger(ms->L, + ct_diff2S(ms->capture[i].init - ms->src_init) + 1); return capl; } } @@ -727,7 +730,7 @@ static void push_onecapture (MatchState *ms, int i, const char *s, const char *cap; ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); if (l != CAP_POSITION) - lua_pushlstring(ms->L, cap, l); + lua_pushlstring(ms->L, cap, cast_sizet(l)); /* else position was already pushed */ } @@ -784,8 +787,8 @@ static int str_find_aux (lua_State *L, int find) { /* do a plain search */ const char *s2 = lmemfind(s + init, ls - init, p, lp); if (s2) { - lua_pushinteger(L, (s2 - s) + 1); - lua_pushinteger(L, (s2 - s) + lp); + lua_pushinteger(L, ct_diff2S(s2 - s) + 1); + lua_pushinteger(L, cast_st2S(ct_diff2sz(s2 - s) + lp)); return 2; } } @@ -802,8 +805,8 @@ static int str_find_aux (lua_State *L, int find) { reprepstate(&ms); if ((res=match(&ms, s1, p)) != NULL) { if (find) { - lua_pushinteger(L, (s1 - s) + 1); /* start */ - lua_pushinteger(L, res - s); /* end */ + lua_pushinteger(L, ct_diff2S(s1 - s) + 1); /* start */ + lua_pushinteger(L, ct_diff2S(res - s)); /* end */ return push_captures(&ms, NULL, 0) + 2; } else @@ -875,23 +878,23 @@ static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *news = lua_tolstring(L, 3, &l); const char *p; while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { - luaL_addlstring(b, news, p - news); + luaL_addlstring(b, news, ct_diff2sz(p - news)); p++; /* skip ESC */ if (*p == L_ESC) /* '%%' */ luaL_addchar(b, *p); else if (*p == '0') /* '%0' */ - luaL_addlstring(b, s, e - s); - else if (isdigit(uchar(*p))) { /* '%n' */ + luaL_addlstring(b, s, ct_diff2sz(e - s)); + else if (isdigit(cast_uchar(*p))) { /* '%n' */ const char *cap; ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); if (resl == CAP_POSITION) luaL_addvalue(b); /* add position to accumulated result */ else - luaL_addlstring(b, cap, resl); + luaL_addlstring(b, cap, cast_sizet(resl)); } else luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); - l -= p + 1 - news; + l -= ct_diff2sz(p + 1 - news); news = p + 1; } luaL_addlstring(b, news, l); @@ -926,7 +929,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, } if (!lua_toboolean(L, -1)) { /* nil or false? */ lua_pop(L, 1); /* remove value */ - luaL_addlstring(b, s, e - s); /* keep original text */ + luaL_addlstring(b, s, ct_diff2sz(e - s)); /* keep original text */ return 0; /* no changes */ } else if (l_unlikely(!lua_isstring(L, -1))) @@ -945,7 +948,8 @@ static int str_gsub (lua_State *L) { const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ const char *lastmatch = NULL; /* end of last match */ int tr = lua_type(L, 3); /* replacement type */ - lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ + /* max replacements */ + lua_Integer max_s = luaL_optinteger(L, 4, cast_st2S(srcl) + 1); int anchor = (*p == '^'); lua_Integer n = 0; /* replacement count */ int changed = 0; /* change flag */ @@ -975,7 +979,7 @@ static int str_gsub (lua_State *L) { if (!changed) /* no changes? */ lua_pushvalue(L, 1); /* return original string */ else { /* something changed */ - luaL_addlstring(&b, src, ms.src_end-src); + luaL_addlstring(&b, src, ct_diff2sz(ms.src_end - src)); luaL_pushresult(&b); /* create and return new string */ } lua_pushinteger(L, n); /* number of substitutions */ @@ -1013,15 +1017,15 @@ static int str_gsub (lua_State *L) { /* ** Add integer part of 'x' to buffer and return new 'x' */ -static lua_Number adddigit (char *buff, int n, lua_Number x) { +static lua_Number adddigit (char *buff, unsigned n, lua_Number x) { lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ int d = (int)dd; - buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + buff[n] = cast_char(d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ return x - dd; /* return what is left */ } -static int num2straux (char *buff, int sz, lua_Number x) { +static int num2straux (char *buff, unsigned sz, lua_Number x) { /* if 'inf' or 'NaN', format it like '%g' */ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); @@ -1032,7 +1036,7 @@ static int num2straux (char *buff, int sz, lua_Number x) { else { int e; lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ - int n = 0; /* character count */ + unsigned n = 0; /* character count */ if (m < 0) { /* is number negative? */ buff[n++] = '-'; /* add sign */ m = -m; /* make it positive */ @@ -1046,20 +1050,20 @@ static int num2straux (char *buff, int sz, lua_Number x) { m = adddigit(buff, n++, m * 16); } while (m > 0); } - n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ + n += cast_uint(l_sprintf(buff + n, sz - n, "p%+d", e)); /* add exponent */ lua_assert(n < sz); - return n; + return cast_int(n); } } -static int lua_number2strx (lua_State *L, char *buff, int sz, +static int lua_number2strx (lua_State *L, char *buff, unsigned sz, const char *fmt, lua_Number x) { int n = num2straux(buff, sz, x); if (fmt[SIZELENMOD] == 'A') { int i; for (i = 0; i < n; i++) - buff[i] = toupper(uchar(buff[i])); + buff[i] = cast_char(toupper(cast_uchar(buff[i]))); } else if (l_unlikely(fmt[SIZELENMOD] != 'a')) return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); @@ -1126,12 +1130,12 @@ static void addquoted (luaL_Buffer *b, const char *s, size_t len) { luaL_addchar(b, '\\'); luaL_addchar(b, *s); } - else if (iscntrl(uchar(*s))) { + else if (iscntrl(cast_uchar(*s))) { char buff[10]; - if (!isdigit(uchar(*(s+1)))) - l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); + if (!isdigit(cast_uchar(*(s+1)))) + l_sprintf(buff, sizeof(buff), "\\%d", (int)cast_uchar(*s)); else - l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); + l_sprintf(buff, sizeof(buff), "\\%03d", (int)cast_uchar(*s)); luaL_addstring(b, buff); } else @@ -1160,9 +1164,9 @@ static int quotefloat (lua_State *L, char *buff, lua_Number n) { int nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n); /* ensures that 'buff' string uses a dot as the radix character */ - if (memchr(buff, '.', nb) == NULL) { /* no dot? */ + if (memchr(buff, '.', cast_uint(nb)) == NULL) { /* no dot? */ char point = lua_getlocaledecpoint(); /* try locale point */ - char *ppoint = (char *)memchr(buff, point, nb); + char *ppoint = (char *)memchr(buff, point, cast_uint(nb)); if (ppoint) *ppoint = '.'; /* change it to a dot */ } return nb; @@ -1192,7 +1196,7 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { : LUA_INTEGER_FMT; /* else use default format */ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); } - luaL_addsize(b, nb); + luaL_addsize(b, cast_uint(nb)); break; } case LUA_TNIL: case LUA_TBOOLEAN: { @@ -1208,9 +1212,9 @@ static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { static const char *get2digits (const char *s) { - if (isdigit(uchar(*s))) { + if (isdigit(cast_uchar(*s))) { s++; - if (isdigit(uchar(*s))) s++; /* (2 digits at most) */ + if (isdigit(cast_uchar(*s))) s++; /* (2 digits at most) */ } return s; } @@ -1233,7 +1237,7 @@ static void checkformat (lua_State *L, const char *form, const char *flags, spec = get2digits(spec); /* skip precision */ } } - if (!isalpha(uchar(*spec))) /* did not go to the end? */ + if (!isalpha(cast_uchar(*spec))) /* did not go to the end? */ luaL_error(L, "invalid conversion specification: '%s'", form); } @@ -1286,7 +1290,7 @@ static int str_format (lua_State *L) { luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format ('%...') */ - int maxitem = MAX_ITEM; /* maximum length for the result */ + unsigned maxitem = MAX_ITEM; /* maximum length for the result */ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ int nb = 0; /* number of bytes in result */ if (++arg > top) @@ -1369,8 +1373,8 @@ static int str_format (lua_State *L) { return luaL_error(L, "invalid conversion '%s' to 'format'", form); } } - lua_assert(nb < maxitem); - luaL_addsize(&b, nb); + lua_assert(cast_uint(nb) < maxitem); + luaL_addsize(&b, cast_uint(nb)); } } luaL_pushresult(&b); @@ -1418,7 +1422,7 @@ static const union { typedef struct Header { lua_State *L; int islittle; - int maxalign; + unsigned maxalign; } Header; @@ -1446,14 +1450,14 @@ typedef enum KOption { */ static int digit (int c) { return '0' <= c && c <= '9'; } -static int getnum (const char **fmt, int df) { +static size_t getnum (const char **fmt, size_t df) { if (!digit(**fmt)) /* no number? */ return df; /* return default value */ else { - int a = 0; + size_t a = 0; do { - a = a*10 + (*((*fmt)++) - '0'); - } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); + a = a*10 + cast_uint(*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= (MAX_SIZE - 9)/10); return a; } } @@ -1461,14 +1465,14 @@ static int getnum (const char **fmt, int df) { /* ** Read an integer numeral and raises an error if it is larger -** than the maximum size for integers. +** than the maximum size of integers. */ -static int getnumlimit (Header *h, const char **fmt, int df) { - int sz = getnum(fmt, df); - if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) - return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", - sz, MAXINTSIZE); - return sz; +static unsigned getnumlimit (Header *h, const char **fmt, size_t df) { + size_t sz = getnum(fmt, df); + if (l_unlikely((sz - 1u) >= MAXINTSIZE)) + return cast_uint(luaL_error(h->L, + "integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE)); + return cast_uint(sz); } @@ -1485,7 +1489,7 @@ static void initheader (lua_State *L, Header *h) { /* ** Read and classify next option. 'size' is filled with option's size. */ -static KOption getoption (Header *h, const char **fmt, int *size) { +static KOption getoption (Header *h, const char **fmt, size_t *size) { /* dummy structure to get native alignment requirements */ struct cD { char c; union { LUAI_MAXALIGN; } u; }; int opt = *((*fmt)++); @@ -1507,8 +1511,8 @@ static KOption getoption (Header *h, const char **fmt, int *size) { case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; case 'c': - *size = getnum(fmt, -1); - if (l_unlikely(*size == -1)) + *size = getnum(fmt, cast_sizet(-1)); + if (l_unlikely(*size == cast_sizet(-1))) luaL_error(h->L, "missing size for format option 'c'"); return Kchar; case 'z': return Kzstr; @@ -1519,7 +1523,7 @@ static KOption getoption (Header *h, const char **fmt, int *size) { case '>': h->islittle = 0; break; case '=': h->islittle = nativeendian.little; break; case '!': { - const int maxalign = offsetof(struct cD, u); + const size_t maxalign = offsetof(struct cD, u); h->maxalign = getnumlimit(h, fmt, maxalign); break; } @@ -1538,10 +1542,10 @@ static KOption getoption (Header *h, const char **fmt, int *size) { ** the maximum alignment ('maxalign'). Kchar option needs no alignment ** despite its size. */ -static KOption getdetails (Header *h, size_t totalsize, - const char **fmt, int *psize, int *ntoalign) { +static KOption getdetails (Header *h, size_t totalsize, const char **fmt, + size_t *psize, unsigned *ntoalign) { KOption opt = getoption(h, fmt, psize); - int align = *psize; /* usually, alignment follows size */ + size_t align = *psize; /* usually, alignment follows size */ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) luaL_argerror(h->L, 1, "invalid next option for option 'X'"); @@ -1551,9 +1555,15 @@ static KOption getdetails (Header *h, size_t totalsize, else { if (align > h->maxalign) /* enforce maximum alignment */ align = h->maxalign; - if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ + if (l_unlikely(!ispow2(align))) { /* not a power of 2? */ + *ntoalign = 0; /* to avoid warnings */ luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); - *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); + } + else { + /* 'szmoda' = totalsize % align */ + unsigned szmoda = cast_uint(totalsize & (align - 1)); + *ntoalign = cast_uint((align - szmoda) & (align - 1)); + } } return opt; } @@ -1566,9 +1576,9 @@ static KOption getdetails (Header *h, size_t totalsize, ** bytes if necessary (by default they would be zeros). */ static void packint (luaL_Buffer *b, lua_Unsigned n, - int islittle, int size, int neg) { + int islittle, unsigned size, int neg) { char *buff = luaL_prepbuffsize(b, size); - int i; + unsigned i; buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ for (i = 1; i < size; i++) { n >>= NB; @@ -1587,7 +1597,7 @@ static void packint (luaL_Buffer *b, lua_Unsigned n, ** given 'islittle' is different from native endianness. */ static void copywithendian (char *dest, const char *src, - int size, int islittle) { + unsigned size, int islittle) { if (islittle == nativeendian.little) memcpy(dest, src, size); else { @@ -1608,8 +1618,11 @@ static int str_pack (lua_State *L) { lua_pushnil(L); /* mark to separate arguments from string buffer */ luaL_buffinit(L, &b); while (*fmt != '\0') { - int size, ntoalign; + unsigned ntoalign; + size_t size; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, size + ntoalign <= MAX_SIZE - totalsize, arg, + "result too long"); totalsize += ntoalign + size; while (ntoalign-- > 0) luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ @@ -1621,7 +1634,7 @@ static int str_pack (lua_State *L) { lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); } - packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); + packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), (n < 0)); break; } case Kuint: { /* unsigned integers */ @@ -1629,7 +1642,7 @@ static int str_pack (lua_State *L) { if (size < SZINT) /* need overflow check? */ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), arg, "unsigned overflow"); - packint(&b, (lua_Unsigned)n, h.islittle, size, 0); + packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), 0); break; } case Kfloat: { /* C float */ @@ -1659,20 +1672,24 @@ static int str_pack (lua_State *L) { case Kchar: { /* fixed-size string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); - luaL_argcheck(L, len <= (size_t)size, arg, - "string longer than given size"); + luaL_argcheck(L, len <= size, arg, "string longer than given size"); luaL_addlstring(&b, s, len); /* add string */ - while (len++ < (size_t)size) /* pad extra space */ - luaL_addchar(&b, LUAL_PACKPADBYTE); + if (len < size) { /* does it need padding? */ + size_t psize = size - len; /* pad size */ + char *buff = luaL_prepbuffsize(&b, psize); + memset(buff, LUAL_PACKPADBYTE, psize); + luaL_addsize(&b, psize); + } break; } case Kstring: { /* strings with length count */ size_t len; const char *s = luaL_checklstring(L, arg, &len); - luaL_argcheck(L, size >= (int)sizeof(size_t) || - len < ((size_t)1 << (size * NB)), + luaL_argcheck(L, size >= sizeof(lua_Unsigned) || + len < ((lua_Unsigned)1 << (size * NB)), arg, "string length does not fit in given size"); - packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ + /* pack length */ + packint(&b, (lua_Unsigned)len, h.islittle, cast_uint(size), 0); luaL_addlstring(&b, s, len); totalsize += len; break; @@ -1703,16 +1720,17 @@ static int str_packsize (lua_State *L) { size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); while (*fmt != '\0') { - int size, ntoalign; + unsigned ntoalign; + size_t size; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, "variable-length format"); size += ntoalign; /* total space used by option */ - luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, - "format result too large"); + luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size, + 1, "format result too large"); totalsize += size; } - lua_pushinteger(L, (lua_Integer)totalsize); + lua_pushinteger(L, cast_st2S(totalsize)); return 1; } @@ -1761,9 +1779,10 @@ static int str_unpack (lua_State *L) { luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); initheader(L, &h); while (*fmt != '\0') { - int size, ntoalign; + unsigned ntoalign; + size_t size; KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); - luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, + luaL_argcheck(L, ntoalign + size <= ld - pos, 2, "data string too short"); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ @@ -1772,8 +1791,8 @@ static int str_unpack (lua_State *L) { switch (opt) { case Kint: case Kuint: { - lua_Integer res = unpackint(L, data + pos, h.islittle, size, - (opt == Kint)); + lua_Integer res = unpackint(L, data + pos, h.islittle, + cast_int(size), (opt == Kint)); lua_pushinteger(L, res); break; } @@ -1800,10 +1819,11 @@ static int str_unpack (lua_State *L) { break; } case Kstring: { - size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); + lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos, + h.islittle, cast_int(size), 0); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); - lua_pushlstring(L, data + pos + size, len); - pos += len; /* skip string */ + lua_pushlstring(L, data + pos + size, cast_sizet(len)); + pos += cast_sizet(len); /* skip string */ break; } case Kzstr: { @@ -1820,7 +1840,7 @@ static int str_unpack (lua_State *L) { } pos += size; } - lua_pushinteger(L, pos + 1); /* next position */ + lua_pushinteger(L, cast_st2S(pos) + 1); /* next position */ return n + 1; } diff --git a/lua/src/ltable.c b/lua/src/ltable.c index 3353c04..b7f88f6 100644 --- a/lua/src/ltable.c +++ b/lua/src/ltable.c @@ -25,6 +25,7 @@ #include #include +#include #include "lua.h" @@ -40,18 +41,48 @@ /* -** MAXABITS is the largest integer such that MAXASIZE fits in an +** Only hash parts with at least 2^LIMFORLAST have a 'lastfree' field +** that optimizes finding a free slot. That field is stored just before +** the array of nodes, in the same block. Smaller tables do a complete +** search when looking for a free slot. +*/ +#define LIMFORLAST 3 /* log2 of real limit (8) */ + +/* +** The union 'Limbox' stores 'lastfree' and ensures that what follows it +** is properly aligned to store a Node. +*/ +typedef struct { Node *dummy; Node follows_pNode; } Limbox_aux; + +typedef union { + Node *lastfree; + char padding[offsetof(Limbox_aux, follows_pNode)]; +} Limbox; + +#define haslastfree(t) ((t)->lsizenode >= LIMFORLAST) +#define getlastfree(t) ((cast(Limbox *, (t)->node) - 1)->lastfree) + + +/* +** MAXABITS is the largest integer such that 2^MAXABITS fits in an ** unsigned int. */ -#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) +#define MAXABITS (l_numbits(int) - 1) + + +/* +** MAXASIZEB is the maximum number of elements in the array part such +** that the size of the array fits in 'size_t'. +*/ +#define MAXASIZEB (MAX_SIZET/(sizeof(Value) + 1)) /* ** MAXASIZE is the maximum size of the array part. It is the minimum -** between 2^MAXABITS and the maximum size that, measured in bytes, -** fits in a 'size_t'. +** between 2^MAXABITS and MAXASIZEB. */ -#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) +#define MAXASIZE \ + (((1u << MAXABITS) < MAXASIZEB) ? (1u << MAXABITS) : cast_uint(MAXASIZEB)) /* ** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a @@ -65,7 +96,7 @@ ** between 2^MAXHBITS and the maximum size such that, measured in bytes, ** it fits in a 'size_t'. */ -#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) +#define MAXHSIZE luaM_limitN(1 << MAXHBITS, Node) /* @@ -78,7 +109,7 @@ ** for other types, it is better to avoid modulo by power of 2, as ** they can have many 2 factors. */ -#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1u)|1u)))) #define hashstr(t,str) hashpow2(t, (str)->hash) @@ -90,9 +121,15 @@ #define dummynode (&dummynode_) +/* +** Common hash part for tables with empty hash parts. That allows all +** tables to have a hash part, avoiding an extra check ("is there a hash +** part?") when indexing. Its sole node has an empty value and a key +** (DEADKEY, NULL) that is different from any valid TValue. +*/ static const Node dummynode_ = { {{NULL}, LUA_VEMPTY, /* value's value and type */ - LUA_VNIL, 0, {NULL}} /* key type, next, and key value */ + LUA_TDEADKEY, 0, {NULL}} /* key type, next, and key value */ }; @@ -108,7 +145,7 @@ static const TValue absentkey = {ABSTKEYCONSTANT}; static Node *hashint (const Table *t, lua_Integer i) { lua_Unsigned ui = l_castS2U(i); if (ui <= cast_uint(INT_MAX)) - return hashmod(t, cast_int(ui)); + return gnode(t, cast_int(ui) % cast_int((sizenode(t)-1) | 1)); else return hashmod(t, ui); } @@ -119,7 +156,7 @@ static Node *hashint (const Table *t, lua_Integer i) { ** The main computation should be just ** n = frexp(n, &i); return (n * INT_MAX) + i ** but there are some numerical subtleties. -** In a two-complement representation, INT_MAX does not has an exact +** In a two-complement representation, INT_MAX may not have an exact ** representation as a float, but INT_MIN does; because the absolute ** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** absolute value of the product 'frexp * -INT_MIN' is smaller or equal @@ -128,7 +165,7 @@ static Node *hashint (const Table *t, lua_Integer i) { ** INT_MIN. */ #if !defined(l_hashfloat) -static int l_hashfloat (lua_Number n) { +static unsigned l_hashfloat (lua_Number n) { int i; lua_Integer ni; n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); @@ -138,7 +175,7 @@ static int l_hashfloat (lua_Number n) { } else { /* normal case */ unsigned int u = cast_uint(i) + cast_uint(ni); - return cast_int(u <= cast_uint(INT_MAX) ? u : ~u); + return (u <= cast_uint(INT_MAX) ? u : ~u); } } #endif @@ -197,100 +234,55 @@ l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { ** Check whether key 'k1' is equal to the key in node 'n2'. This ** equality is raw, so there are no metamethods. Floats with integer ** values have been normalized, so integers cannot be equal to -** floats. It is assumed that 'eqshrstr' is simply pointer equality, so -** that short strings are handled in the default case. -** A true 'deadok' means to accept dead keys as equal to their original -** values. All dead keys are compared in the default case, by pointer -** identity. (Only collectable objects can produce dead keys.) Note that -** dead long strings are also compared by identity. -** Once a key is dead, its corresponding value may be collected, and -** then another value can be created with the same address. If this -** other value is given to 'next', 'equalkey' will signal a false -** positive. In a regular traversal, this situation should never happen, -** as all keys given to 'next' came from the table itself, and therefore -** could not have been collected. Outside a regular traversal, we -** have garbage in, garbage out. What is relevant is that this false -** positive does not break anything. (In particular, 'next' will return -** some other valid item on the table or nil.) +** floats. It is assumed that 'eqshrstr' is simply pointer equality, +** so that short strings are handled in the default case. The flag +** 'deadok' means to accept dead keys as equal to their original values. +** (Only collectable objects can produce dead keys.) Note that dead +** long strings are also compared by identity. Once a key is dead, +** its corresponding value may be collected, and then another value +** can be created with the same address. If this other value is given +** to 'next', 'equalkey' will signal a false positive. In a regular +** traversal, this situation should never happen, as all keys given to +** 'next' came from the table itself, and therefore could not have been +** collected. Outside a regular traversal, we have garbage in, garbage +** out. What is relevant is that this false positive does not break +** anything. (In particular, 'next' will return some other valid item +** on the table or nil.) */ static int equalkey (const TValue *k1, const Node *n2, int deadok) { - if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ - !(deadok && keyisdead(n2) && iscollectable(k1))) - return 0; /* cannot be same key */ - switch (keytt(n2)) { - case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: - return 1; - case LUA_VNUMINT: - return (ivalue(k1) == keyival(n2)); - case LUA_VNUMFLT: - return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); - case LUA_VLIGHTUSERDATA: - return pvalue(k1) == pvalueraw(keyval(n2)); - case LUA_VLCF: - return fvalue(k1) == fvalueraw(keyval(n2)); - case ctb(LUA_VLNGSTR): - return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); - default: + if (rawtt(k1) != keytt(n2)) { /* not the same variants? */ + if (keyisshrstr(n2) && ttislngstring(k1)) { + /* an external string can be equal to a short-string key */ + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + } + else if (deadok && keyisdead(n2) && iscollectable(k1)) { + /* a collectable value can be equal to a dead key */ return gcvalue(k1) == gcvalueraw(keyval(n2)); + } + else + return 0; /* otherwise, different variants cannot be equal */ } -} - - -/* -** True if value of 'alimit' is equal to the real size of the array -** part of table 't'. (Otherwise, the array part must be larger than -** 'alimit'.) -*/ -#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) - - -/* -** Returns the real size of the 'array' array -*/ -LUAI_FUNC unsigned int luaH_realasize (const Table *t) { - if (limitequalsasize(t)) - return t->alimit; /* this is the size */ - else { - unsigned int size = t->alimit; - /* compute the smallest power of 2 not smaller than 'size' */ - size |= (size >> 1); - size |= (size >> 2); - size |= (size >> 4); - size |= (size >> 8); -#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */ - size |= (size >> 16); -#if (UINT_MAX >> 30) > 3 - size |= (size >> 32); /* unsigned int has more than 32 bits */ -#endif -#endif - size++; - lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); - return size; + else { /* equal variants */ + switch (keytt(n2)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case ctb(LUA_VLNGSTR): + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + default: + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } } } -/* -** Check whether real size of the array is a power of 2. -** (If it is not, 'alimit' cannot be changed to any other value -** without changing the real size.) -*/ -static int ispow2realasize (const Table *t) { - return (!isrealasize(t) || ispow2(t->alimit)); -} - - -static unsigned int setlimittosize (Table *t) { - t->alimit = luaH_realasize(t); - setrealasize(t); - return t->alimit; -} - - -#define limitasasize(t) check_exp(isrealasize(t), t->alimit) - - - /* ** "Generic" get version. (Not that generic: not valid for integers, ** which may be in array part, nor for floats with integral values.) @@ -312,14 +304,34 @@ static const TValue *getgeneric (Table *t, const TValue *key, int deadok) { /* -** returns the index for 'k' if 'k' is an appropriate key to live in -** the array part of a table, 0 otherwise. +** Return the index 'k' (converted to an unsigned) if it is inside +** the range [1, limit]. */ -static unsigned int arrayindex (lua_Integer k) { - if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */ - return cast_uint(k); /* 'key' is an appropriate array index */ - else - return 0; +static unsigned checkrange (lua_Integer k, unsigned limit) { + return (l_castS2U(k) - 1u < limit) ? cast_uint(k) : 0; +} + + +/* +** Return the index 'k' if 'k' is an appropriate key to live in the +** array part of a table, 0 otherwise. +*/ +#define arrayindex(k) checkrange(k, MAXASIZE) + + +/* +** Check whether an integer key is in the array part of a table and +** return its index there, or zero. +*/ +#define ikeyinarray(t,k) checkrange(k, t->asize) + + +/* +** Check whether a key is in the array part of a table and return its +** index there, or zero. +*/ +static unsigned keyinarray (Table *t, const TValue *key) { + return (ttisinteger(key)) ? ikeyinarray(t, ivalue(key)) : 0; } @@ -328,18 +340,18 @@ static unsigned int arrayindex (lua_Integer k) { ** elements in the array part, then elements in the hash part. The ** beginning of a traversal is signaled by 0. */ -static unsigned int findindex (lua_State *L, Table *t, TValue *key, - unsigned int asize) { +static unsigned findindex (lua_State *L, Table *t, TValue *key, + unsigned asize) { unsigned int i; if (ttisnil(key)) return 0; /* first iteration */ - i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0; - if (i - 1u < asize) /* is 'key' inside array part? */ + i = keyinarray(t, key); + if (i != 0) /* is 'key' inside array part? */ return i; /* yes; that's the index */ else { const TValue *n = getgeneric(t, key, 1); if (l_unlikely(isabstkey(n))) luaG_runerror(L, "invalid key to 'next'"); /* key not found */ - i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ + i = cast_uint(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ return (i + 1) + asize; } @@ -347,16 +359,17 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key, int luaH_next (lua_State *L, Table *t, StkId key) { - unsigned int asize = luaH_realasize(t); + unsigned int asize = t->asize; unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ for (; i < asize; i++) { /* try first array part */ - if (!isempty(&t->array[i])) { /* a non-empty entry? */ - setivalue(s2v(key), i + 1); - setobj2s(L, key + 1, &t->array[i]); + lu_byte tag = *getArrTag(t, i); + if (!tagisempty(tag)) { /* a non-empty entry? */ + setivalue(s2v(key), cast_int(i) + 1); + farr2val(t, i, tag, s2v(key + 1)); return 1; } } - for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */ + for (i -= asize; i < sizenode(t); i++) { /* hash part */ if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ Node *n = gnode(t, i); getnodekey(L, s2v(key), n); @@ -368,9 +381,21 @@ int luaH_next (lua_State *L, Table *t, StkId key) { } +/* Extra space in Node array if it has a lastfree entry */ +#define extraLastfree(t) (haslastfree(t) ? sizeof(Limbox) : 0) + +/* 'node' size in bytes */ +static size_t sizehash (Table *t) { + return cast_sizet(sizenode(t)) * sizeof(Node) + extraLastfree(t); +} + + static void freehash (lua_State *L, Table *t) { - if (!isdummy(t)) - luaM_freearray(L, t->node, cast_sizet(sizenode(t))); + if (!isdummy(t)) { + /* get pointer to the beginning of Node array */ + char *arr = cast_charp(t->node) - extraLastfree(t); + luaM_freearray(L, arr, sizehash(t)); + } } @@ -380,58 +405,92 @@ static void freehash (lua_State *L, Table *t) { ** ============================================================== */ +static int insertkey (Table *t, const TValue *key, TValue *value); +static void newcheckedkey (Table *t, const TValue *key, TValue *value); + + +/* +** Structure to count the keys in a table. +** 'total' is the total number of keys in the table. +** 'na' is the number of *array indices* in the table (see 'arrayindex'). +** 'deleted' is true if there are deleted nodes in the hash part. +** 'nums' is a "count array" where 'nums[i]' is the number of integer +** keys between 2^(i - 1) + 1 and 2^i. Note that 'na' is the summation +** of 'nums'. +*/ +typedef struct { + unsigned total; + unsigned na; + int deleted; + unsigned nums[MAXABITS + 1]; +} Counters; + + +/* +** Check whether it is worth to use 'na' array entries instead of 'nh' +** hash nodes. (A hash node uses ~3 times more memory than an array +** entry: Two values plus 'next' versus one value.) Evaluate with size_t +** to avoid overflows. +*/ +#define arrayXhash(na,nh) (cast_sizet(na) <= cast_sizet(nh) * 3) + /* -** Compute the optimal size for the array part of table 't'. 'nums' is a -** "count array" where 'nums[i]' is the number of integers in the table -** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of -** integer keys in the table and leaves with the number of keys that -** will go to the array part; return the optimal size. (The condition -** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) +** Compute the optimal size for the array part of table 't'. +** This size maximizes the number of elements going to the array part +** while satisfying the condition 'arrayXhash' with the use of memory if +** all those elements went to the hash part. +** 'ct->na' enters with the total number of array indices in the table +** and leaves with the number of keys that will go to the array part; +** return the optimal size for the array part. */ -static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { +static unsigned computesizes (Counters *ct) { int i; unsigned int twotoi; /* 2^i (candidate for optimal size) */ unsigned int a = 0; /* number of elements smaller than 2^i */ unsigned int na = 0; /* number of elements to go to array part */ unsigned int optimal = 0; /* optimal size for array part */ - /* loop while keys can fill more than half of total size */ + /* traverse slices while 'twotoi' does not overflow and total of array + indices still can satisfy 'arrayXhash' against the array size */ for (i = 0, twotoi = 1; - twotoi > 0 && *pna > twotoi / 2; + twotoi > 0 && arrayXhash(twotoi, ct->na); i++, twotoi *= 2) { - a += nums[i]; - if (a > twotoi/2) { /* more than half elements present? */ + unsigned nums = ct->nums[i]; + a += nums; + if (nums > 0 && /* grows array only if it gets more elements... */ + arrayXhash(twotoi, a)) { /* ...while using "less memory" */ optimal = twotoi; /* optimal size (till now) */ na = a; /* all elements up to 'optimal' will go to array part */ } } - lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); - *pna = na; + ct->na = na; return optimal; } -static int countint (lua_Integer key, unsigned int *nums) { +static void countint (lua_Integer key, Counters *ct) { unsigned int k = arrayindex(key); - if (k != 0) { /* is 'key' an appropriate array index? */ - nums[luaO_ceillog2(k)]++; /* count as such */ - return 1; + if (k != 0) { /* is 'key' an array index? */ + ct->nums[luaO_ceillog2(k)]++; /* count as such */ + ct->na++; } - else - return 0; +} + + +l_sinline int arraykeyisempty (const Table *t, unsigned key) { + int tag = *getArrTag(t, key - 1); + return tagisempty(tag); } /* -** Count keys in array part of table 't': Fill 'nums[i]' with -** number of keys that will go into corresponding slice and return -** total number of non-nil keys. +** Count keys in array part of table 't'. */ -static unsigned int numusearray (const Table *t, unsigned int *nums) { +static void numusearray (const Table *t, Counters *ct) { int lg; unsigned int ttlg; /* 2^lg */ unsigned int ause = 0; /* summation of 'nums' */ - unsigned int i = 1; /* count to traverse all array keys */ - unsigned int asize = limitasasize(t); /* real array size */ + unsigned int i = 1; /* index to traverse all array keys */ + unsigned int asize = t->asize; /* traverse each slice */ for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { unsigned int lc = 0; /* counter */ @@ -443,30 +502,93 @@ static unsigned int numusearray (const Table *t, unsigned int *nums) { } /* count elements in range (2^(lg - 1), 2^lg] */ for (; i <= lim; i++) { - if (!isempty(&t->array[i-1])) + if (!arraykeyisempty(t, i)) lc++; } - nums[lg] += lc; + ct->nums[lg] += lc; ause += lc; } - return ause; + ct->total += ause; + ct->na += ause; } -static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { - int totaluse = 0; /* total number of elements */ - int ause = 0; /* elements added to 'nums' (can go to array part) */ - int i = sizenode(t); +/* +** Count keys in hash part of table 't'. As this only happens during +** a rehash, all nodes have been used. A node can have a nil value only +** if it was deleted after being created. +*/ +static void numusehash (const Table *t, Counters *ct) { + unsigned i = sizenode(t); + unsigned total = 0; while (i--) { Node *n = &t->node[i]; - if (!isempty(gval(n))) { + if (isempty(gval(n))) { + lua_assert(!keyisnil(n)); /* entry was deleted; key cannot be nil */ + ct->deleted = 1; + } + else { + total++; if (keyisinteger(n)) - ause += countint(keyival(n), nums); - totaluse++; + countint(keyival(n), ct); + } + } + ct->total += total; +} + + +/* +** Convert an "abstract size" (number of slots in an array) to +** "concrete size" (number of bytes in the array). +*/ +static size_t concretesize (unsigned int size) { + if (size == 0) + return 0; + else /* space for the two arrays plus an unsigned in between */ + return size * (sizeof(Value) + 1) + sizeof(unsigned); +} + + +/* +** Resize the array part of a table. If new size is equal to the old, +** do nothing. Else, if new size is zero, free the old array. (It must +** be present, as the sizes are different.) Otherwise, allocate a new +** array, move the common elements to new proper position, and then +** frees the old array. +** We could reallocate the array, but we still would need to move the +** elements to their new position, so the copy implicit in realloc is a +** waste. Moreover, most allocators will move the array anyway when the +** new size is double the old one (the most common case). +*/ +static Value *resizearray (lua_State *L , Table *t, + unsigned oldasize, + unsigned newasize) { + if (oldasize == newasize) + return t->array; /* nothing to be done */ + else if (newasize == 0) { /* erasing array? */ + Value *op = t->array - oldasize; /* original array's real address */ + luaM_freemem(L, op, concretesize(oldasize)); /* free it */ + return NULL; + } + else { + size_t newasizeb = concretesize(newasize); + Value *np = cast(Value *, + luaM_reallocvector(L, NULL, 0, newasizeb, lu_byte)); + if (np == NULL) /* allocation error? */ + return NULL; + np += newasize; /* shift pointer to the end of value segment */ + if (oldasize > 0) { + /* move common elements to new position */ + size_t oldasizeb = concretesize(oldasize); + Value *op = t->array; /* original array */ + unsigned tomove = (oldasize < newasize) ? oldasize : newasize; + size_t tomoveb = (oldasize < newasize) ? oldasizeb : newasizeb; + lua_assert(tomoveb > 0); + memcpy(np - tomove, op - tomove, tomoveb); + luaM_freemem(L, op - oldasize, oldasizeb); /* free old block */ } + return np; } - *pna += ause; - return totaluse; } @@ -477,27 +599,34 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { ** comparison ensures that the shift in the second one does not ** overflow. */ -static void setnodevector (lua_State *L, Table *t, unsigned int size) { +static void setnodevector (lua_State *L, Table *t, unsigned size) { if (size == 0) { /* no elements to hash part? */ t->node = cast(Node *, dummynode); /* use common 'dummynode' */ t->lsizenode = 0; - t->lastfree = NULL; /* signal that it is using dummy node */ + setdummy(t); /* signal that it is using dummy node */ } else { int i; int lsize = luaO_ceillog2(size); - if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) + if (lsize > MAXHBITS || (1 << lsize) > MAXHSIZE) luaG_runerror(L, "table overflow"); size = twoto(lsize); - t->node = luaM_newvector(L, size, Node); + if (lsize < LIMFORLAST) /* no 'lastfree' field? */ + t->node = luaM_newvector(L, size, Node); + else { + size_t bsize = size * sizeof(Node) + sizeof(Limbox); + char *node = luaM_newblock(L, bsize); + t->node = cast(Node *, node + sizeof(Limbox)); + getlastfree(t) = gnode(t, size); /* all positions are free */ + } + t->lsizenode = cast_byte(lsize); + setnodummy(t); for (i = 0; i < cast_int(size); i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilkey(n); setempty(gval(n)); } - t->lsizenode = cast_byte(lsize); - t->lastfree = gnode(t, size); /* all positions are free */ } } @@ -505,9 +634,9 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { /* ** (Re)insert all elements from the hash part of 'ot' into table 't'. */ -static void reinsert (lua_State *L, Table *ot, Table *t) { - int j; - int size = sizenode(ot); +static void reinserthash (lua_State *L, Table *ot, Table *t) { + unsigned j; + unsigned size = sizenode(ot); for (j = 0; j < size; j++) { Node *old = gnode(ot, j); if (!isempty(gval(old))) { @@ -515,25 +644,56 @@ static void reinsert (lua_State *L, Table *ot, Table *t) { already present in the table */ TValue k; getnodekey(L, &k, old); - luaH_set(L, t, &k, gval(old)); + newcheckedkey(t, &k, gval(old)); } } } /* -** Exchange the hash part of 't1' and 't2'. +** Exchange the hash part of 't1' and 't2'. (In 'flags', only the +** dummy bit must be exchanged: The 'isrealasize' is not related +** to the hash part, and the metamethod bits do not change during +** a resize, so the "real" table can keep their values.) */ static void exchangehashpart (Table *t1, Table *t2) { lu_byte lsizenode = t1->lsizenode; Node *node = t1->node; - Node *lastfree = t1->lastfree; + int bitdummy1 = t1->flags & BITDUMMY; t1->lsizenode = t2->lsizenode; t1->node = t2->node; - t1->lastfree = t2->lastfree; + t1->flags = cast_byte((t1->flags & NOTBITDUMMY) | (t2->flags & BITDUMMY)); t2->lsizenode = lsizenode; t2->node = node; - t2->lastfree = lastfree; + t2->flags = cast_byte((t2->flags & NOTBITDUMMY) | bitdummy1); +} + + +/* +** Re-insert into the new hash part of a table the elements from the +** vanishing slice of the array part. +*/ +static void reinsertOldSlice (Table *t, unsigned oldasize, + unsigned newasize) { + unsigned i; + for (i = newasize; i < oldasize; i++) { /* traverse vanishing slice */ + lu_byte tag = *getArrTag(t, i); + if (!tagisempty(tag)) { /* a non-empty entry? */ + TValue key, aux; + setivalue(&key, l_castU2S(i) + 1); /* make the key */ + farr2val(t, i, tag, &aux); /* copy value into 'aux' */ + insertkey(t, &key, &aux); /* insert entry into the hash part */ + } + } +} + + +/* +** Clear new slice of the array. +*/ +static void clearNewSlice (Table *t, unsigned oldasize, unsigned newasize) { + for (; oldasize < newasize; oldasize++) + *getArrTag(t, oldasize) = LUA_VEMPTY; } @@ -549,28 +709,28 @@ static void exchangehashpart (Table *t1, Table *t2) { ** into the table, initializes the new part of the array (if any) with ** nils and reinserts the elements of the old hash back into the new ** parts of the table. +** Note that if the new size for the array part ('newasize') is equal to +** the old one ('oldasize'), this function will do nothing with that +** part. */ -void luaH_resize (lua_State *L, Table *t, unsigned int newasize, - unsigned int nhsize) { - unsigned int i; +void luaH_resize (lua_State *L, Table *t, unsigned newasize, + unsigned nhsize) { Table newt; /* to keep the new hash part */ - unsigned int oldasize = setlimittosize(t); - TValue *newarray; + unsigned oldasize = t->asize; + Value *newarray; + if (newasize > MAXASIZE) + luaG_runerror(L, "table overflow"); /* create new hash part with appropriate size into 'newt' */ + newt.flags = 0; setnodevector(L, &newt, nhsize); if (newasize < oldasize) { /* will array shrink? */ - t->alimit = newasize; /* pretend array has new size... */ - exchangehashpart(t, &newt); /* and new hash */ /* re-insert into the new hash the elements from vanishing slice */ - for (i = newasize; i < oldasize; i++) { - if (!isempty(&t->array[i])) - luaH_setint(L, t, i + 1, &t->array[i]); - } - t->alimit = oldasize; /* restore current size... */ - exchangehashpart(t, &newt); /* and hash (in case of errors) */ + exchangehashpart(t, &newt); /* pretend table has new hash */ + reinsertOldSlice(t, oldasize, newasize); + exchangehashpart(t, &newt); /* restore old hash (in case of errors) */ } /* allocate new array */ - newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); + newarray = resizearray(L, t, oldasize, newasize); if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ freehash(L, &newt); /* release new hash part */ luaM_error(L); /* raise error (with array unchanged) */ @@ -578,46 +738,59 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, /* allocation ok; initialize new part of the array */ exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ t->array = newarray; /* set new array part */ - t->alimit = newasize; - for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ - setempty(&t->array[i]); + t->asize = newasize; + if (newarray != NULL) + *lenhint(t) = newasize / 2u; /* set an initial hint */ + clearNewSlice(t, oldasize, newasize); /* re-insert elements from old hash part into new parts */ - reinsert(L, &newt, t); /* 'newt' now has the old hash */ + reinserthash(L, &newt, t); /* 'newt' now has the old hash */ freehash(L, &newt); /* free old hash part */ } void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { - int nsize = allocsizenode(t); + unsigned nsize = allocsizenode(t); luaH_resize(L, t, nasize, nsize); } + /* -** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i +** Rehash a table. First, count its keys. If there are array indices +** outside the array part, compute the new best size for that part. +** Then, resize the table. */ static void rehash (lua_State *L, Table *t, const TValue *ek) { - unsigned int asize; /* optimal size for array part */ - unsigned int na; /* number of keys in the array part */ - unsigned int nums[MAXABITS + 1]; - int i; - int totaluse; - for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ - setlimittosize(t); - na = numusearray(t, nums); /* count keys in array part */ - totaluse = na; /* all those keys are integer keys */ - totaluse += numusehash(t, nums, &na); /* count keys in hash part */ - /* count extra key */ + unsigned asize; /* optimal size for array part */ + Counters ct; + unsigned i; + unsigned nsize; /* size for the hash part */ + /* reset counts */ + for (i = 0; i <= MAXABITS; i++) ct.nums[i] = 0; + ct.na = 0; + ct.deleted = 0; + ct.total = 1; /* count extra key */ if (ttisinteger(ek)) - na += countint(ivalue(ek), nums); - totaluse++; - /* compute new size for array part */ - asize = computesizes(nums, &na); + countint(ivalue(ek), &ct); /* extra key may go to array */ + numusehash(t, &ct); /* count keys in hash part */ + if (ct.na == 0) { + /* no new keys to enter array part; keep it with the same size */ + asize = t->asize; + } + else { /* compute best size for array part */ + numusearray(t, &ct); /* count keys in array part */ + asize = computesizes(&ct); /* compute new size for array part */ + } + /* all keys not in the array part go to the hash part */ + nsize = ct.total - ct.na; + if (ct.deleted) { /* table has deleted entries? */ + /* insertion-deletion-insertion: give hash some extra size to + avoid repeated resizings */ + nsize += nsize >> 2; + } /* resize the table to new computed sizes */ - luaH_resize(L, t, asize, totaluse - na); + luaH_resize(L, t, asize, nsize); } - - /* ** }============================================================= */ @@ -627,27 +800,47 @@ Table *luaH_new (lua_State *L) { GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); Table *t = gco2t(o); t->metatable = NULL; - t->flags = cast_byte(maskflags); /* table has no metamethod fields */ + t->flags = maskflags; /* table has no metamethod fields */ t->array = NULL; - t->alimit = 0; + t->asize = 0; setnodevector(L, t, 0); return t; } +lu_mem luaH_size (Table *t) { + lu_mem sz = cast(lu_mem, sizeof(Table)) + concretesize(t->asize); + if (!isdummy(t)) + sz += sizehash(t); + return sz; +} + + +/* +** Frees a table. +*/ void luaH_free (lua_State *L, Table *t) { freehash(L, t); - luaM_freearray(L, t->array, luaH_realasize(t)); + resizearray(L, t, t->asize, 0); luaM_free(L, t); } static Node *getfreepos (Table *t) { - if (!isdummy(t)) { - while (t->lastfree > t->node) { - t->lastfree--; - if (keyisnil(t->lastfree)) - return t->lastfree; + if (haslastfree(t)) { /* does it have 'lastfree' information? */ + /* look for a spot before 'lastfree', updating 'lastfree' */ + while (getlastfree(t) > t->node) { + Node *free = --getlastfree(t); + if (keyisnil(free)) + return free; + } + } + else { /* no 'lastfree' information */ + unsigned i = sizenode(t); + while (i--) { /* do a linear search */ + Node *free = gnode(t, i); + if (keyisnil(free)) + return free; } } return NULL; /* could not find a free place */ @@ -656,40 +849,22 @@ static Node *getfreepos (Table *t) { /* -** inserts a new key into a hash table; first, check whether key's main +** Inserts a new key into a hash table; first, check whether key's main ** position is free. If not, check whether colliding node is in its main -** position or not: if it is not, move colliding node to an empty place and -** put new key in its main position; otherwise (colliding node is in its main -** position), new key goes to an empty position. +** position or not: if it is not, move colliding node to an empty place +** and put new key in its main position; otherwise (colliding node is in +** its main position), new key goes to an empty position. Return 0 if +** could not insert key (could not find a free space). */ -static void luaH_newkey (lua_State *L, Table *t, const TValue *key, - TValue *value) { - Node *mp; - TValue aux; - if (l_unlikely(ttisnil(key))) - luaG_runerror(L, "table index is nil"); - else if (ttisfloat(key)) { - lua_Number f = fltvalue(key); - lua_Integer k; - if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ - setivalue(&aux, k); - key = &aux; /* insert it as an integer */ - } - else if (l_unlikely(luai_numisnan(f))) - luaG_runerror(L, "table index is NaN"); - } - if (ttisnil(value)) - return; /* do not insert nil values */ - mp = mainpositionTV(t, key); +static int insertkey (Table *t, const TValue *key, TValue *value) { + Node *mp = mainpositionTV(t, key); + /* table cannot already contain the key */ + lua_assert(isabstkey(getgeneric(t, key, 0))); if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ Node *othern; Node *f = getfreepos(t); /* get a free place */ - if (f == NULL) { /* cannot find a free place? */ - rehash(L, t, key); /* grow table */ - /* whatever called 'newkey' takes care of TM cache */ - luaH_set(L, t, key, value); /* insert key into grown table */ - return; - } + if (f == NULL) /* cannot find a free place? */ + return 0; lua_assert(!isdummy(t)); othern = mainpositionfromnode(t, mp); if (othern != mp) { /* is colliding node out of its main position? */ @@ -713,66 +888,93 @@ static void luaH_newkey (lua_State *L, Table *t, const TValue *key, mp = f; } } - setnodekey(L, mp, key); - luaC_barrierback(L, obj2gco(t), key); + setnodekey(mp, key); lua_assert(isempty(gval(mp))); - setobj2t(L, gval(mp), value); -} - - -/* -** Search function for integers. If integer is inside 'alimit', get it -** directly from the array part. Otherwise, if 'alimit' is not -** the real size of the array, the key still can be in the array part. -** In this case, do the "Xmilia trick" to check whether 'key-1' is -** smaller than the real size. -** The trick works as follow: let 'p' be an integer such that -** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'. -** That is, 2^(p+1) is the real size of the array, and 'p' is the highest -** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'. -** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will -** have the 'p' bit cleared. If the key is outside the array, that is, -** 'key-1 >= 2^(p+1)', then 'res' will have some bit on higher than 'p', -** therefore it will be larger or equal to 'alimit', and the check -** will fail. If 'key-1 < 2^(p+1)', then 'res' has no bit on higher than -** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller -** than 2^p, therefore smaller than 'alimit', and the check succeeds. -** As special cases, when 'alimit' is 0 the condition is trivially false, -** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'. -** If key is 0 or negative, 'res' will have its higher bit on, so that -** if cannot be smaller than alimit. -*/ -const TValue *luaH_getint (Table *t, lua_Integer key) { - lua_Unsigned alimit = t->alimit; - if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */ - return &t->array[key - 1]; - else if (!isrealasize(t) && /* key still may be in the array part? */ - (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) { - t->alimit = cast_uint(key); /* probably '#t' is here now */ - return &t->array[key - 1]; - } - else { /* key is not in the array part; check the hash */ - Node *n = hashint(t, key); - for (;;) { /* check whether 'key' is somewhere in the chain */ - if (keyisinteger(n) && keyival(n) == key) - return gval(n); /* that's it */ - else { - int nx = gnext(n); - if (nx == 0) break; - n += nx; - } + setobj2t(cast(lua_State *, 0), gval(mp), value); + return 1; +} + + +/* +** Insert a key in a table where there is space for that key, the +** key is valid, and the value is not nil. +*/ +static void newcheckedkey (Table *t, const TValue *key, TValue *value) { + unsigned i = keyinarray(t, key); + if (i > 0) /* is key in the array part? */ + obj2arr(t, i - 1, value); /* set value in the array */ + else { + int done = insertkey(t, key, value); /* insert key in the hash part */ + lua_assert(done); /* it cannot fail */ + cast(void, done); /* to avoid warnings */ + } +} + + +static void luaH_newkey (lua_State *L, Table *t, const TValue *key, + TValue *value) { + if (!ttisnil(value)) { /* do not insert nil values */ + int done = insertkey(t, key, value); + if (!done) { /* could not find a free place? */ + rehash(L, t, key); /* grow table */ + newcheckedkey(t, key, value); /* insert key in grown table */ + } + luaC_barrierback(L, obj2gco(t), key); + /* for debugging only: any new key may force an emergency collection */ + condchangemem(L, (void)0, (void)0, 1); + } +} + + +static const TValue *getintfromhash (Table *t, lua_Integer key) { + Node *n = hashint(t, key); + lua_assert(!ikeyinarray(t, key)); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisinteger(n) && keyival(n) == key) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; } - return &absentkey; } + return &absentkey; +} + + +static int hashkeyisempty (Table *t, lua_Unsigned key) { + const TValue *val = getintfromhash(t, l_castU2S(key)); + return isempty(val); +} + + +static lu_byte finishnodeget (const TValue *val, TValue *res) { + if (!ttisnil(val)) { + setobj(((lua_State*)NULL), res, val); + } + return ttypetag(val); +} + + +lu_byte luaH_getint (Table *t, lua_Integer key, TValue *res) { + unsigned k = ikeyinarray(t, key); + if (k > 0) { + lu_byte tag = *getArrTag(t, k - 1); + if (!tagisempty(tag)) + farr2val(t, k - 1, tag, res); + return tag; + } + else + return finishnodeget(getintfromhash(t, key), res); } /* ** search function for short strings */ -const TValue *luaH_getshortstr (Table *t, TString *key) { +const TValue *luaH_Hgetshortstr (Table *t, TString *key) { Node *n = hashstr(t, key); - lua_assert(key->tt == LUA_VSHRSTR); + lua_assert(strisshr(key)); for (;;) { /* check whether 'key' is somewhere in the chain */ if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) return gval(n); /* that's it */ @@ -786,49 +988,203 @@ const TValue *luaH_getshortstr (Table *t, TString *key) { } -const TValue *luaH_getstr (Table *t, TString *key) { - if (key->tt == LUA_VSHRSTR) - return luaH_getshortstr(t, key); - else { /* for long strings, use generic case */ - TValue ko; - setsvalue(cast(lua_State *, NULL), &ko, key); - return getgeneric(t, &ko, 0); - } +lu_byte luaH_getshortstr (Table *t, TString *key, TValue *res) { + return finishnodeget(luaH_Hgetshortstr(t, key), res); +} + + +static const TValue *Hgetlongstr (Table *t, TString *key) { + TValue ko; + lua_assert(!strisshr(key)); + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko, 0); /* for long strings, use generic case */ +} + + +static const TValue *Hgetstr (Table *t, TString *key) { + if (strisshr(key)) + return luaH_Hgetshortstr(t, key); + else + return Hgetlongstr(t, key); +} + + +lu_byte luaH_getstr (Table *t, TString *key, TValue *res) { + return finishnodeget(Hgetstr(t, key), res); } /* ** main search function */ -const TValue *luaH_get (Table *t, const TValue *key) { +lu_byte luaH_get (Table *t, const TValue *key, TValue *res) { + const TValue *slot; switch (ttypetag(key)) { - case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key)); - case LUA_VNUMINT: return luaH_getint(t, ivalue(key)); - case LUA_VNIL: return &absentkey; + case LUA_VSHRSTR: + slot = luaH_Hgetshortstr(t, tsvalue(key)); + break; + case LUA_VNUMINT: + return luaH_getint(t, ivalue(key), res); + case LUA_VNIL: + slot = &absentkey; + break; case LUA_VNUMFLT: { lua_Integer k; if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ - return luaH_getint(t, k); /* use specialized version */ + return luaH_getint(t, k, res); /* use specialized version */ /* else... */ } /* FALLTHROUGH */ default: - return getgeneric(t, key, 0); + slot = getgeneric(t, key, 0); + break; } + return finishnodeget(slot, res); } /* -** Finish a raw "set table" operation, where 'slot' is where the value -** should have been (the result of a previous "get table"). -** Beware: when using this function you probably need to check a GC -** barrier and invalidate the TM cache. +** When a 'pset' cannot be completed, this function returns an encoding +** of its result, to be used by 'luaH_finishset'. */ -void luaH_finishset (lua_State *L, Table *t, const TValue *key, - const TValue *slot, TValue *value) { +static int retpsetcode (Table *t, const TValue *slot) { if (isabstkey(slot)) - luaH_newkey(L, t, key, value); + return HNOTFOUND; /* no slot with that key */ + else /* return node encoded */ + return cast_int((cast(Node*, slot) - t->node)) + HFIRSTNODE; +} + + +static int finishnodeset (Table *t, const TValue *slot, TValue *val) { + if (!ttisnil(slot)) { + setobj(((lua_State*)NULL), cast(TValue*, slot), val); + return HOK; /* success */ + } + else + return retpsetcode(t, slot); +} + + +static int rawfinishnodeset (const TValue *slot, TValue *val) { + if (isabstkey(slot)) + return 0; /* no slot with that key */ + else { + setobj(((lua_State*)NULL), cast(TValue*, slot), val); + return 1; /* success */ + } +} + + +int luaH_psetint (Table *t, lua_Integer key, TValue *val) { + lua_assert(!ikeyinarray(t, key)); + return finishnodeset(t, getintfromhash(t, key), val); +} + + +static int psetint (Table *t, lua_Integer key, TValue *val) { + int hres; + luaH_fastseti(t, key, val, hres); + return hres; +} + + +/* +** This function could be just this: +** return finishnodeset(t, luaH_Hgetshortstr(t, key), val); +** However, it optimizes the common case created by constructors (e.g., +** {x=1, y=2}), which creates a key in a table that has no metatable, +** it is not old/black, and it already has space for the key. +*/ + +int luaH_psetshortstr (Table *t, TString *key, TValue *val) { + const TValue *slot = luaH_Hgetshortstr(t, key); + if (!ttisnil(slot)) { /* key already has a value? (all too common) */ + setobj(((lua_State*)NULL), cast(TValue*, slot), val); /* update it */ + return HOK; /* done */ + } + else if (checknoTM(t->metatable, TM_NEWINDEX)) { /* no metamethod? */ + if (ttisnil(val)) /* new value is nil? */ + return HOK; /* done (value is already nil/absent) */ + if (isabstkey(slot) && /* key is absent? */ + !(isblack(t) && iswhite(key))) { /* and don't need barrier? */ + TValue tk; /* key as a TValue */ + setsvalue(cast(lua_State *, NULL), &tk, key); + if (insertkey(t, &tk, val)) { /* insert key, if there is space */ + invalidateTMcache(t); + return HOK; + } + } + } + /* Else, either table has new-index metamethod, or it needs barrier, + or it needs to rehash for the new key. In any of these cases, the + operation cannot be completed here. Return a code for the caller. */ + return retpsetcode(t, slot); +} + + +int luaH_psetstr (Table *t, TString *key, TValue *val) { + if (strisshr(key)) + return luaH_psetshortstr(t, key, val); else - setobj2t(L, cast(TValue *, slot), value); + return finishnodeset(t, Hgetlongstr(t, key), val); +} + + +int luaH_pset (Table *t, const TValue *key, TValue *val) { + switch (ttypetag(key)) { + case LUA_VSHRSTR: return luaH_psetshortstr(t, tsvalue(key), val); + case LUA_VNUMINT: return psetint(t, ivalue(key), val); + case LUA_VNIL: return HNOTFOUND; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ + return psetint(t, k, val); /* use specialized version */ + /* else... */ + } /* FALLTHROUGH */ + default: + return finishnodeset(t, getgeneric(t, key, 0), val); + } +} + +/* +** Finish a raw "set table" operation, where 'hres' encodes where the +** value should have been (the result of a previous 'pset' operation). +** Beware: when using this function the caller probably need to check a +** GC barrier and invalidate the TM cache. +*/ +void luaH_finishset (lua_State *L, Table *t, const TValue *key, + TValue *value, int hres) { + lua_assert(hres != HOK); + if (hres == HNOTFOUND) { + TValue aux; + if (l_unlikely(ttisnil(key))) + luaG_runerror(L, "table index is nil"); + else if (ttisfloat(key)) { + lua_Number f = fltvalue(key); + lua_Integer k; + if (luaV_flttointeger(f, &k, F2Ieq)) { + setivalue(&aux, k); /* key is equal to an integer */ + key = &aux; /* insert it as an integer */ + } + else if (l_unlikely(luai_numisnan(f))) + luaG_runerror(L, "table index is NaN"); + } + else if (isextstr(key)) { /* external string? */ + /* If string is short, must internalize it to be used as table key */ + TString *ts = luaS_normstr(L, tsvalue(key)); + setsvalue2s(L, L->top.p++, ts); /* anchor 'ts' (EXTRA_STACK) */ + luaH_newkey(L, t, s2v(L->top.p - 1), value); + L->top.p--; + return; + } + luaH_newkey(L, t, key, value); + } + else if (hres > 0) { /* regular Node? */ + setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value); + } + else { /* array entry */ + hres = ~hres; /* real index */ + obj2arr(t, cast_uint(hres), value); + } } @@ -837,156 +1193,160 @@ void luaH_finishset (lua_State *L, Table *t, const TValue *key, ** barrier and invalidate the TM cache. */ void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) { - const TValue *slot = luaH_get(t, key); - luaH_finishset(L, t, key, slot, value); + int hres = luaH_pset(t, key, value); + if (hres != HOK) + luaH_finishset(L, t, key, value, hres); } +/* +** Ditto for a GC barrier. (No need to invalidate the TM cache, as +** integers cannot be keys to metamethods.) +*/ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { - const TValue *p = luaH_getint(t, key); - if (isabstkey(p)) { - TValue k; - setivalue(&k, key); - luaH_newkey(L, t, &k, value); + unsigned ik = ikeyinarray(t, key); + if (ik > 0) + obj2arr(t, ik - 1, value); + else { + int ok = rawfinishnodeset(getintfromhash(t, key), value); + if (!ok) { + TValue k; + setivalue(&k, key); + luaH_newkey(L, t, &k, value); + } } - else - setobj2t(L, cast(TValue *, p), value); } /* ** Try to find a boundary in the hash part of table 't'. From the -** caller, we know that 'j' is zero or present and that 'j + 1' is -** present. We want to find a larger key that is absent from the -** table, so that we can do a binary search between the two keys to -** find a boundary. We keep doubling 'j' until we get an absent index. -** If the doubling would overflow, we try LUA_MAXINTEGER. If it is -** absent, we are ready for the binary search. ('j', being max integer, -** is larger or equal to 'i', but it cannot be equal because it is -** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a -** boundary. ('j + 1' cannot be a present integer key because it is -** not a valid integer in Lua.) -*/ -static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { - lua_Unsigned i; - if (j == 0) j++; /* the caller ensures 'j + 1' is present */ - do { +** caller, we know that 'asize + 1' is present. We want to find a larger +** key that is absent from the table, so that we can do a binary search +** between the two keys to find a boundary. We keep doubling 'j' until +** we get an absent index. If the doubling would overflow, we try +** LUA_MAXINTEGER. If it is absent, we are ready for the binary search. +** ('j', being max integer, is larger or equal to 'i', but it cannot be +** equal because it is absent while 'i' is present.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is not +** a valid integer in Lua.) +** About 'rnd': If we used a fixed algorithm, a bad actor could fill +** a table with only the keys that would be probed, in such a way that +** a small table could result in a huge length. To avoid that, we use +** the state's seed as a source of randomness. For the first probe, +** we "randomly double" 'i' by adding to it a random number roughly its +** width. +*/ +static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) { + lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */ + unsigned rnd = G(L)->seed; + int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */ + unsigned mask = (1u << n) - 1; /* 11...111 with the width of 'asize' */ + unsigned incr = (rnd & mask) + 1; /* first increment (at least 1) */ + lua_Unsigned j = (incr <= l_castS2U(LUA_MAXINTEGER) - i) ? i + incr : i + 1; + rnd >>= n; /* used 'n' bits from 'rnd' */ + while (!hashkeyisempty(t, j)) { /* repeat until an absent t[j] */ i = j; /* 'i' is a present index */ - if (j <= l_castS2U(LUA_MAXINTEGER) / 2) - j *= 2; + if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) { + j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */ + rnd >>= 1; + } else { j = LUA_MAXINTEGER; - if (isempty(luaH_getint(t, j))) /* t[j] not present? */ + if (hashkeyisempty(t, j)) /* t[j] not present? */ break; /* 'j' now is an absent index */ else /* weird case */ return j; /* well, max integer is a boundary... */ } - } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */ + } /* i < j && t[i] present && t[j] absent */ while (j - i > 1u) { /* do a binary search between them */ lua_Unsigned m = (i + j) / 2; - if (isempty(luaH_getint(t, m))) j = m; + if (hashkeyisempty(t, m)) j = m; else i = m; } return i; } -static unsigned int binsearch (const TValue *array, unsigned int i, - unsigned int j) { +static unsigned int binsearch (Table *array, unsigned int i, unsigned int j) { + lua_assert(i <= j); while (j - i > 1u) { /* binary search */ unsigned int m = (i + j) / 2; - if (isempty(&array[m - 1])) j = m; + if (arraykeyisempty(array, m)) j = m; else i = m; } return i; } +/* return a border, saving it as a hint for next call */ +static lua_Unsigned newhint (Table *t, unsigned hint) { + lua_assert(hint <= t->asize); + *lenhint(t) = hint; + return hint; +} + + /* -** Try to find a boundary in table 't'. (A 'boundary' is an integer index -** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent -** and 'maxinteger' if t[maxinteger] is present.) -** (In the next explanation, we use Lua indices, that is, with base 1. -** The code itself uses base 0 when indexing the array part of the table.) -** The code starts with 'limit = t->alimit', a position in the array -** part that may be a boundary. -** -** (1) If 't[limit]' is empty, there must be a boundary before it. -** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1' -** is present. If so, it is a boundary. Otherwise, do a binary search -** between 0 and limit to find a boundary. In both cases, try to -** use this boundary as the new 'alimit', as a hint for the next call. -** -** (2) If 't[limit]' is not empty and the array has more elements -** after 'limit', try to find a boundary there. Again, try first -** the special case (which should be quite frequent) where 'limit+1' -** is empty, so that 'limit' is a boundary. Otherwise, check the -** last element of the array part. If it is empty, there must be a -** boundary between the old limit (present) and the last element -** (absent), which is found with a binary search. (This boundary always -** can be a new limit.) -** -** (3) The last case is when there are no elements in the array part -** (limit == 0) or its last element (the new limit) is present. -** In this case, must check the hash part. If there is no hash part -** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call -** 'hash_search' to find a boundary in the hash part of the table. -** (In those cases, the boundary is not inside the array part, and -** therefore cannot be used as a new limit.) -*/ -lua_Unsigned luaH_getn (Table *t) { - unsigned int limit = t->alimit; - if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ - /* there must be a boundary before 'limit' */ - if (limit >= 2 && !isempty(&t->array[limit - 2])) { - /* 'limit - 1' is a boundary; can it be a new limit? */ - if (ispow2realasize(t) && !ispow2(limit - 1)) { - t->alimit = limit - 1; - setnorealasize(t); /* now 'alimit' is not the real size */ - } - return limit - 1; - } - else { /* must search for a boundary in [0, limit] */ - unsigned int boundary = binsearch(t->array, 0, limit); - /* can this boundary represent the real size of the array? */ - if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { - t->alimit = boundary; /* use it as the new limit */ - setnorealasize(t); +** Try to find a border in table 't'. (A 'border' is an integer index +** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent, +** or 'maxinteger' if t[maxinteger] is present.) +** If there is an array part, try to find a border there. First try +** to find it in the vicinity of the previous result (hint), to handle +** cases like 't[#t + 1] = val' or 't[#t] = nil', that move the border +** by one entry. Otherwise, do a binary search to find the border. +** If there is no array part, or its last element is non empty, the +** border may be in the hash part. +*/ +lua_Unsigned luaH_getn (lua_State *L, Table *t) { + unsigned asize = t->asize; + if (asize > 0) { /* is there an array part? */ + const unsigned maxvicinity = 4; + unsigned limit = *lenhint(t); /* start with the hint */ + if (limit == 0) + limit = 1; /* make limit a valid index in the array */ + if (arraykeyisempty(t, limit)) { /* t[limit] empty? */ + /* there must be a border before 'limit' */ + unsigned i; + /* look for a border in the vicinity of the hint */ + for (i = 0; i < maxvicinity && limit > 1; i++) { + limit--; + if (!arraykeyisempty(t, limit)) + return newhint(t, limit); /* 'limit' is a border */ } - return boundary; + /* t[limit] still empty; search for a border in [0, limit) */ + return newhint(t, binsearch(t, 0, limit)); } - } - /* 'limit' is zero or present in table */ - if (!limitequalsasize(t)) { /* (2)? */ - /* 'limit' > 0 and array has more elements after 'limit' */ - if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */ - return limit; /* this is the boundary */ - /* else, try last element in the array */ - limit = luaH_realasize(t); - if (isempty(&t->array[limit - 1])) { /* empty? */ - /* there must be a boundary in the array after old limit, - and it must be a valid new limit */ - unsigned int boundary = binsearch(t->array, t->alimit, limit); - t->alimit = boundary; - return boundary; + else { /* 'limit' is present in table; look for a border after it */ + unsigned i; + /* look for a border in the vicinity of the hint */ + for (i = 0; i < maxvicinity && limit < asize; i++) { + limit++; + if (arraykeyisempty(t, limit)) + return newhint(t, limit - 1); /* 'limit - 1' is a border */ + } + if (arraykeyisempty(t, asize)) { /* last element empty? */ + /* t[limit] not empty; search for a border in [limit, asize) */ + return newhint(t, binsearch(t, limit, asize)); + } } - /* else, new limit is present in the table; check the hash part */ + /* last element non empty; set a hint to speed up finding that again */ + /* (keys in the hash part cannot be hints) */ + *lenhint(t) = asize; } - /* (3) 'limit' is the last element and either is zero or present in table */ - lua_assert(limit == luaH_realasize(t) && - (limit == 0 || !isempty(&t->array[limit - 1]))); - if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1)))) - return limit; /* 'limit + 1' is absent */ - else /* 'limit + 1' is also present */ - return hash_search(t, limit); + /* no array part or t[asize] is not empty; check the hash part */ + lua_assert(asize == 0 || !arraykeyisempty(t, asize)); + if (isdummy(t) || hashkeyisempty(t, asize + 1)) + return asize; /* 'asize + 1' is empty */ + else /* 'asize + 1' is also non empty */ + return hash_search(L, t, asize); } #if defined(LUA_DEBUG) -/* export these functions for the test library */ +/* export this function for the test library */ Node *luaH_mainposition (const Table *t, const TValue *key) { return mainpositionTV(t, key); diff --git a/lua/src/ltable.h b/lua/src/ltable.h index 8e68903..f3b7bc7 100644 --- a/lua/src/ltable.h +++ b/lua/src/ltable.h @@ -20,11 +20,21 @@ ** may have any of these metamethods. (First access that fails after the ** clearing will set the bit again.) */ -#define invalidateTMcache(t) ((t)->flags &= ~maskflags) +#define invalidateTMcache(t) ((t)->flags &= cast_byte(~maskflags)) -/* true when 't' is using 'dummynode' as its hash part */ -#define isdummy(t) ((t)->lastfree == NULL) +/* +** Bit BITDUMMY set in 'flags' means the table is using the dummy node +** for its hash part. +*/ + +#define BITDUMMY (1 << 6) +#define NOTBITDUMMY cast_byte(~BITDUMMY) +#define isdummy(t) ((t)->flags & BITDUMMY) + +#define setnodummy(t) ((t)->flags &= NOTBITDUMMY) +#define setdummy(t) ((t)->flags |= BITDUMMY) + /* allocated size for hash nodes */ @@ -35,24 +45,135 @@ #define nodefromval(v) cast(Node *, (v)) -LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); + +#define luaH_fastgeti(t,k,res,tag) \ + { Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \ + if ((u < h->asize)) { \ + tag = *getArrTag(h, u); \ + if (!tagisempty(tag)) { farr2val(h, u, tag, res); }} \ + else { tag = luaH_getint(h, (k), res); }} + + +#define luaH_fastseti(t,k,val,hres) \ + { Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \ + if ((u < h->asize)) { \ + lu_byte *tag = getArrTag(h, u); \ + if (checknoTM(h->metatable, TM_NEWINDEX) || !tagisempty(*tag)) \ + { fval2arr(h, u, tag, val); hres = HOK; } \ + else hres = ~cast_int(u); } \ + else { hres = luaH_psetint(h, k, val); }} + + +/* results from pset */ +#define HOK 0 +#define HNOTFOUND 1 +#define HNOTATABLE 2 +#define HFIRSTNODE 3 + +/* +** 'luaH_get*' operations set 'res', unless the value is absent, and +** return the tag of the result. +** The 'luaH_pset*' (pre-set) operations set the given value and return +** HOK, unless the original value is absent. In that case, if the key +** is really absent, they return HNOTFOUND. Otherwise, if there is a +** slot with that key but with no value, 'luaH_pset*' return an encoding +** of where the key is (usually called 'hres'). (pset cannot set that +** value because there might be a metamethod.) If the slot is in the +** hash part, the encoding is (HFIRSTNODE + hash index); if the slot is +** in the array part, the encoding is (~array index), a negative value. +** The value HNOTATABLE is used by the fast macros to signal that the +** value being indexed is not a table. +** (The size for the array part is limited by the maximum power of two +** that fits in an unsigned integer; that is INT_MAX+1. So, the C-index +** ranges from 0, which encodes to -1, to INT_MAX, which encodes to +** INT_MIN. The size of the hash part is limited by the maximum power of +** two that fits in a signed integer; that is (INT_MAX+1)/2. So, it is +** safe to add HFIRSTNODE to any index there.) +*/ + + +/* +** The array part of a table is represented by an inverted array of +** values followed by an array of tags, to avoid wasting space with +** padding. In between them there is an unsigned int, explained later. +** The 'array' pointer points between the two arrays, so that values are +** indexed with negative indices and tags with non-negative indices. + + Values Tags + -------------------------------------------------------- + ... | Value 1 | Value 0 |unsigned|0|1|... + -------------------------------------------------------- + ^ t->array + +** All accesses to 't->array' should be through the macros 'getArrTag' +** and 'getArrVal'. +*/ + +/* Computes the address of the tag for the abstract C-index 'k' */ +#define getArrTag(t,k) (cast(lu_byte*, (t)->array) + sizeof(unsigned) + (k)) + +/* Computes the address of the value for the abstract C-index 'k' */ +#define getArrVal(t,k) ((t)->array - 1 - (k)) + + +/* +** The unsigned between the two arrays is used as a hint for #t; +** see luaH_getn. It is stored there to avoid wasting space in +** the structure Table for tables with no array part. +*/ +#define lenhint(t) cast(unsigned*, (t)->array) + + +/* +** Move TValues to/from arrays, using C indices +*/ +#define arr2obj(h,k,val) \ + ((val)->tt_ = *getArrTag(h,(k)), (val)->value_ = *getArrVal(h,(k))) + +#define obj2arr(h,k,val) \ + (*getArrTag(h,(k)) = (val)->tt_, *getArrVal(h,(k)) = (val)->value_) + + +/* +** Often, we need to check the tag of a value before moving it. The +** following macros also move TValues to/from arrays, but receive the +** precomputed tag value or address as an extra argument. +*/ +#define farr2val(h,k,tag,res) \ + ((res)->tt_ = tag, (res)->value_ = *getArrVal(h,(k))) + +#define fval2arr(h,k,tag,val) \ + (*tag = (val)->tt_, *getArrVal(h,(k)) = (val)->value_) + + +LUAI_FUNC lu_byte luaH_get (Table *t, const TValue *key, TValue *res); +LUAI_FUNC lu_byte luaH_getshortstr (Table *t, TString *key, TValue *res); +LUAI_FUNC lu_byte luaH_getstr (Table *t, TString *key, TValue *res); +LUAI_FUNC lu_byte luaH_getint (Table *t, lua_Integer key, TValue *res); + +/* Special get for metamethods */ +LUAI_FUNC const TValue *luaH_Hgetshortstr (Table *t, TString *key); + +LUAI_FUNC int luaH_psetint (Table *t, lua_Integer key, TValue *val); +LUAI_FUNC int luaH_psetshortstr (Table *t, TString *key, TValue *val); +LUAI_FUNC int luaH_psetstr (Table *t, TString *key, TValue *val); +LUAI_FUNC int luaH_pset (Table *t, const TValue *key, TValue *val); + LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value); -LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); -LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); -LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value); + LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, - const TValue *slot, TValue *value); + TValue *value, int hres); LUAI_FUNC Table *luaH_new (lua_State *L); -LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, - unsigned int nhsize); -LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned nasize, + unsigned nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize); +LUAI_FUNC lu_mem luaH_size (Table *t); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); -LUAI_FUNC lua_Unsigned luaH_getn (Table *t); -LUAI_FUNC unsigned int luaH_realasize (const Table *t); +LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t); #if defined(LUA_DEBUG) diff --git a/lua/src/ltablib.c b/lua/src/ltablib.c index e6bc4d0..46ecb5e 100644 --- a/lua/src/ltablib.c +++ b/lua/src/ltablib.c @@ -18,6 +18,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" /* @@ -58,6 +59,16 @@ static void checktab (lua_State *L, int arg, int what) { } +static int tcreate (lua_State *L) { + lua_Unsigned sizeseq = (lua_Unsigned)luaL_checkinteger(L, 1); + lua_Unsigned sizerest = (lua_Unsigned)luaL_optinteger(L, 2, 0); + luaL_argcheck(L, sizeseq <= cast_uint(INT_MAX), 1, "out of range"); + luaL_argcheck(L, sizerest <= cast_uint(INT_MAX), 2, "out of range"); + lua_createtable(L, cast_int(sizeseq), cast_int(sizerest)); + return 1; +} + + static int tinsert (lua_State *L) { lua_Integer pos; /* where to insert new element */ lua_Integer e = aux_getn(L, 1, TAB_RW); @@ -196,7 +207,7 @@ static int tunpack (lua_State *L) { lua_Integer i = luaL_optinteger(L, 2, 1); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ - n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ + n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */ if (l_unlikely(n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))) return luaL_error(L, "too many results to unpack"); @@ -220,41 +231,26 @@ static int tunpack (lua_State *L) { */ -/* type for array indices */ +/* +** Type for array indices. These indices are always limited by INT_MAX, +** so it is safe to cast them to lua_Integer even for Lua 32 bits. +*/ typedef unsigned int IdxT; +/* Versions of lua_seti/lua_geti specialized for IdxT */ +#define geti(L,idt,idx) lua_geti(L, idt, l_castU2S(idx)) +#define seti(L,idt,idx) lua_seti(L, idt, l_castU2S(idx)) + + /* ** Produce a "random" 'unsigned int' to randomize pivot choice. This ** macro is used only when 'sort' detects a big imbalance in the result ** of a partition. (If you don't want/need this "randomness", ~0 is a ** good choice.) */ -#if !defined(l_randomizePivot) /* { */ - -#include - -/* size of 'e' measured in number of 'unsigned int's */ -#define sof(e) (sizeof(e) / sizeof(unsigned int)) - -/* -** Use 'time' and 'clock' as sources of "randomness". Because we don't -** know the types 'clock_t' and 'time_t', we cannot cast them to -** anything without risking overflows. A safe way to use their values -** is to copy them to an array of a known type and use the array values. -*/ -static unsigned int l_randomizePivot (void) { - clock_t c = clock(); - time_t t = time(NULL); - unsigned int buff[sof(c) + sof(t)]; - unsigned int i, rnd = 0; - memcpy(buff, &c, sof(c) * sizeof(unsigned int)); - memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); - for (i = 0; i < sof(buff); i++) - rnd += buff[i]; - return rnd; -} - +#if !defined(l_randomizePivot) +#define l_randomizePivot(L) luaL_makeseed(L) #endif /* } */ @@ -263,8 +259,8 @@ static unsigned int l_randomizePivot (void) { static void set2 (lua_State *L, IdxT i, IdxT j) { - lua_seti(L, 1, i); - lua_seti(L, 1, j); + seti(L, 1, i); + seti(L, 1, j); } @@ -301,15 +297,15 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) { /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ for (;;) { /* next loop: repeat ++i while a[i] < P */ - while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */ + while ((void)geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (l_unlikely(i == up - 1)) /* a[up - 1] < P == a[up - 1] */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } - /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ + /* after the loop, a[i] >= P and a[lo .. i - 1] < P (a) */ /* next loop: repeat --j while P < a[j] */ - while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { - if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */ + while ((void)geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (l_unlikely(j < i)) /* j <= i - 1 and a[j] > P, contradicts (a) */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[j] */ } @@ -333,7 +329,7 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) { */ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { IdxT r4 = (up - lo) / 4; /* range/4 */ - IdxT p = rnd % (r4 * 2) + (lo + r4); + IdxT p = (rnd ^ lo ^ up) % (r4 * 2) + (lo + r4); lua_assert(lo + r4 <= p && p <= up - r4); return p; } @@ -342,14 +338,13 @@ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { /* ** Quicksort algorithm (recursive function) */ -static void auxsort (lua_State *L, IdxT lo, IdxT up, - unsigned int rnd) { +static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned rnd) { while (lo < up) { /* loop for tail recursion */ IdxT p; /* Pivot index */ IdxT n; /* to be used later */ /* sort elements 'lo', 'p', and 'up' */ - lua_geti(L, 1, lo); - lua_geti(L, 1, up); + geti(L, 1, lo); + geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ set2(L, lo, up); /* swap a[lo] - a[up] */ else @@ -360,13 +355,13 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up, p = (lo + up)/2; /* middle element is a good pivot */ else /* for larger intervals, it is worth a random pivot */ p = choosePivot(lo, up, rnd); - lua_geti(L, 1, p); - lua_geti(L, 1, lo); + geti(L, 1, p); + geti(L, 1, lo); if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ set2(L, p, lo); /* swap a[p] - a[lo] */ else { lua_pop(L, 1); /* remove a[lo] */ - lua_geti(L, 1, up); + geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ set2(L, p, up); /* swap a[up] - a[p] */ else @@ -374,9 +369,9 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up, } if (up - lo == 2) /* only 3 elements? */ return; /* already sorted */ - lua_geti(L, 1, p); /* get middle element (Pivot) */ + geti(L, 1, p); /* get middle element (Pivot) */ lua_pushvalue(L, -1); /* push Pivot */ - lua_geti(L, 1, up - 1); /* push a[up - 1] */ + geti(L, 1, up - 1); /* push a[up - 1] */ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ p = partition(L, lo, up); /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ @@ -391,7 +386,7 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up, up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ } if ((up - lo) / 128 > n) /* partition too imbalanced? */ - rnd = l_randomizePivot(); /* try a new randomization */ + rnd = l_randomizePivot(L); /* try a new randomization */ } /* tail call auxsort(L, lo, up, rnd) */ } @@ -413,6 +408,7 @@ static int sort (lua_State *L) { static const luaL_Reg tab_funcs[] = { {"concat", tconcat}, + {"create", tcreate}, {"insert", tinsert}, {"pack", tpack}, {"unpack", tunpack}, diff --git a/lua/src/ltm.c b/lua/src/ltm.c index 07a0608..f2a373f 100644 --- a/lua/src/ltm.c +++ b/lua/src/ltm.c @@ -58,7 +58,7 @@ void luaT_init (lua_State *L) { ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { - const TValue *tm = luaH_getshortstr(events, ename); + const TValue *tm = luaH_Hgetshortstr(events, ename); lua_assert(event <= TM_EQ); if (notm(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<mt[ttype(o)]; } - return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); + return (mt ? luaH_Hgetshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); } @@ -92,7 +92,7 @@ const char *luaT_objtypename (lua_State *L, const TValue *o) { Table *mt; if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { - const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); + const TValue *name = luaH_Hgetshortstr(mt, luaS_new(L, "__name")); if (ttisstring(name)) /* is '__name' a string? */ return getstr(tsvalue(name)); /* use it as type name */ } @@ -116,8 +116,8 @@ void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, } -void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, - const TValue *p2, StkId res) { +lu_byte luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, StkId res) { ptrdiff_t result = savestack(L, res); StkId func = L->top.p; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ @@ -131,6 +131,7 @@ void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, luaD_callnoyield(L, func, 1); res = restorestack(L, result); setobjs2s(L, res, --L->top.p); /* move result to its place */ + return ttypetag(s2v(res)); /* return tag of the result */ } @@ -139,15 +140,16 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (notm(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ - if (notm(tm)) return 0; - luaT_callTMres(L, tm, p1, p2, res); - return 1; + if (notm(tm)) + return -1; /* tag method not found */ + else /* call tag method and return the tag of the result */ + return luaT_callTMres(L, tm, p1, p2, res); } void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { - if (l_unlikely(!callbinTM(L, p1, p2, res, event))) { + if (l_unlikely(callbinTM(L, p1, p2, res, event) < 0)) { switch (event) { case TM_BAND: case TM_BOR: case TM_BXOR: case TM_SHL: case TM_SHR: case TM_BNOT: { @@ -164,11 +166,14 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, } +/* +** The use of 'p1' after 'callbinTM' is safe because, when a tag +** method is not found, 'callbinTM' cannot change the stack. +*/ void luaT_tryconcatTM (lua_State *L) { - StkId top = L->top.p; - if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, - TM_CONCAT))) - luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); + StkId p1 = L->top.p - 2; /* first argument */ + if (l_unlikely(callbinTM(L, s2v(p1), s2v(p1 + 1), p1, TM_CONCAT) < 0)) + luaG_concaterror(L, s2v(p1), s2v(p1 + 1)); } @@ -191,28 +196,12 @@ void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, /* ** Calls an order tag method. -** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old -** behavior: if there is no '__le', try '__lt', based on l <= r iff -** !(r < l) (assuming a total order). If the metamethod yields during -** this substitution, the continuation has to know about it (to negate -** the result of rtop.p, event)) /* try original event */ - return !l_isfalse(s2v(L->top.p)); -#if defined(LUA_COMPAT_LT_LE) - else if (event == TM_LE) { - /* try '!(p2 < p1)' for '(p1 <= p2)' */ - L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ - if (callbinTM(L, p2, p1, L->top.p, TM_LT)) { - L->ci->callstatus ^= CIST_LEQ; /* clear mark */ - return l_isfalse(s2v(L->top.p)); - } - /* else error will remove this 'ci'; no need to clear mark */ - } -#endif + int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */ + if (tag >= 0) /* found tag method? */ + return !tagisfalse(tag); luaG_ordererror(L, p1, p2); /* no metamethod found */ return 0; /* to avoid warnings */ } @@ -235,36 +224,140 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, } -void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, - const Proto *p) { +/* +** Create a vararg table at the top of the stack, with 'n' elements +** starting at 'f'. +*/ +static void createvarargtab (lua_State *L, StkId f, int n) { + int i; + TValue key, value; + Table *t = luaH_new(L); + sethvalue(L, s2v(L->top.p), t); + L->top.p++; + luaH_resize(L, t, cast_uint(n), 1); + setsvalue(L, &key, luaS_new(L, "n")); /* key is "n" */ + setivalue(&value, n); /* value is n */ + /* No need to anchor the key: Due to the resize, the next operation + cannot trigger a garbage collection */ + luaH_set(L, t, &key, &value); /* t.n = n */ + for (i = 0; i < n; i++) + luaH_setint(L, t, i + 1, s2v(f + i)); + luaC_checkGC(L); +} + + +/* +** initial stack: func arg1 ... argn extra1 ... +** ^ ci->func ^ L->top +** final stack: func nil ... nil extra1 ... func arg1 ... argn +** ^ ci->func +*/ +static void buildhiddenargs (lua_State *L, CallInfo *ci, const Proto *p, + int totalargs, int nfixparams, int nextra) { int i; - int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */ - int nextra = actual - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); - /* copy function to the top of the stack */ + /* copy function to the top of the stack, after extra arguments */ setobjs2s(L, L->top.p++, ci->func.p); - /* move fixed parameters to the top of the stack */ + /* move fixed parameters to after the copied function */ for (i = 1; i <= nfixparams; i++) { setobjs2s(L, L->top.p++, ci->func.p + i); setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ } - ci->func.p += actual + 1; - ci->top.p += actual + 1; - lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); + ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */ + ci->top.p += totalargs + 1; } -void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { - int i; +void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { + int totalargs = cast_int(L->top.p - ci->func.p) - 1; + int nfixparams = p->numparams; + int nextra = totalargs - nfixparams; /* number of extra arguments */ + if (p->flag & PF_VATAB) { /* does it need a vararg table? */ + lua_assert(!(p->flag & PF_VAHID)); + createvarargtab(L, ci->func.p + nfixparams + 1, nextra); + /* move table to proper place (last parameter) */ + setobjs2s(L, ci->func.p + nfixparams + 1, L->top.p - 1); + } + else { /* no table */ + lua_assert(p->flag & PF_VAHID); + buildhiddenargs(L, ci, p, totalargs, nfixparams, nextra); + /* set vararg parameter to nil */ + setnilvalue(s2v(ci->func.p + nfixparams + 1)); + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); + } +} + + +void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) { int nextra = ci->u.l.nextraargs; + lua_Integer n; + if (tointegerns(rc, &n)) { /* integral value? */ + if (l_castS2U(n) - 1 < cast_uint(nextra)) { + StkId slot = ci->func.p - nextra + cast_int(n) - 1; + setobjs2s(((lua_State*)NULL), ra, slot); + return; + } + } + else if (ttisstring(rc)) { /* string value? */ + size_t len; + const char *s = getlstr(tsvalue(rc), len); + if (len == 1 && s[0] == 'n') { /* key is "n"? */ + setivalue(s2v(ra), nextra); + return; + } + } + setnilvalue(s2v(ra)); /* else produce nil */ +} + + +/* +** Get the number of extra arguments in a vararg function. If vararg +** table has been optimized away, that number is in the call info. +** Otherwise, get the field 'n' from the vararg table and check that it +** has a proper value (non-negative integer not larger than the stack +** limit). +*/ +static int getnumargs (lua_State *L, CallInfo *ci, Table *h) { + if (h == NULL) /* no vararg table? */ + return ci->u.l.nextraargs; + else { + TValue res; + if (luaH_getshortstr(h, luaS_new(L, "n"), &res) != LUA_VNUMINT || + l_castS2U(ivalue(&res)) > cast_uint(INT_MAX/2)) + luaG_runerror(L, "vararg table has no proper 'n'"); + return cast_int(ivalue(&res)); + } +} + + +/* +** Get 'wanted' vararg arguments and put them in 'where'. 'vatab' is +** the register of the vararg table or -1 if there is no vararg table. +*/ +void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted, + int vatab) { + Table *h = (vatab < 0) ? NULL : hvalue(s2v(ci->func.p + vatab + 1)); + int nargs = getnumargs(L, ci, h); /* number of available vararg args. */ + int i, touse; /* 'touse' is minimum between 'wanted' and 'nargs' */ if (wanted < 0) { - wanted = nextra; /* get all extra arguments available */ - checkstackGCp(L, nextra, where); /* ensure stack space */ - L->top.p = where + nextra; /* next instruction will need top */ + touse = wanted = nargs; /* get all extra arguments available */ + checkstackp(L, nargs, where); /* ensure stack space */ + L->top.p = where + nargs; /* next instruction will need top */ + } + else + touse = (nargs > wanted) ? wanted : nargs; + if (h == NULL) { /* no vararg table? */ + for (i = 0; i < touse; i++) /* get vararg values from the stack */ + setobjs2s(L, where + i, ci->func.p - nargs + i); + } + else { /* get vararg values from vararg table */ + for (i = 0; i < touse; i++) { + lu_byte tag = luaH_getint(h, i + 1, s2v(where + i)); + if (tagisempty(tag)) + setnilvalue(s2v(where + i)); + } } - for (i = 0; i < wanted && i < nextra; i++) - setobjs2s(L, where + i, ci->func.p - nextra + i); for (; i < wanted; i++) /* complete required results with nil */ setnilvalue(s2v(where + i)); } diff --git a/lua/src/ltm.h b/lua/src/ltm.h index 73b833c..07fc8c1 100644 --- a/lua/src/ltm.h +++ b/lua/src/ltm.h @@ -48,10 +48,10 @@ typedef enum { /* ** Mask with 1 in all fast-access methods. A 1 in any of these bits ** in the flag of a (meta)table means the metatable does not have the -** corresponding metamethod field. (Bit 7 of the flag is used for -** 'isrealasize'.) +** corresponding metamethod field. (Bit 6 of the flag indicates that +** the table is using the dummy node; bit 7 is used for 'isrealasize'.) */ -#define maskflags (~(~0u << (TM_EQ + 1))) +#define maskflags cast_byte(~(~0u << (TM_EQ + 1))) /* @@ -60,11 +60,12 @@ typedef enum { */ #define notm(tm) ttisnil(tm) +#define checknoTM(mt,e) ((mt) == NULL || (mt)->flags & (1u<<(e))) -#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ - ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) +#define gfasttm(g,mt,e) \ + (checknoTM(mt, e) ? NULL : luaT_gettm(mt, e, (g)->tmname[e])) -#define fasttm(l,et,e) gfasttm(G(l), et, e) +#define fasttm(l,mt,e) gfasttm(G(l), mt, e) #define ttypename(x) luaT_typenames_[(x) + 1] @@ -80,8 +81,8 @@ LUAI_FUNC void luaT_init (lua_State *L); LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3); -LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f, - const TValue *p1, const TValue *p2, StkId p3); +LUAI_FUNC lu_byte luaT_callTMres (lua_State *L, const TValue *f, + const TValue *p1, const TValue *p2, StkId p3); LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event); LUAI_FUNC void luaT_tryconcatTM (lua_State *L); @@ -94,10 +95,11 @@ LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int inv, int isfloat, TMS event); -LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, - struct CallInfo *ci, const Proto *p); -LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, - StkId where, int wanted); +LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, + const Proto *p); +LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc); +LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where, + int wanted, int vatab); #endif diff --git a/lua/src/lua.c b/lua/src/lua.c index 6da331f..5054583 100644 --- a/lua/src/lua.c +++ b/lua/src/lua.c @@ -19,6 +19,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" #if !defined(LUA_PROGNAME) @@ -302,7 +303,8 @@ static int collectargs (char **argv, int *first) { case '-': /* '--' */ if (argv[i][2] != '\0') /* extra characters after '--'? */ return has_error; /* invalid option */ - *first = i + 1; + /* if there is a script name, it comes after '--' */ + *first = (argv[i + 1] != NULL) ? i + 1 : 0; return args; case '\0': /* '-' */ return args; /* script "name" is '-' */ @@ -347,6 +349,7 @@ static int collectargs (char **argv, int *first) { */ static int runargs (lua_State *L, char **argv, int n) { int i; + lua_warning(L, "@off", 0); /* by default, Lua stand-alone has warnings off */ for (i = 1; i < n; i++) { int option = argv[i][1]; lua_assert(argv[i][0] == '-'); /* already checked */ @@ -431,30 +434,91 @@ static int handle_luainit (lua_State *L) { /* -** lua_readline defines how to show a prompt and then read a line from -** the standard input. -** lua_saveline defines how to "save" a read line in a "history". -** lua_freeline defines how to free a line read by lua_readline. +** * lua_initreadline initializes the readline system. +** * lua_readline defines how to show a prompt and then read a line from +** the standard input. +** * lua_saveline defines how to "save" a read line in a "history". +** * lua_freeline defines how to free a line read by lua_readline. */ + #if !defined(lua_readline) /* { */ +/* Otherwise, all previously listed functions should be defined. */ #if defined(LUA_USE_READLINE) /* { */ +/* Lua will be linked with '-lreadline' */ #include #include + #define lua_initreadline(L) ((void)L, rl_readline_name="lua") -#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) -#define lua_saveline(L,line) ((void)L, add_history(line)) -#define lua_freeline(L,b) ((void)L, free(b)) +#define lua_readline(buff,prompt) ((void)buff, readline(prompt)) +#define lua_saveline(line) add_history(line) +#define lua_freeline(line) free(line) -#else /* }{ */ +#else /* }{ */ +/* use dynamically loaded readline (or nothing) */ + +/* pointer to 'readline' function (if any) */ +typedef char *(*l_readlineT) (const char *prompt); +static l_readlineT l_readline = NULL; + +/* pointer to 'add_history' function (if any) */ +typedef void (*l_addhistT) (const char *string); +static l_addhistT l_addhist = NULL; + + +static char *lua_readline (char *buff, const char *prompt) { + if (l_readline != NULL) /* is there a 'readline'? */ + return (*l_readline)(prompt); /* use it */ + else { /* emulate 'readline' over 'buff' */ + fputs(prompt, stdout); + fflush(stdout); /* show prompt */ + return fgets(buff, LUA_MAXINPUT, stdin); /* read line */ + } +} -#define lua_initreadline(L) ((void)L) -#define lua_readline(L,b,p) \ - ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ - fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ -#define lua_saveline(L,line) { (void)L; (void)line; } -#define lua_freeline(L,b) { (void)L; (void)b; } + +static void lua_saveline (const char *line) { + if (l_addhist != NULL) /* is there an 'add_history'? */ + (*l_addhist)(line); /* use it */ + /* else nothing to be done */ +} + + +static void lua_freeline (char *line) { + if (l_readline != NULL) /* is there a 'readline'? */ + free(line); /* free line created by it */ + /* else 'lua_readline' used an automatic buffer; nothing to free */ +} + + +#if defined(LUA_USE_DLOPEN) && defined(LUA_READLINELIB) /* { */ +/* try to load 'readline' dynamically */ + +#include + +static void lua_initreadline (lua_State *L) { + void *lib = dlopen(LUA_READLINELIB, RTLD_NOW | RTLD_LOCAL); + if (lib == NULL) + lua_warning(L, "library '" LUA_READLINELIB "' not found", 0); + else { + const char **name = cast(const char**, dlsym(lib, "rl_readline_name")); + if (name != NULL) + *name = "lua"; + l_readline = cast(l_readlineT, cast_func(dlsym(lib, "readline"))); + l_addhist = cast(l_addhistT, cast_func(dlsym(lib, "add_history"))); + if (l_readline == NULL) + lua_warning(L, "unable to load 'readline'", 0); + } +} + +#else /* }{ */ +/* no dlopen or LUA_READLINELIB undefined */ + +/* Leave pointers with NULL */ +#define lua_initreadline(L) ((void)L) + +#endif /* } */ #endif /* } */ @@ -490,10 +554,8 @@ static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); - if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { - lua_pop(L, 1); + if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) return 1; - } } return 0; /* else... */ } @@ -504,21 +566,17 @@ static int incomplete (lua_State *L, int status) { */ static int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; - char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); - int readstatus = lua_readline(L, b, prmt); - if (readstatus == 0) - return 0; /* no input (prompt will be popped by caller) */ + char *b = lua_readline(buffer, prmt); lua_pop(L, 1); /* remove prompt */ + if (b == NULL) + return 0; /* no input */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ - if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ - lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ - else - lua_pushlstring(L, b, l); - lua_freeline(L, b); + lua_pushlstring(L, b, l); + lua_freeline(b); return 1; } @@ -531,32 +589,44 @@ static int addreturn (lua_State *L) { const char *line = lua_tostring(L, -1); /* original line */ const char *retline = lua_pushfstring(L, "return %s;", line); int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); - if (status == LUA_OK) { + if (status == LUA_OK) lua_remove(L, -2); /* remove modified line */ - if (line[0] != '\0') /* non empty? */ - lua_saveline(L, line); /* keep history */ - } else lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ return status; } +static void checklocal (const char *line) { + static const size_t szloc = sizeof("local") - 1; + static const char space[] = " \t"; + line += strspn(line, space); /* skip spaces */ + if (strncmp(line, "local", szloc) == 0 && /* "local"? */ + strchr(space, *(line + szloc)) != NULL) { /* followed by a space? */ + lua_writestringerror("%s\n", + "warning: locals do not survive across lines in interactive mode"); + } +} + + /* -** Read multiple lines until a complete Lua statement +** Read multiple lines until a complete Lua statement or an error not +** for an incomplete statement. Start with first line already read in +** the stack. */ static int multiline (lua_State *L) { + size_t len; + const char *line = lua_tolstring(L, 1, &len); /* get first line */ + checklocal(line); for (;;) { /* repeat until gets a complete statement */ - size_t len; - const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ - if (!incomplete(L, status) || !pushline(L, 0)) { - lua_saveline(L, line); /* keep history */ - return status; /* cannot or should not try to add continuation line */ - } + if (!incomplete(L, status) || !pushline(L, 0)) + return status; /* should not or cannot try to add continuation line */ + lua_remove(L, -2); /* remove error message (from incomplete line) */ lua_pushliteral(L, "\n"); /* add newline... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ + line = lua_tolstring(L, 1, &len); /* get what is has */ } } @@ -568,12 +638,16 @@ static int multiline (lua_State *L) { ** in the top of the stack. */ static int loadline (lua_State *L) { + const char *line; int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ + line = lua_tostring(L, 1); + if (line[0] != '\0') /* non empty? */ + lua_saveline(line); /* keep history */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); return status; @@ -618,6 +692,10 @@ static void doREPL (lua_State *L) { /* }================================================================== */ +#if !defined(luai_openlibs) +#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0) +#endif + /* ** Main body of stand-alone interpreter (to be called in protected mode). @@ -640,15 +718,15 @@ static int pmain (lua_State *L) { lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } - luaL_openlibs(L); /* open standard libraries */ + luai_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ lua_gc(L, LUA_GCRESTART); /* start GC... */ - lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ + lua_gc(L, LUA_GCGEN); /* ...in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } - if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */ + if (!runargs(L, argv, optlim)) /* execute arguments -e, -l, and -W */ return 0; /* something failed */ if (script > 0) { /* execute main script (if there is one) */ if (handle_script(L, argv + script) != LUA_OK) diff --git a/lua/src/lua.h b/lua/src/lua.h index f050dac..ab473dc 100644 --- a/lua/src/lua.h +++ b/lua/src/lua.h @@ -1,7 +1,7 @@ /* ** $Id: lua.h $ ** Lua - A Scripting Language -** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** Lua.org, PUC-Rio, Brazil (www.lua.org) ** See Copyright Notice at the end of this file */ @@ -13,20 +13,19 @@ #include -#include "luaconf.h" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" -#define LUA_VERSION_MAJOR "5" -#define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "7" +#define LUA_VERSION_MAJOR_N 5 +#define LUA_VERSION_MINOR_N 5 +#define LUA_VERSION_RELEASE_N 0 -#define LUA_VERSION_NUM 504 -#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 7) +#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) -#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR -#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2024 Lua.org, PUC-Rio" -#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + +#include "luaconf.h" /* mark for precompiled code ('Lua') */ @@ -38,10 +37,10 @@ /* ** Pseudo-indices -** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty -** space after that to help overflow detection) +** (The stack size is limited to INT_MAX/2; we keep some free empty +** space after that to help overflow detection.) */ -#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000)) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) @@ -81,9 +80,10 @@ typedef struct lua_State lua_State; /* predefined values in the registry */ -#define LUA_RIDX_MAINTHREAD 1 +/* index 1 is reserved for the reference mechanism */ #define LUA_RIDX_GLOBALS 2 -#define LUA_RIDX_LAST LUA_RIDX_GLOBALS +#define LUA_RIDX_MAINTHREAD 3 +#define LUA_RIDX_LAST 3 /* type of numbers in Lua */ @@ -160,11 +160,10 @@ extern const char lua_ident[]; /* ** state manipulation */ -LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud, unsigned seed); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); LUA_API int (lua_closethread) (lua_State *L, lua_State *from); -LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */ LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); @@ -245,6 +244,8 @@ LUA_API void (lua_pushnil) (lua_State *L); LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushexternalstring) (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud); LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); @@ -324,7 +325,7 @@ LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); /* -** garbage-collection function and options +** garbage-collection options */ #define LUA_GCSTOP 0 @@ -333,11 +334,28 @@ LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); #define LUA_GCCOUNT 3 #define LUA_GCCOUNTB 4 #define LUA_GCSTEP 5 -#define LUA_GCSETPAUSE 6 -#define LUA_GCSETSTEPMUL 7 -#define LUA_GCISRUNNING 9 -#define LUA_GCGEN 10 -#define LUA_GCINC 11 +#define LUA_GCISRUNNING 6 +#define LUA_GCGEN 7 +#define LUA_GCINC 8 +#define LUA_GCPARAM 9 + + +/* +** garbage-collection parameters +*/ +/* parameters for generational mode */ +#define LUA_GCPMINORMUL 0 /* control minor collections */ +#define LUA_GCPMAJORMINOR 1 /* control shift major->minor */ +#define LUA_GCPMINORMAJOR 2 /* control shift minor->major */ + +/* parameters for incremental mode */ +#define LUA_GCPPAUSE 3 /* size of pause between successive GCs */ +#define LUA_GCPSTEPMUL 4 /* GC "speed" */ +#define LUA_GCPSTEPSIZE 5 /* GC granularity */ + +/* number of parameters */ +#define LUA_GCPN 6 + LUA_API int (lua_gc) (lua_State *L, int what, ...); @@ -353,7 +371,9 @@ LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); -LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); +#define LUA_N2SBUFFSZ 64 +LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff); +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); @@ -412,19 +432,12 @@ LUA_API void (lua_closeslot) (lua_State *L, int idx); ** compatibility macros ** =============================================================== */ -#if defined(LUA_COMPAT_APIINTCASTS) - -#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) -#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) -#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) - -#endif #define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) #define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) #define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) -#define LUA_NUMTAGS LUA_NUMTYPES +#define lua_resetthread(L) lua_closethread(L,NULL) /* }============================================================== */ @@ -470,7 +483,6 @@ LUA_API lua_Hook (lua_gethook) (lua_State *L); LUA_API int (lua_gethookmask) (lua_State *L); LUA_API int (lua_gethookcount) (lua_State *L); -LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit); struct lua_Debug { int event; @@ -485,9 +497,10 @@ struct lua_Debug { unsigned char nups; /* (u) number of upvalues */ unsigned char nparams;/* (u) number of parameters */ char isvararg; /* (u) */ + unsigned char extraargs; /* (t) number of extra arguments */ char istailcall; /* (t) */ - unsigned short ftransfer; /* (r) index of first value transferred */ - unsigned short ntransfer; /* (r) number of transferred values */ + int ftransfer; /* (r) index of first value transferred */ + int ntransfer; /* (r) number of transferred values */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ @@ -496,8 +509,19 @@ struct lua_Debug { /* }====================================================================== */ +#define LUAI_TOSTRAUX(x) #x +#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x) + +#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N) +#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE + + /****************************************************************************** -* Copyright (C) 1994-2024 Lua.org, PUC-Rio. +* Copyright (C) 1994-2025 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/lua/src/lua.hpp b/lua/src/lua.hpp index ec417f5..2853364 100644 --- a/lua/src/lua.hpp +++ b/lua/src/lua.hpp @@ -1,6 +1,7 @@ // lua.hpp // Lua header files for C++ -// <> not supplied automatically because Lua also compiles as C++ +// 'extern "C" not supplied automatically in lua.h and other headers +// because Lua also compiles as C++ extern "C" { #include "lua.h" diff --git a/lua/src/luac.c b/lua/src/luac.c index 5f4a141..4a2016a 100644 --- a/lua/src/luac.c +++ b/lua/src/luac.c @@ -18,6 +18,7 @@ #include "lua.h" #include "lauxlib.h" +#include "lapi.h" #include "ldebug.h" #include "lobject.h" #include "lopcodes.h" @@ -346,6 +347,8 @@ static void PrintCode(const Proto* f) int bx=GETARG_Bx(i); int sb=GETARG_sB(i); int sc=GETARG_sC(i); + int vb=GETARG_vB(i); + int vc=GETARG_vC(i); int sbx=GETARG_sBx(i); int isk=GETARG_k(i); int line=luaG_getfuncline(f,pc); @@ -427,8 +430,8 @@ static void PrintCode(const Proto* f) if (isk) { printf(" "); PrintConstant(f,c); } break; case OP_NEWTABLE: - printf("%d %d %d",a,b,c); - printf(COMMENT "%d",c+EXTRAARGC); + printf("%d %d %d%s",a,vb,vc,ISK); + printf(COMMENT "%d",vc+EXTRAARGC); break; case OP_SELF: printf("%d %d %d%s",a,b,c,ISK); @@ -477,10 +480,10 @@ static void PrintCode(const Proto* f) printf("%d %d %d",a,b,c); printf(COMMENT); PrintConstant(f,c); break; - case OP_SHRI: + case OP_SHLI: printf("%d %d %d",a,b,sc); break; - case OP_SHLI: + case OP_SHRI: printf("%d %d %d",a,b,sc); break; case OP_ADD: @@ -632,7 +635,7 @@ static void PrintCode(const Proto* f) printf(COMMENT "to %d",pc-bx+2); break; case OP_SETLIST: - printf("%d %d %d",a,b,c); + printf("%d %d %d%s",a,vb,vc,ISK); if (isk) printf(COMMENT "%d",c+EXTRAARGC); break; case OP_CLOSURE: @@ -640,10 +643,18 @@ static void PrintCode(const Proto* f) printf(COMMENT "%p",VOID(f->p[bx])); break; case OP_VARARG: - printf("%d %d",a,c); + printf("%d %d %d%s",a,b,c,ISK); printf(COMMENT); if (c==0) printf("all out"); else printf("%d out",c-1); break; + case OP_GETVARG: + printf("%d %d %d",a,b,c); + break; + case OP_ERRNNIL: + printf("%d %d",a,bx); + printf(COMMENT); + if (bx==0) printf("?"); else PrintConstant(f,bx-1); + break; case OP_VARARGPREP: printf("%d",a); break; @@ -661,7 +672,6 @@ static void PrintCode(const Proto* f) } } - #define SS(x) ((x==1)?"":"s") #define S(x) (int)(x),SS(x) @@ -679,7 +689,7 @@ static void PrintHeader(const Proto* f) f->linedefined,f->lastlinedefined, S(f->sizecode),VOID(f)); printf("%d%s param%s, %d slot%s, %d upvalue%s, ", - (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams), + (int)(f->numparams),isvararg(f)?"+":"",SS(f->numparams), S(f->maxstacksize),S(f->sizeupvalues)); printf("%d local%s, %d constant%s, %d function%s\n", S(f->sizelocvars),S(f->sizek),S(f->sizep)); diff --git a/lua/src/luaconf.h b/lua/src/luaconf.h index 33bb580..96a7780 100644 --- a/lua/src/luaconf.h +++ b/lua/src/luaconf.h @@ -58,15 +58,26 @@ #endif +/* +** When POSIX DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone +** application will try to dynamically link a 'readline' facility +** for its REPL. In that case, LUA_READLINELIB is the name of the +** library it will look for those facilities. If lua.c cannot open +** the specified library, it will generate a warning and then run +** without 'readline'. If that macro is not defined, lua.c will not +** use 'readline'. +*/ #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_READLINELIB "libreadline.so" #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX -#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#define LUA_USE_DLOPEN /* macOS does not need -ldl */ +#define LUA_READLINELIB "libedit.dylib" #endif @@ -76,6 +87,11 @@ #endif +#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) +#error "POSIX is not compatible with C89" +#endif + + /* @@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. */ @@ -122,7 +138,7 @@ /* @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. */ -#define LUA_32BITS 0 +/* #define LUA_32BITS */ /* @@ -137,7 +153,7 @@ #endif -#if LUA_32BITS /* { */ +#if defined(LUA_32BITS) /* { */ /* ** 32-bit integers and 'float' */ @@ -303,32 +319,13 @@ ** More often than not the libs go together with the core. */ #define LUALIB_API LUA_API -#define LUAMOD_API LUA_API - -/* -@@ LUAI_FUNC is a mark for all extern functions that are not to be -** exported to outside modules. -@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, -** none of which to be exported to outside modules (LUAI_DDEF for -** definitions and LUAI_DDEC for declarations). -** CHANGE them if you need to mark them in some special way. Elf/gcc -** (versions 3.2 and later) mark them as "hidden" to optimize access -** when Lua is compiled as a shared library. Not all elf targets support -** this attribute. Unfortunately, gcc does not offer a way to check -** whether the target offers that support, and those without support -** give a warning about it. To avoid these warnings, change to the -** default definition. -*/ -#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ - defined(__ELF__) /* { */ -#define LUAI_FUNC __attribute__((visibility("internal"))) extern -#else /* }{ */ -#define LUAI_FUNC extern -#endif /* } */ - -#define LUAI_DDEC(dec) LUAI_FUNC dec -#define LUAI_DDEF /* empty */ +#if defined(__cplusplus) +/* Lua uses the "C name" when calling open functions */ +#define LUAMOD_API extern "C" +#else +#define LUAMOD_API LUA_API +#endif /* }================================================================== */ @@ -340,11 +337,10 @@ */ /* -@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. -** You can define it to get all options, or change specific options -** to fit your specific needs. +@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word */ -#if defined(LUA_COMPAT_5_3) /* { */ +#define LUA_COMPAT_GLOBAL + /* @@ LUA_COMPAT_MATHLIB controls the presence of several deprecated @@ -352,23 +348,7 @@ ** (These functions were already officially removed in 5.3; ** nevertheless they are still available here.) */ -#define LUA_COMPAT_MATHLIB - -/* -@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for -** manipulating other integer types (lua_pushunsigned, lua_tounsigned, -** luaL_checkint, luaL_checklong, etc.) -** (These macros were also officially removed in 5.3, but they are still -** available here.) -*/ -#define LUA_COMPAT_APIINTCASTS - - -/* -@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod -** using '__lt'. -*/ -#define LUA_COMPAT_LT_LE +/* #define LUA_COMPAT_MATHLIB */ /* @@ -385,8 +365,6 @@ #define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) -#endif /* } */ - /* }================================================================== */ @@ -405,35 +383,23 @@ @@ l_floatatt(x) corrects float attribute 'x' to the proper float type ** by prefixing it with one of FLT/DBL/LDBL. @@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. -@@ LUA_NUMBER_FMT is the format for writing floats. -@@ lua_number2str converts a float to a string. +@@ LUA_NUMBER_FMT is the format for writing floats with the maximum +** number of digits that respects tostring(tonumber(numeral)) == numeral. +** (That would be floor(log10(2^n)), where n is the number of bits in +** the float mantissa.) +@@ LUA_NUMBER_FMT_N is the format for writing floats with the minimum +** number of digits that ensures tonumber(tostring(number)) == number. +** (That would be LUA_NUMBER_FMT+2.) @@ l_mathop allows the addition of an 'l' or 'f' to all math operations. @@ l_floor takes the floor of a float. @@ lua_str2number converts a decimal numeral to a number. */ -/* The following definitions are good for most cases here */ +/* The following definition is good for most cases here */ #define l_floor(x) (l_mathop(floor)(x)) -#define lua_number2str(s,sz,n) \ - l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) - -/* -@@ lua_numbertointeger converts a float number with an integral value -** to an integer, or returns 0 if float is not within the range of -** a lua_Integer. (The range comparisons are tricky because of -** rounding. The tests here assume a two-complement representation, -** where MININTEGER always has an exact representation as a float; -** MAXINTEGER may not have one, and therefore its conversion to float -** may have an ill-defined value.) -*/ -#define lua_numbertointeger(n,p) \ - ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ - (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ - (*(p) = (LUA_INTEGER)(n), 1)) - /* now the variable definitions */ @@ -447,6 +413,7 @@ #define LUA_NUMBER_FRMLEN "" #define LUA_NUMBER_FMT "%.7g" +#define LUA_NUMBER_FMT_N "%.9g" #define l_mathop(op) op##f @@ -463,6 +430,7 @@ #define LUA_NUMBER_FRMLEN "L" #define LUA_NUMBER_FMT "%.19Lg" +#define LUA_NUMBER_FMT_N "%.21Lg" #define l_mathop(op) op##l @@ -477,7 +445,8 @@ #define LUAI_UACNUMBER double #define LUA_NUMBER_FRMLEN "" -#define LUA_NUMBER_FMT "%.14g" +#define LUA_NUMBER_FMT "%.15g" +#define LUA_NUMBER_FMT_N "%.17g" #define l_mathop(op) op @@ -691,13 +660,6 @@ #endif -#if defined(LUA_CORE) || defined(LUA_LIB) -/* shorter names for Lua's own use */ -#define l_likely(x) luai_likely(x) -#define l_unlikely(x) luai_unlikely(x) -#endif - - /* }================================================================== */ @@ -722,10 +684,7 @@ @@ LUA_USE_APICHECK turns on several consistency checks on the C API. ** Define it as a help when debugging C code. */ -#if defined(LUA_USE_APICHECK) -#include -#define luai_apicheck(l,e) assert(e) -#endif +/* #define LUA_USE_APICHECK */ /* }================================================================== */ @@ -738,20 +697,6 @@ ** ===================================================================== */ -/* -@@ LUAI_MAXSTACK limits the size of the Lua stack. -** CHANGE it if you need a different limit. This limit is arbitrary; -** its only purpose is to stop Lua from consuming unlimited stack -** space (and to reserve some numbers for pseudo-indices). -** (It must fit into max(size_t)/32 and max(int)/2.) -*/ -#if LUAI_IS32INT -#define LUAI_MAXSTACK 1000000 -#else -#define LUAI_MAXSTACK 15000 -#endif - - /* @@ LUA_EXTRASPACE defines the size of a raw memory area associated with ** a Lua state with very fast access. @@ -796,7 +741,5 @@ - - #endif diff --git a/lua/src/lualib.h b/lua/src/lualib.h index 2625529..068f60a 100644 --- a/lua/src/lualib.h +++ b/lua/src/lualib.h @@ -14,39 +14,52 @@ /* version suffix for environment variable names */ #define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR - +#define LUA_GLIBK 1 LUAMOD_API int (luaopen_base) (lua_State *L); +#define LUA_LOADLIBNAME "package" +#define LUA_LOADLIBK (LUA_GLIBK << 1) +LUAMOD_API int (luaopen_package) (lua_State *L); + + #define LUA_COLIBNAME "coroutine" +#define LUA_COLIBK (LUA_LOADLIBK << 1) LUAMOD_API int (luaopen_coroutine) (lua_State *L); -#define LUA_TABLIBNAME "table" -LUAMOD_API int (luaopen_table) (lua_State *L); +#define LUA_DBLIBNAME "debug" +#define LUA_DBLIBK (LUA_COLIBK << 1) +LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_IOLIBNAME "io" +#define LUA_IOLIBK (LUA_DBLIBK << 1) LUAMOD_API int (luaopen_io) (lua_State *L); +#define LUA_MATHLIBNAME "math" +#define LUA_MATHLIBK (LUA_IOLIBK << 1) +LUAMOD_API int (luaopen_math) (lua_State *L); + #define LUA_OSLIBNAME "os" +#define LUA_OSLIBK (LUA_MATHLIBK << 1) LUAMOD_API int (luaopen_os) (lua_State *L); #define LUA_STRLIBNAME "string" +#define LUA_STRLIBK (LUA_OSLIBK << 1) LUAMOD_API int (luaopen_string) (lua_State *L); +#define LUA_TABLIBNAME "table" +#define LUA_TABLIBK (LUA_STRLIBK << 1) +LUAMOD_API int (luaopen_table) (lua_State *L); + #define LUA_UTF8LIBNAME "utf8" +#define LUA_UTF8LIBK (LUA_TABLIBK << 1) LUAMOD_API int (luaopen_utf8) (lua_State *L); -#define LUA_MATHLIBNAME "math" -LUAMOD_API int (luaopen_math) (lua_State *L); - -#define LUA_DBLIBNAME "debug" -LUAMOD_API int (luaopen_debug) (lua_State *L); - -#define LUA_LOADLIBNAME "package" -LUAMOD_API int (luaopen_package) (lua_State *L); +/* open selected libraries */ +LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload); -/* open all previous libraries */ -LUALIB_API void (luaL_openlibs) (lua_State *L); +/* open all libraries */ +#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0) #endif diff --git a/lua/src/lundump.c b/lua/src/lundump.c index e8d92a8..3b61cc8 100644 --- a/lua/src/lundump.c +++ b/lua/src/lundump.c @@ -21,6 +21,7 @@ #include "lmem.h" #include "lobject.h" #include "lstring.h" +#include "ltable.h" #include "lundump.h" #include "lzio.h" @@ -34,6 +35,10 @@ typedef struct { lua_State *L; ZIO *Z; const char *name; + Table *h; /* list for string reuse */ + size_t offset; /* current position relative to beginning of dump */ + lua_Unsigned nstr; /* number of strings in the list */ + lu_byte fixed; /* dump is fixed in memory */ } LoadState; @@ -47,11 +52,33 @@ static l_noret error (LoadState *S, const char *why) { ** All high-level loads go through loadVector; you can change it to ** adapt to the endianness of the input */ -#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) +#define loadVector(S,b,n) loadBlock(S,b,cast_sizet(n)*sizeof((b)[0])) static void loadBlock (LoadState *S, void *b, size_t size) { if (luaZ_read(S->Z, b, size) != 0) error(S, "truncated chunk"); + S->offset += size; +} + + +static void loadAlign (LoadState *S, unsigned align) { + unsigned padding = align - cast_uint(S->offset % align); + if (padding < align) { /* (padding == align) means no padding */ + lua_Integer paddingContent; + loadBlock(S, &paddingContent, padding); + lua_assert(S->offset % align == 0); + } +} + + +#define getaddr(S,n,t) cast(t *, getaddr_(S,cast_sizet(n) * sizeof(t))) + +static const void *getaddr_ (LoadState *S, size_t size) { + const void *block = luaZ_getaddr(S->Z, size); + S->offset += size; + if (block == NULL) + error(S, "truncated fixed buffer"); + return block; } @@ -62,34 +89,36 @@ static lu_byte loadByte (LoadState *S) { int b = zgetc(S->Z); if (b == EOZ) error(S, "truncated chunk"); + S->offset++; return cast_byte(b); } -static size_t loadUnsigned (LoadState *S, size_t limit) { - size_t x = 0; +static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) { + lua_Unsigned x = 0; int b; limit >>= 7; do { b = loadByte(S); - if (x >= limit) + if (x > limit) error(S, "integer overflow"); x = (x << 7) | (b & 0x7f); - } while ((b & 0x80) == 0); + } while ((b & 0x80) != 0); return x; } static size_t loadSize (LoadState *S) { - return loadUnsigned(S, MAX_SIZET); + return cast_sizet(loadVarint(S, MAX_SIZE)); } static int loadInt (LoadState *S) { - return cast_int(loadUnsigned(S, INT_MAX)); + return cast_int(loadVarint(S, cast_sizet(INT_MAX))); } + static lua_Number loadNumber (LoadState *S) { lua_Number x; loadVar(S, x); @@ -98,58 +127,79 @@ static lua_Number loadNumber (LoadState *S) { static lua_Integer loadInteger (LoadState *S) { - lua_Integer x; - loadVar(S, x); - return x; + lua_Unsigned cx = loadVarint(S, LUA_MAXUNSIGNED); + /* decode unsigned to signed */ + if ((cx & 1) != 0) + return l_castU2S(~(cx >> 1)); + else + return l_castU2S(cx >> 1); } /* -** Load a nullable string into prototype 'p'. +** Load a nullable string into slot 'sl' from prototype 'p'. The +** assignment to the slot and the barrier must be performed before any +** possible GC activity, to anchor the string. (Both 'loadVector' and +** 'luaH_setint' can call the GC.) */ -static TString *loadStringN (LoadState *S, Proto *p) { +static void loadString (LoadState *S, Proto *p, TString **sl) { lua_State *L = S->L; TString *ts; + TValue sv; size_t size = loadSize(S); - if (size == 0) /* no string? */ - return NULL; - else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ - char buff[LUAI_MAXSHORTLEN]; - loadVector(S, buff, size); /* load string into buffer */ - ts = luaS_newlstr(L, buff, size); /* create string */ + if (size == 0) { /* previously saved string? */ + lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */ + TValue stv; + if (idx == 0) { /* no string? */ + lua_assert(*sl == NULL); /* must be prefilled */ + return; + } + if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING) + error(S, "invalid string index"); + *sl = ts = tsvalue(&stv); /* get its value */ + luaC_objbarrier(L, p, ts); + return; /* do not save it again */ } - else { /* long string */ - ts = luaS_createlngstrobj(L, size); /* create string */ - setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */ - luaD_inctop(L); - loadVector(S, getlngstr(ts), size); /* load directly in final place */ - L->top.p--; /* pop string */ + else if ((size -= 1) <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */ + loadVector(S, buff, size + 1); /* load string into buffer */ + *sl = ts = luaS_newlstr(L, buff, size); /* create string */ + luaC_objbarrier(L, p, ts); } - luaC_objbarrier(L, p, ts); - return ts; -} - - -/* -** Load a non-nullable string into prototype 'p'. -*/ -static TString *loadString (LoadState *S, Proto *p) { - TString *st = loadStringN(S, p); - if (st == NULL) - error(S, "bad format for constant string"); - return st; + else if (S->fixed) { /* for a fixed buffer, use a fixed string */ + const char *s = getaddr(S, size + 1, char); /* get content address */ + *sl = ts = luaS_newextlstr(L, s, size, NULL, NULL); + luaC_objbarrier(L, p, ts); + } + else { /* create internal copy */ + *sl = ts = luaS_createlngstrobj(L, size); /* create string */ + luaC_objbarrier(L, p, ts); + loadVector(S, getlngstr(ts), size + 1); /* load directly in final place */ + } + /* add string to list of saved strings */ + S->nstr++; + setsvalue(L, &sv, ts); + luaH_setint(L, S->h, l_castU2S(S->nstr), &sv); + luaC_objbarrierback(L, obj2gco(S->h), ts); } static void loadCode (LoadState *S, Proto *f) { int n = loadInt(S); - f->code = luaM_newvectorchecked(S->L, n, Instruction); - f->sizecode = n; - loadVector(S, f->code, n); + loadAlign(S, sizeof(f->code[0])); + if (S->fixed) { + f->code = getaddr(S, n, Instruction); + f->sizecode = n; + } + else { + f->code = luaM_newvectorchecked(S->L, n, Instruction); + f->sizecode = n; + loadVector(S, f->code, n); + } } -static void loadFunction(LoadState *S, Proto *f, TString *psource); +static void loadFunction(LoadState *S, Proto *f); static void loadConstants (LoadState *S, Proto *f) { @@ -179,10 +229,16 @@ static void loadConstants (LoadState *S, Proto *f) { setivalue(o, loadInteger(S)); break; case LUA_VSHRSTR: - case LUA_VLNGSTR: - setsvalue2n(S->L, o, loadString(S, f)); + case LUA_VLNGSTR: { + lua_assert(f->source == NULL); + loadString(S, f, &f->source); /* use 'source' to anchor string */ + if (f->source == NULL) + error(S, "bad format for constant string"); + setsvalue2n(S->L, o, f->source); /* save it in the right place */ + f->source = NULL; break; - default: lua_assert(0); + } + default: error(S, "invalid constant"); } } } @@ -198,7 +254,7 @@ static void loadProtos (LoadState *S, Proto *f) { for (i = 0; i < n; i++) { f->p[i] = luaF_newproto(S->L); luaC_objbarrier(S->L, f, f->p[i]); - loadFunction(S, f->p[i], f->source); + loadFunction(S, f->p[i]); } } @@ -210,8 +266,8 @@ static void loadProtos (LoadState *S, Proto *f) { ** in that case all prototypes must be consistent for the GC. */ static void loadUpvalues (LoadState *S, Proto *f) { - int i, n; - n = loadInt(S); + int i; + int n = loadInt(S); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); f->sizeupvalues = n; for (i = 0; i < n; i++) /* make array valid for GC */ @@ -225,17 +281,29 @@ static void loadUpvalues (LoadState *S, Proto *f) { static void loadDebug (LoadState *S, Proto *f) { - int i, n; - n = loadInt(S); - f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); - f->sizelineinfo = n; - loadVector(S, f->lineinfo, n); + int i; + int n = loadInt(S); + if (S->fixed) { + f->lineinfo = getaddr(S, n, ls_byte); + f->sizelineinfo = n; + } + else { + f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); + f->sizelineinfo = n; + loadVector(S, f->lineinfo, n); + } n = loadInt(S); - f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); - f->sizeabslineinfo = n; - for (i = 0; i < n; i++) { - f->abslineinfo[i].pc = loadInt(S); - f->abslineinfo[i].line = loadInt(S); + if (n > 0) { + loadAlign(S, sizeof(int)); + if (S->fixed) { + f->abslineinfo = getaddr(S, n, AbsLineInfo); + f->sizeabslineinfo = n; + } + else { + f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); + f->sizeabslineinfo = n; + loadVector(S, f->abslineinfo, n); + } } n = loadInt(S); f->locvars = luaM_newvectorchecked(S->L, n, LocVar); @@ -243,7 +311,7 @@ static void loadDebug (LoadState *S, Proto *f) { for (i = 0; i < n; i++) f->locvars[i].varname = NULL; for (i = 0; i < n; i++) { - f->locvars[i].varname = loadStringN(S, f); + loadString(S, f, &f->locvars[i].varname); f->locvars[i].startpc = loadInt(S); f->locvars[i].endpc = loadInt(S); } @@ -251,23 +319,24 @@ static void loadDebug (LoadState *S, Proto *f) { if (n != 0) /* does it have debug information? */ n = f->sizeupvalues; /* must be this many */ for (i = 0; i < n; i++) - f->upvalues[i].name = loadStringN(S, f); + loadString(S, f, &f->upvalues[i].name); } -static void loadFunction (LoadState *S, Proto *f, TString *psource) { - f->source = loadStringN(S, f); - if (f->source == NULL) /* no source in dump? */ - f->source = psource; /* reuse parent's source */ +static void loadFunction (LoadState *S, Proto *f) { f->linedefined = loadInt(S); f->lastlinedefined = loadInt(S); f->numparams = loadByte(S); - f->is_vararg = loadByte(S); + /* get only the meaningful flags */ + f->flag = cast_byte(loadByte(S) & ~PF_FIXED); + if (S->fixed) + f->flag |= PF_FIXED; /* signal that code is fixed */ f->maxstacksize = loadByte(S); loadCode(S, f); loadConstants(S, f); loadUpvalues(S, f); loadProtos(S, f); + loadString(S, f, &f->source); loadDebug(S, f); } @@ -281,13 +350,29 @@ static void checkliteral (LoadState *S, const char *s, const char *msg) { } -static void fchecksize (LoadState *S, size_t size, const char *tname) { - if (loadByte(S) != size) - error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); +static l_noret numerror (LoadState *S, const char *what, const char *tname) { + const char *msg = luaO_pushfstring(S->L, "%s %s mismatch", tname, what); + error(S, msg); +} + + +static void checknumsize (LoadState *S, int size, const char *tname) { + if (size != loadByte(S)) + numerror(S, "size", tname); +} + + +static void checknumformat (LoadState *S, int eq, const char *tname) { + if (!eq) + numerror(S, "format", tname); } -#define checksize(S,t) fchecksize(S,sizeof(t),#t) +#define checknum(S,tvar,value,tname) \ + { tvar i; checknumsize(S, sizeof(i), tname); \ + loadVar(S, i); \ + checknumformat(S, i == value, tname); } + static void checkHeader (LoadState *S) { /* skip 1st char (already read and checked) */ @@ -297,39 +382,43 @@ static void checkHeader (LoadState *S) { if (loadByte(S) != LUAC_FORMAT) error(S, "format mismatch"); checkliteral(S, LUAC_DATA, "corrupted chunk"); - checksize(S, Instruction); - checksize(S, lua_Integer); - checksize(S, lua_Number); - if (loadInteger(S) != LUAC_INT) - error(S, "integer format mismatch"); - if (loadNumber(S) != LUAC_NUM) - error(S, "float format mismatch"); + checknum(S, int, LUAC_INT, "int"); + checknum(S, Instruction, LUAC_INST, "instruction"); + checknum(S, lua_Integer, LUAC_INT, "Lua integer"); + checknum(S, lua_Number, LUAC_NUM, "Lua number"); } /* ** Load precompiled chunk. */ -LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { +LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) { LoadState S; LClosure *cl; if (*name == '@' || *name == '=') - S.name = name + 1; + name = name + 1; else if (*name == LUA_SIGNATURE[0]) - S.name = "binary string"; - else - S.name = name; + name = "binary string"; + S.name = name; S.L = L; S.Z = Z; + S.fixed = cast_byte(fixed); + S.offset = 1; /* fist byte was already read */ checkHeader(&S); cl = luaF_newLclosure(L, loadByte(&S)); setclLvalue2s(L, L->top.p, cl); luaD_inctop(L); + S.h = luaH_new(L); /* create list of saved strings */ + S.nstr = 0; + sethvalue2s(L, L->top.p, S.h); /* anchor it */ + luaD_inctop(L); cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); - loadFunction(&S, cl->p, NULL); - lua_assert(cl->nupvalues == cl->p->sizeupvalues); + loadFunction(&S, cl->p); + if (cl->nupvalues != cl->p->sizeupvalues) + error(&S, "corrupted chunk"); luai_verifycode(L, cl->p); + L->top.p--; /* pop table */ return cl; } diff --git a/lua/src/lundump.h b/lua/src/lundump.h index a97676c..c4e06f9 100644 --- a/lua/src/lundump.h +++ b/lua/src/lundump.h @@ -7,6 +7,8 @@ #ifndef lundump_h #define lundump_h +#include + #include "llimits.h" #include "lobject.h" #include "lzio.h" @@ -15,18 +17,21 @@ /* data to catch conversion errors */ #define LUAC_DATA "\x19\x93\r\n\x1a\n" -#define LUAC_INT 0x5678 -#define LUAC_NUM cast_num(370.5) +#define LUAC_INT -0x5678 +#define LUAC_INST 0x12345678 +#define LUAC_NUM cast_num(-370.5) /* ** Encode major-minor version in one byte, one nibble for each */ -#define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100) +#define LUAC_VERSION (LUA_VERSION_MAJOR_N*16+LUA_VERSION_MINOR_N) #define LUAC_FORMAT 0 /* this is the official format */ + /* load one chunk; from lundump.c */ -LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, + int fixed); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, diff --git a/lua/src/lutf8lib.c b/lua/src/lutf8lib.c index 3a5b9bc..b7f3fe1 100644 --- a/lua/src/lutf8lib.c +++ b/lua/src/lutf8lib.c @@ -10,7 +10,6 @@ #include "lprefix.h" -#include #include #include #include @@ -19,6 +18,7 @@ #include "lauxlib.h" #include "lualib.h" +#include "llimits.h" #define MAXUNICODE 0x10FFFFu @@ -28,15 +28,6 @@ #define MSGInvalid "invalid UTF-8 code" -/* -** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. -*/ -#if (UINT_MAX >> 30) >= 1 -typedef unsigned int utfint; -#else -typedef unsigned long utfint; -#endif - #define iscont(c) (((c) & 0xC0) == 0x80) #define iscontp(p) iscont(*(p)) @@ -55,15 +46,15 @@ static lua_Integer u_posrelat (lua_Integer pos, size_t len) { ** Decode one UTF-8 sequence, returning NULL if byte sequence is ** invalid. The array 'limits' stores the minimum value for each ** sequence length, to check for overlong representations. Its first -** entry forces an error for non-ascii bytes with no continuation +** entry forces an error for non-ASCII bytes with no continuation ** bytes (count == 0). */ -static const char *utf8_decode (const char *s, utfint *val, int strict) { - static const utfint limits[] = - {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; +static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { + static const l_uint32 limits[] = + {~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; unsigned int c = (unsigned char)s[0]; - utfint res = 0; /* final result */ - if (c < 0x80) /* ascii? */ + l_uint32 res = 0; /* final result */ + if (c < 0x80) /* ASCII? */ res = c; else { int count = 0; /* to count number of continuation bytes */ @@ -73,7 +64,7 @@ static const char *utf8_decode (const char *s, utfint *val, int strict) { return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } - res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ + res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */ if (count > 5 || res > MAXUTF || res < limits[count]) return NULL; /* invalid byte sequence */ s += count; /* skip continuation bytes read */ @@ -111,7 +102,7 @@ static int utflen (lua_State *L) { lua_pushinteger(L, posi + 1); /* ... and current position */ return 2; } - posi = s1 - s; + posi = ct_diff2S(s1 - s); n++; } lua_pushinteger(L, n); @@ -141,11 +132,11 @@ static int codepoint (lua_State *L) { n = 0; /* count the number of returns */ se = s + pose; /* string end */ for (s += posi - 1; s < se;) { - utfint code; + l_uint32 code; s = utf8_decode(s, &code, !lax); if (s == NULL) return luaL_error(L, MSGInvalid); - lua_pushinteger(L, code); + lua_pushinteger(L, l_castU2S(code)); n++; } return n; @@ -181,14 +172,14 @@ static int utfchar (lua_State *L) { /* -** offset(s, n, [i]) -> index where n-th character counting from -** position 'i' starts; 0 means character at 'i'. +** offset(s, n, [i]) -> indices where n-th character counting from +** position 'i' starts and ends; 0 means character at 'i'. */ static int byteoffset (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer n = luaL_checkinteger(L, 2); - lua_Integer posi = (n >= 0) ? 1 : len + 1; + lua_Integer posi = (n >= 0) ? 1 : cast_st2S(len) + 1; posi = u_posrelat(luaL_optinteger(L, 3, posi), len); luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, "position out of bounds"); @@ -200,28 +191,37 @@ static int byteoffset (lua_State *L) { if (iscontp(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { - while (n < 0 && posi > 0) { /* move back */ - do { /* find beginning of previous character */ - posi--; - } while (posi > 0 && iscontp(s + posi)); - n++; - } - } - else { - n--; /* do not move for 1st character */ - while (n > 0 && posi < (lua_Integer)len) { - do { /* find beginning of next character */ - posi++; - } while (iscontp(s + posi)); /* (cannot pass final '\0') */ - n--; - } - } + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscontp(s + posi)); + n++; + } + } + else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } } - if (n == 0) /* did it find given character? */ - lua_pushinteger(L, posi + 1); - else /* no such character */ + if (n != 0) { /* did not find given character? */ luaL_pushfail(L); - return 1; + return 1; + } + lua_pushinteger(L, posi + 1); /* initial position */ + if ((s[posi] & 0x80) != 0) { /* multi-byte character? */ + if (iscont(s[posi])) + return luaL_error(L, "initial position is a continuation byte"); + while (iscontp(s + posi + 1)) + posi++; /* skip to last continuation byte */ + } + /* else one-byte character: final position is the initial one */ + lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */ + return 2; } @@ -235,12 +235,12 @@ static int iter_aux (lua_State *L, int strict) { if (n >= len) /* (also handles original 'n' being negative) */ return 0; /* no more codepoints */ else { - utfint code; + l_uint32 code; const char *next = utf8_decode(s + n, &code, strict); if (next == NULL || iscontp(next)) return luaL_error(L, MSGInvalid); - lua_pushinteger(L, n + 1); - lua_pushinteger(L, code); + lua_pushinteger(L, l_castU2S(n + 1)); + lua_pushinteger(L, l_castU2S(code)); return 2; } } diff --git a/lua/src/lvm.c b/lua/src/lvm.c index fcd24e1..c70e2b8 100644 --- a/lua/src/lvm.c +++ b/lua/src/lvm.c @@ -18,6 +18,7 @@ #include "lua.h" +#include "lapi.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" @@ -93,7 +94,9 @@ static int l_strton (const TValue *obj, TValue *result) { return 0; else { TString *st = tsvalue(obj); - return (luaO_str2num(getstr(st), result) == tsslen(st) + 1); + size_t stlen; + const char *s = getlstr(st, stlen); + return (luaO_str2num(s, result) == stlen + 1); } } @@ -124,8 +127,8 @@ int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) { lua_Number f = l_floor(n); if (n != f) { /* not an integral value? */ if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ - else if (mode == F2Iceil) /* needs ceil? */ - f += 1; /* convert floor to ceil (remember: n != f) */ + else if (mode == F2Iceil) /* needs ceiling? */ + f += 1; /* convert floor to ceiling (remember: n != f) */ } return lua_numbertointeger(f, p); } @@ -198,12 +201,15 @@ static int forlimit (lua_State *L, lua_Integer init, const TValue *lim, /* ** Prepare a numerical for loop (opcode OP_FORPREP). +** Before execution, stack is as follows: +** ra : initial value +** ra + 1 : limit +** ra + 2 : step ** Return true to skip the loop. Otherwise, ** after preparation, stack will be as follows: -** ra : internal index (safe copy of the control variable) -** ra + 1 : loop counter (integer loops) or limit (float loops) -** ra + 2 : step -** ra + 3 : control variable +** ra : loop counter (integer loops) or limit (float loops) +** ra + 1 : step +** ra + 2 : control variable */ static int forprep (lua_State *L, StkId ra) { TValue *pinit = s2v(ra); @@ -215,7 +221,6 @@ static int forprep (lua_State *L, StkId ra) { lua_Integer limit; if (step == 0) luaG_runerror(L, "'for' step is zero"); - setivalue(s2v(ra + 3), init); /* control variable */ if (forlimit(L, init, plimit, &limit, step)) return 1; /* skip the loop */ else { /* prepare loop counter */ @@ -230,9 +235,10 @@ static int forprep (lua_State *L, StkId ra) { /* 'step+1' avoids negating 'mininteger' */ count /= l_castS2U(-(step + 1)) + 1u; } - /* store the counter in place of the limit (which won't be - needed anymore) */ - setivalue(plimit, l_castU2S(count)); + /* use 'chgivalue' for places that for sure had integers */ + chgivalue(s2v(ra), l_castU2S(count)); /* change init to count */ + setivalue(s2v(ra + 1), step); /* change limit to step */ + chgivalue(s2v(ra + 2), init); /* change step to init */ } } else { /* try making all values floats */ @@ -249,11 +255,10 @@ static int forprep (lua_State *L, StkId ra) { : luai_numlt(init, limit)) return 1; /* skip the loop */ else { - /* make sure internal values are all floats */ - setfltvalue(plimit, limit); - setfltvalue(pstep, step); - setfltvalue(s2v(ra), init); /* internal index */ - setfltvalue(s2v(ra + 3), init); /* control variable */ + /* make sure all values are floats */ + setfltvalue(s2v(ra), limit); + setfltvalue(s2v(ra + 1), step); + setfltvalue(s2v(ra + 2), init); /* control variable */ } } return 0; @@ -266,14 +271,13 @@ static int forprep (lua_State *L, StkId ra) { ** written online with opcode OP_FORLOOP, for performance.) */ static int floatforloop (StkId ra) { - lua_Number step = fltvalue(s2v(ra + 2)); - lua_Number limit = fltvalue(s2v(ra + 1)); - lua_Number idx = fltvalue(s2v(ra)); /* internal index */ + lua_Number step = fltvalue(s2v(ra + 1)); + lua_Number limit = fltvalue(s2v(ra)); + lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */ idx = luai_numadd(L, idx, step); /* increment index */ if (luai_numlt(0, step) ? luai_numle(idx, limit) : luai_numle(limit, idx)) { - chgfltvalue(s2v(ra), idx); /* update internal index */ - setfltvalue(s2v(ra + 3), idx); /* and control variable */ + chgfltvalue(s2v(ra + 2), idx); /* update control variable */ return 1; /* jump back */ } else @@ -282,16 +286,14 @@ static int floatforloop (StkId ra) { /* -** Finish the table access 'val = t[key]'. -** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to -** t[k] entry (which must be empty). +** Finish the table access 'val = t[key]' and return the tag of the result. */ -void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, - const TValue *slot) { +lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, lu_byte tag) { int loop; /* counter to avoid infinite loops */ const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - if (slot == NULL) { /* 't' is not a table? */ + if (tag == LUA_VNOTABLE) { /* 't' is not a table? */ lua_assert(!ttistable(t)); tm = luaT_gettmbyobj(L, t, TM_INDEX); if (l_unlikely(notm(tm))) @@ -299,47 +301,49 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, /* else will try the metamethod */ } else { /* 't' is a table */ - lua_assert(isempty(slot)); tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ if (tm == NULL) { /* no metamethod? */ setnilvalue(s2v(val)); /* result is nil */ - return; + return LUA_VNIL; } /* else will try the metamethod */ } if (ttisfunction(tm)) { /* is metamethod a function? */ - luaT_callTMres(L, tm, t, key, val); /* call it */ - return; + tag = luaT_callTMres(L, tm, t, key, val); /* call it */ + return tag; /* return tag of the result */ } t = tm; /* else try to access 'tm[key]' */ - if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */ - setobj2s(L, val, slot); /* done */ - return; - } + luaV_fastget(t, key, s2v(val), luaH_get, tag); + if (!tagisempty(tag)) + return tag; /* done */ /* else repeat (tail call 'luaV_finishget') */ } luaG_runerror(L, "'__index' chain too long; possible loop"); + return 0; /* to avoid warnings */ } /* ** Finish a table assignment 't[key] = val'. -** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points -** to the entry 't[key]', or to a value with an absent key if there -** is no such entry. (The value at 'slot' must be empty, otherwise -** 'luaV_fastget' would have done the job.) +** About anchoring the table before the call to 'luaH_finishset': +** This call may trigger an emergency collection. When loop>0, +** the table being accessed is a field in some metatable. If this +** metatable is weak and the table is not anchored, this collection +** could collect that table while it is being updated. */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, - TValue *val, const TValue *slot) { + TValue *val, int hres) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; /* '__newindex' metamethod */ - if (slot != NULL) { /* is 't' a table? */ + if (hres != HNOTATABLE) { /* is 't' a table? */ Table *h = hvalue(t); /* save 't' table */ - lua_assert(isempty(slot)); /* slot must be empty */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ - luaH_finishset(L, h, key, slot, val); /* set new value */ + sethvalue2s(L, L->top.p, h); /* anchor 't' */ + L->top.p++; /* assume EXTRA_STACK */ + luaH_finishset(L, h, key, val, hres); /* set new value */ + L->top.p--; invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; @@ -357,8 +361,9 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, return; } t = tm; /* else repeat assignment over 'tm' */ - if (luaV_fastget(L, t, key, slot, luaH_get)) { - luaV_finishfastset(L, t, slot, val); + luaV_fastset(t, key, val, hres, luaH_pset); + if (hres == HOK) { + luaV_finishfastset(L, t, val); return; /* done */ } /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ @@ -367,6 +372,14 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, } +/* +** Function to be used for 0-terminated string order comparison +*/ +#if !defined(l_strcoll) +#define l_strcoll strcoll +#endif + + /* ** Compare two strings 'ts1' x 'ts2', returning an integer less-equal- ** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. @@ -376,12 +389,12 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, ** have different lengths. */ static int l_strcmp (const TString *ts1, const TString *ts2) { - const char *s1 = getstr(ts1); - size_t rl1 = tsslen(ts1); /* real length */ - const char *s2 = getstr(ts2); - size_t rl2 = tsslen(ts2); + size_t rl1; /* real length */ + const char *s1 = getlstr(ts1, rl1); + size_t rl2; + const char *s2 = getlstr(ts2, rl2); for (;;) { /* for each segment */ - int temp = strcoll(s1, s2); + int temp = l_strcoll(s1, s2); if (temp != 0) /* not equal? */ return temp; /* done */ else { /* strings are equal up to a '\0' */ @@ -568,52 +581,74 @@ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { */ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; - if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ - if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) - return 0; /* only numbers can be equal with different variants */ - else { /* two numbers with different variants */ - /* One of them is an integer. If the other does not have an - integer value, they cannot be equal; otherwise, compare their - integer values. */ - lua_Integer i1, i2; - return (luaV_tointegerns(t1, &i1, F2Ieq) && - luaV_tointegerns(t2, &i2, F2Ieq) && - i1 == i2); + if (ttype(t1) != ttype(t2)) /* not the same type? */ + return 0; + else if (ttypetag(t1) != ttypetag(t2)) { + switch (ttypetag(t1)) { + case LUA_VNUMINT: { /* integer == float? */ + /* integer and float can only be equal if float has an integer + value equal to the integer */ + lua_Integer i2; + return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) && + ivalue(t1) == i2); + } + case LUA_VNUMFLT: { /* float == integer? */ + lua_Integer i1; /* see comment in previous case */ + return (luaV_flttointeger(fltvalue(t1), &i1, F2Ieq) && + i1 == ivalue(t2)); + } + case LUA_VSHRSTR: case LUA_VLNGSTR: { + /* compare two strings with different variants: they can be + equal when one string is a short string and the other is + an external string */ + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + } + default: + /* only numbers (integer/float) and strings (long/short) can have + equal values with different variants */ + return 0; } } - /* values have same type and same variant */ - switch (ttypetag(t1)) { - case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; - case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); - case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); - case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); - case LUA_VLCF: return fvalue(t1) == fvalue(t2); - case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); - case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); - case LUA_VUSERDATA: { - if (uvalue(t1) == uvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); - if (tm == NULL) - tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ + else { /* equal variants */ + switch (ttypetag(t1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: + return (fltvalue(t1) == fltvalue(t2)); + case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_VSHRSTR: + return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VLCF: + return (fvalue(t1) == fvalue(t2)); + default: /* functions and threads */ + return (gcvalue(t1) == gcvalue(t2)); } - case LUA_VTABLE: { - if (hvalue(t1) == hvalue(t2)) return 1; - else if (L == NULL) return 0; - tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); - if (tm == NULL) - tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); - break; /* will try TM */ + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !tagisfalse(tag); } - default: - return gcvalue(t1) == gcvalue(t2); - } - if (tm == NULL) /* no TM? */ - return 0; /* objects are different */ - else { - luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ - return !l_isfalse(s2v(L->top.p)); } } @@ -622,6 +657,11 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { #define tostring(L,o) \ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) +/* +** Check whether object is a short empty string to optimize concatenation. +** (External strings can be empty too; they will be concatenated like +** non-empty ones.) +*/ #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) /* copy strings in stack from top - n up to top - 1 to buffer */ @@ -629,8 +669,9 @@ static void copy2buff (StkId top, int n, char *buff) { size_t tl = 0; /* size already copied */ do { TString *st = tsvalue(s2v(top - n)); - size_t l = tsslen(st); /* length of string being copied */ - memcpy(buff + tl, getstr(st), l * sizeof(char)); + size_t l; /* length of string being copied */ + const char *s = getlstr(st, l); + memcpy(buff + tl, s, l * sizeof(char)); tl += l; } while (--n > 0); } @@ -655,8 +696,8 @@ void luaV_concat (lua_State *L, int total) { setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { - /* at least two non-empty string values; get as many as possible */ - size_t tl = tsslen(tsvalue(s2v(top - 1))); + /* at least two string values; get as many as possible */ + size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */ TString *ts; /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { @@ -694,7 +735,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ - setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ + setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */ return; } case LUA_VSHRSTR: { @@ -702,7 +743,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { return; } case LUA_VLNGSTR: { - setivalue(s2v(ra), tsvalue(rb)->u.lnglen); + setivalue(s2v(ra), cast_st2S(tsvalue(rb)->u.lnglen)); return; } default: { /* try metamethod */ @@ -768,7 +809,7 @@ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { /* number of bits in an integer */ -#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) +#define NBITS l_numbits(lua_Integer) /* @@ -833,12 +874,6 @@ void luaV_finishOp (lua_State *L) { case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ int res = !l_isfalse(s2v(L->top.p - 1)); L->top.p--; -#if defined(LUA_COMPAT_LT_LE) - if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ - ci->callstatus ^= CIST_LEQ; /* clear mark */ - res = !res; /* negate result */ - } -#endif lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_k(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ @@ -882,6 +917,10 @@ void luaV_finishOp (lua_State *L) { /* ** {================================================================== ** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** +** All these macros are to be used exclusively inside the main +** iterpreter loop (function luaV_execute) and may access directly +** the local variables of that function (L, i, pc, ci, etc.). ** =================================================================== */ @@ -903,27 +942,28 @@ void luaV_finishOp (lua_State *L) { ** operation, 'fop' is the float operation. */ #define op_arithI(L,iop,fop) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ TValue *v1 = vRB(i); \ int imm = GETARG_sC(i); \ if (ttisinteger(v1)) { \ lua_Integer iv1 = ivalue(v1); \ - pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ + pc++; setivalue(ra, iop(L, iv1, imm)); \ } \ else if (ttisfloat(v1)) { \ lua_Number nb = fltvalue(v1); \ lua_Number fimm = cast_num(imm); \ - pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ + pc++; setfltvalue(ra, fop(L, nb, fimm)); \ }} /* ** Auxiliary function for arithmetic operations over floats and others -** with two register operands. +** with two operands. */ #define op_arithf_aux(L,v1,v2,fop) { \ lua_Number n1; lua_Number n2; \ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + StkId ra = RA(i); \ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ }} @@ -932,7 +972,6 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over floats and others with register operands. */ #define op_arithf(L,fop) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arithf_aux(L, v1, v2, fop); } @@ -942,7 +981,6 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations with K operands for floats. */ #define op_arithfK(L,fop) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } @@ -952,8 +990,8 @@ void luaV_finishOp (lua_State *L) { ** Arithmetic operations over integers and floats. */ #define op_arith_aux(L,v1,v2,iop,fop) { \ - StkId ra = RA(i); \ if (ttisinteger(v1) && ttisinteger(v2)) { \ + StkId ra = RA(i); \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ } \ @@ -982,12 +1020,12 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with constant operand. */ #define op_bitwiseK(L,op) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); \ lua_Integer i1; \ lua_Integer i2 = ivalue(v2); \ if (tointegerns(v1, &i1)) { \ + StkId ra = RA(i); \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} @@ -996,11 +1034,11 @@ void luaV_finishOp (lua_State *L) { ** Bitwise operations with register operands. */ #define op_bitwise(L,op) { \ - StkId ra = RA(i); \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ lua_Integer i1; lua_Integer i2; \ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + StkId ra = RA(i); \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} @@ -1011,18 +1049,18 @@ void luaV_finishOp (lua_State *L) { ** integers. */ #define op_order(L,opi,opn,other) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ int cond; \ TValue *rb = vRB(i); \ - if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ - lua_Integer ia = ivalue(s2v(ra)); \ + if (ttisinteger(ra) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(ra); \ lua_Integer ib = ivalue(rb); \ cond = opi(ia, ib); \ } \ - else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ - cond = opn(s2v(ra), rb); \ + else if (ttisnumber(ra) && ttisnumber(rb)) \ + cond = opn(ra, rb); \ else \ - Protect(cond = other(L, s2v(ra), rb)); \ + Protect(cond = other(L, ra, rb)); \ docondjump(); } @@ -1031,19 +1069,19 @@ void luaV_finishOp (lua_State *L) { ** always small enough to have an exact representation as a float.) */ #define op_orderI(L,opi,opf,inv,tm) { \ - StkId ra = RA(i); \ + TValue *ra = vRA(i); \ int cond; \ int im = GETARG_sB(i); \ - if (ttisinteger(s2v(ra))) \ - cond = opi(ivalue(s2v(ra)), im); \ - else if (ttisfloat(s2v(ra))) { \ - lua_Number fa = fltvalue(s2v(ra)); \ + if (ttisinteger(ra)) \ + cond = opi(ivalue(ra), im); \ + else if (ttisfloat(ra)) { \ + lua_Number fa = fltvalue(ra); \ lua_Number fim = cast_num(im); \ cond = opf(fa, fim); \ } \ else { \ int isf = GETARG_C(i); \ - Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ + Protect(cond = luaT_callorderiTM(L, ra, im, inv, isf, tm)); \ } \ docondjump(); } @@ -1062,6 +1100,7 @@ void luaV_finishOp (lua_State *L) { #define RA(i) (base+GETARG_A(i)) +#define vRA(i) s2v(RA(i)) #define RB(i) (base+GETARG_B(i)) #define vRB(i) s2v(RB(i)) #define KB(i) (k+GETARG_B(i)) @@ -1102,14 +1141,14 @@ void luaV_finishOp (lua_State *L) { /* ** Correct global 'pc'. */ -#define savepc(L) (ci->u.l.savedpc = pc) +#define savepc(ci) (ci->u.l.savedpc = pc) /* ** Whenever code can raise errors, the global 'pc' and the global ** 'top' must be correct to report occasional errors. */ -#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p) +#define savestate(L,ci) (savepc(ci), L->top.p = ci->top.p) /* @@ -1119,7 +1158,7 @@ void luaV_finishOp (lua_State *L) { #define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) /* special version that does not change the top */ -#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) +#define ProtectNT(exp) (savepc(ci), (exp), updatetrap(ci)) /* ** Protect code that can only raise errors. (That is, it cannot change @@ -1127,9 +1166,17 @@ void luaV_finishOp (lua_State *L) { */ #define halfProtect(exp) (savestate(L,ci), (exp)) +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ - { luaC_condGC(L, (savepc(L), L->top.p = (c)), \ + { luaC_condGC(L, (savepc(ci), L->top.p = (c)), \ updatetrap(ci)); \ luai_threadyield(L); } @@ -1171,13 +1218,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) { Instruction i; /* instruction being executed */ vmfetch(); #if 0 - /* low-level line tracing for debugging Lua */ - printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); + { /* low-level line tracing for debugging Lua */ + #include "lopnames.h" + int pcrel = pcRel(pc, cl->p); + printf("line: %d; %s (%d)\n", luaG_getfuncline(cl->p, pcrel), + opnames[GET_OPCODE(i)], pcrel); + } #endif lua_assert(base == ci->func.p + 1); lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); - /* invalidate top for instructions not expecting it */ - lua_assert(isIT(i) || (cast_void(L->top.p = base), 1)); + /* for tests, invalidate top for instructions not expecting it */ + lua_assert(luaP_isIT(i) || (cast_void(L->top.p = base), 1)); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { StkId ra = RA(i); @@ -1248,126 +1299,123 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_GETTABUP) { StkId ra = RA(i); - const TValue *slot; TValue *upval = cl->upvals[GETARG_B(i)]->v.p; TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a short string */ - if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { - setobj2s(L, ra, slot); - } - else - Protect(luaV_finishget(L, upval, rc, ra, slot)); + lu_byte tag; + luaV_fastget(upval, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, upval, rc, ra, tag)); vmbreak; } vmcase(OP_GETTABLE) { StkId ra = RA(i); - const TValue *slot; TValue *rb = vRB(i); TValue *rc = vRC(i); - lua_Unsigned n; - if (ttisinteger(rc) /* fast track for integers? */ - ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot)) - : luaV_fastget(L, rb, rc, slot, luaH_get)) { - setobj2s(L, ra, slot); + lu_byte tag; + if (ttisinteger(rc)) { /* fast track for integers? */ + luaV_fastgeti(rb, ivalue(rc), s2v(ra), tag); } else - Protect(luaV_finishget(L, rb, rc, ra, slot)); + luaV_fastget(rb, rc, s2v(ra), luaH_get, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); vmbreak; } vmcase(OP_GETI) { StkId ra = RA(i); - const TValue *slot; TValue *rb = vRB(i); int c = GETARG_C(i); - if (luaV_fastgeti(L, rb, c, slot)) { - setobj2s(L, ra, slot); - } - else { + lu_byte tag; + luaV_fastgeti(rb, c, s2v(ra), tag); + if (tagisempty(tag)) { TValue key; setivalue(&key, c); - Protect(luaV_finishget(L, rb, &key, ra, slot)); + Protect(luaV_finishget(L, rb, &key, ra, tag)); } vmbreak; } vmcase(OP_GETFIELD) { StkId ra = RA(i); - const TValue *slot; TValue *rb = vRB(i); TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a short string */ - if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { - setobj2s(L, ra, slot); - } - else - Protect(luaV_finishget(L, rb, rc, ra, slot)); + lu_byte tag; + luaV_fastget(rb, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); vmbreak; } vmcase(OP_SETTABUP) { - const TValue *slot; + int hres; TValue *upval = cl->upvals[GETARG_A(i)]->v.p; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a short string */ - if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { - luaV_finishfastset(L, upval, slot, rc); - } + luaV_fastset(upval, key, rc, hres, luaH_psetshortstr); + if (hres == HOK) + luaV_finishfastset(L, upval, rc); else - Protect(luaV_finishset(L, upval, rb, rc, slot)); + Protect(luaV_finishset(L, upval, rb, rc, hres)); vmbreak; } vmcase(OP_SETTABLE) { StkId ra = RA(i); - const TValue *slot; + int hres; TValue *rb = vRB(i); /* key (table is in 'ra') */ TValue *rc = RKC(i); /* value */ - lua_Unsigned n; - if (ttisinteger(rb) /* fast track for integers? */ - ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot)) - : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) { - luaV_finishfastset(L, s2v(ra), slot, rc); + if (ttisinteger(rb)) { /* fast track for integers? */ + luaV_fastseti(s2v(ra), ivalue(rb), rc, hres); } + else { + luaV_fastset(s2v(ra), rb, rc, hres, luaH_pset); + } + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); else - Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + Protect(luaV_finishset(L, s2v(ra), rb, rc, hres)); vmbreak; } vmcase(OP_SETI) { StkId ra = RA(i); - const TValue *slot; - int c = GETARG_B(i); + int hres; + int b = GETARG_B(i); TValue *rc = RKC(i); - if (luaV_fastgeti(L, s2v(ra), c, slot)) { - luaV_finishfastset(L, s2v(ra), slot, rc); - } + luaV_fastseti(s2v(ra), b, rc, hres); + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); else { TValue key; - setivalue(&key, c); - Protect(luaV_finishset(L, s2v(ra), &key, rc, slot)); + setivalue(&key, b); + Protect(luaV_finishset(L, s2v(ra), &key, rc, hres)); } vmbreak; } vmcase(OP_SETFIELD) { StkId ra = RA(i); - const TValue *slot; + int hres; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a short string */ - if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { - luaV_finishfastset(L, s2v(ra), slot, rc); - } + luaV_fastset(s2v(ra), key, rc, hres, luaH_psetshortstr); + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); else - Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); + Protect(luaV_finishset(L, s2v(ra), rb, rc, hres)); vmbreak; } vmcase(OP_NEWTABLE) { StkId ra = RA(i); - int b = GETARG_B(i); /* log2(hash size) + 1 */ - int c = GETARG_C(i); /* array size */ + unsigned b = cast_uint(GETARG_vB(i)); /* log2(hash size) + 1 */ + unsigned c = cast_uint(GETARG_vC(i)); /* array size */ Table *t; if (b > 0) - b = 1 << (b - 1); /* size is 2^(b - 1) */ - lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0)); - if (TESTARG_k(i)) /* non-zero extra argument? */ - c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ + b = 1u << (b - 1); /* hash size is 2^(b - 1) */ + if (TESTARG_k(i)) { /* non-zero extra argument? */ + lua_assert(GETARG_Ax(*pc) != 0); + /* add it to array size */ + c += cast_uint(GETARG_Ax(*pc)) * (MAXARG_vC + 1); + } pc++; /* skip extra argument */ L->top.p = ra + 1; /* correct top in case of emergency GC */ t = luaH_new(L); /* memory allocation */ @@ -1379,16 +1427,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_SELF) { StkId ra = RA(i); - const TValue *slot; + lu_byte tag; TValue *rb = vRB(i); - TValue *rc = RKC(i); - TString *key = tsvalue(rc); /* key must be a string */ + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ setobj2s(L, ra + 1, rb); - if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { - setobj2s(L, ra, slot); - } - else - Protect(luaV_finishget(L, rb, rc, ra, slot)); + luaV_fastget(rb, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); vmbreak; } vmcase(OP_ADDI) { @@ -1437,23 +1483,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) { op_bitwiseK(L, l_bxor); vmbreak; } - vmcase(OP_SHRI) { + vmcase(OP_SHLI) { StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { - pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); } vmbreak; } - vmcase(OP_SHLI) { + vmcase(OP_SHRI) { StkId ra = RA(i); TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { - pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); + pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); } vmbreak; } @@ -1499,14 +1545,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { op_bitwise(L, l_bxor); vmbreak; } - vmcase(OP_SHR) { - op_bitwise(L, luaV_shiftr); - vmbreak; - } vmcase(OP_SHL) { op_bitwise(L, luaV_shiftl); vmbreak; } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } vmcase(OP_MMBIN) { StkId ra = RA(i); Instruction pi = *(pc - 2); /* original arith. expression */ @@ -1587,6 +1633,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_CLOSE) { StkId ra = RA(i); + lua_assert(!GETARG_B(i)); /* 'close must be alive */ Protect(luaF_close(L, ra, LUA_OK, 1)); vmbreak; } @@ -1678,7 +1725,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { if (b != 0) /* fixed number of arguments? */ L->top.p = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ - savepc(L); /* in case of errors */ + savepc(ci); /* in case of errors */ if ((newci = luaD_precall(L, ra, nresults)) == NULL) updatetrap(ci); /* C call; nothing else to be done */ else { /* Lua call: run function in this same C frame */ @@ -1744,10 +1791,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) { trap = 1; } else { /* do the 'poscall' here */ - int nres; + int nres = get_nresults(ci->callstatus); L->ci = ci->previous; /* back to caller */ L->top.p = base - 1; - for (nres = ci->nresults; l_unlikely(nres > 0); nres--) + for (; l_unlikely(nres > 0); nres--) setnilvalue(s2v(L->top.p++)); /* all results are nil */ } goto ret; @@ -1761,7 +1808,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { trap = 1; } else { /* do the 'poscall' here */ - int nres = ci->nresults; + int nres = get_nresults(ci->callstatus); L->ci = ci->previous; /* back to caller */ if (nres == 0) L->top.p = base - 1; /* asked for no results */ @@ -1783,15 +1830,14 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_FORLOOP) { StkId ra = RA(i); - if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ - lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); + if (ttisinteger(s2v(ra + 1))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra))); if (count > 0) { /* still more iterations? */ - lua_Integer step = ivalue(s2v(ra + 2)); - lua_Integer idx = ivalue(s2v(ra)); /* internal index */ - chgivalue(s2v(ra + 1), count - 1); /* update counter */ + lua_Integer step = ivalue(s2v(ra + 1)); + lua_Integer idx = ivalue(s2v(ra + 2)); /* control variable */ + chgivalue(s2v(ra), l_castU2S(count - 1)); /* update counter */ idx = intop(+, idx, step); /* add step to index */ - chgivalue(s2v(ra), idx); /* update internal index */ - setivalue(s2v(ra + 3), idx); /* and control variable */ + chgivalue(s2v(ra + 2), idx); /* update control variable */ pc -= GETARG_Bx(i); /* jump back */ } } @@ -1808,26 +1854,38 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmbreak; } vmcase(OP_TFORPREP) { + /* before: 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the initial value for the control variable, and + 'ra + 3' has the closing variable. This opcode then swaps the + control and the closing variables and marks the closing variable + as to-be-closed. + */ StkId ra = RA(i); - /* create to-be-closed upvalue (if needed) */ - halfProtect(luaF_newtbcupval(L, ra + 3)); - pc += GETARG_Bx(i); - i = *(pc++); /* go to next instruction */ + TValue temp; /* to swap control and closing variables */ + setobj(L, &temp, s2v(ra + 3)); + setobjs2s(L, ra + 3, ra + 2); + setobj2s(L, ra + 2, &temp); + /* create to-be-closed upvalue (if closing var. is not nil) */ + halfProtect(luaF_newtbcupval(L, ra + 2)); + pc += GETARG_Bx(i); /* go to end of the loop */ + i = *(pc++); /* fetch next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); goto l_tforcall; } vmcase(OP_TFORCALL) { l_tforcall: { - StkId ra = RA(i); /* 'ra' has the iterator function, 'ra + 1' has the state, - 'ra + 2' has the control variable, and 'ra + 3' has the - to-be-closed variable. The call will use the stack after - these values (starting at 'ra + 4') + 'ra + 2' has the closing variable, and 'ra + 3' has the control + variable. The call will use the stack starting at 'ra + 3', + so that it preserves the first three values, and the first + return will be the new value for the control variable. */ - /* push function, state, and control variable */ - memcpy(ra + 4, ra, 3 * sizeof(*ra)); - L->top.p = ra + 4 + 3; - ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ + StkId ra = RA(i); + setobjs2s(L, ra + 5, ra + 3); /* copy the control variable */ + setobjs2s(L, ra + 4, ra + 1); /* copy state */ + setobjs2s(L, ra + 3, ra); /* copy function */ + L->top.p = ra + 3 + 3; + ProtectNT(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */ updatestack(ci); /* stack may have changed */ i = *(pc++); /* go to next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); @@ -1836,31 +1894,33 @@ void luaV_execute (lua_State *L, CallInfo *ci) { vmcase(OP_TFORLOOP) { l_tforloop: { StkId ra = RA(i); - if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ - setobjs2s(L, ra + 2, ra + 4); /* save control variable */ + if (!ttisnil(s2v(ra + 3))) /* continue loop? */ pc -= GETARG_Bx(i); /* jump back */ - } vmbreak; }} vmcase(OP_SETLIST) { StkId ra = RA(i); - int n = GETARG_B(i); - unsigned int last = GETARG_C(i); + unsigned n = cast_uint(GETARG_vB(i)); + unsigned last = cast_uint(GETARG_vC(i)); Table *h = hvalue(s2v(ra)); if (n == 0) - n = cast_int(L->top.p - ra) - 1; /* get up to the top */ + n = cast_uint(L->top.p - ra) - 1; /* get up to the top */ else L->top.p = ci->top.p; /* correct top in case of emergency GC */ last += n; if (TESTARG_k(i)) { - last += GETARG_Ax(*pc) * (MAXARG_C + 1); + last += cast_uint(GETARG_Ax(*pc)) * (MAXARG_vC + 1); pc++; } - if (last > luaH_realasize(h)) /* needs more space? */ + /* when 'n' is known, table should have proper size */ + if (last > h->asize) { /* needs more space? */ + /* fixed-size sets should have space preallocated */ + lua_assert(GETARG_vB(i) == 0); luaH_resizearray(L, h, last); /* preallocate it at once */ + } for (; n > 0; n--) { TValue *val = s2v(ra + n); - setobj2t(L, &h->array[last - 1], val); + obj2arr(h, last - 1, val); last--; luaC_barrierback(L, obj2gco(h), val); } @@ -1875,12 +1935,25 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_VARARG) { StkId ra = RA(i); - int n = GETARG_C(i) - 1; /* required results */ - Protect(luaT_getvarargs(L, ci, ra, n)); + int n = GETARG_C(i) - 1; /* required results (-1 means all) */ + int vatab = GETARG_k(i) ? GETARG_B(i) : -1; + Protect(luaT_getvarargs(L, ci, ra, n, vatab)); + vmbreak; + } + vmcase(OP_GETVARG) { + StkId ra = RA(i); + TValue *rc = vRC(i); + luaT_getvararg(ci, ra, rc); + vmbreak; + } + vmcase(OP_ERRNNIL) { + TValue *ra = vRA(i); + if (!ttisnil(ra)) + halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i))); vmbreak; } vmcase(OP_VARARGPREP) { - ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); + ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); if (l_unlikely(trap)) { /* previous "Protect" updated trap */ luaD_hookcall(L, ci); L->oldpc = 1; /* next opcode will be seen as a "new" line */ diff --git a/lua/src/lvm.h b/lua/src/lvm.h index dba1ad2..be7b9cb 100644 --- a/lua/src/lvm.h +++ b/lua/src/lvm.h @@ -43,7 +43,7 @@ typedef enum { F2Ieq, /* no rounding; accepts only integral values */ F2Ifloor, /* takes the floor of the number */ - F2Iceil /* takes the ceil of the number */ + F2Iceil /* takes the ceiling of the number */ } F2Imod; @@ -76,38 +76,33 @@ typedef enum { /* -** fast track for 'gettable': if 't' is a table and 't[k]' is present, -** return 1 with 'slot' pointing to 't[k]' (position of final result). -** Otherwise, return 0 (meaning it will have to check metamethod) -** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL -** (otherwise). 'f' is the raw get function to use. +** fast track for 'gettable' */ -#define luaV_fastget(L,t,k,slot,f) \ - (!ttistable(t) \ - ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ - : (slot = f(hvalue(t), k), /* else, do raw access */ \ - !isempty(slot))) /* result not empty? */ +#define luaV_fastget(t,k,res,f, tag) \ + (tag = (!ttistable(t) ? LUA_VNOTABLE : f(hvalue(t), k, res))) /* ** Special case of 'luaV_fastget' for integers, inlining the fast case ** of 'luaH_getint'. */ -#define luaV_fastgeti(L,t,k,slot) \ - (!ttistable(t) \ - ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ - : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \ - ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ - !isempty(slot))) /* result not empty? */ +#define luaV_fastgeti(t,k,res,tag) \ + if (!ttistable(t)) tag = LUA_VNOTABLE; \ + else { luaH_fastgeti(hvalue(t), k, res, tag); } + + +#define luaV_fastset(t,k,val,hres,f) \ + (hres = (!ttistable(t) ? HNOTATABLE : f(hvalue(t), k, val))) + +#define luaV_fastseti(t,k,val,hres) \ + if (!ttistable(t)) hres = HNOTATABLE; \ + else { luaH_fastseti(hvalue(t), k, val, hres); } /* -** Finish a fast set operation (when fast get succeeds). In that case, -** 'slot' points to the place to put the value. +** Finish a fast set operation (when fast set succeeds). */ -#define luaV_finishfastset(L,t,slot,v) \ - { setobj2t(L, cast(TValue *,slot), v); \ - luaC_barrierback(L, gcvalue(t), v); } +#define luaV_finishfastset(L,t,v) luaC_barrierback(L, gcvalue(t), v) /* @@ -125,10 +120,10 @@ LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode); LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode); LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode); -LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, - StkId val, const TValue *slot); +LUAI_FUNC lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, lu_byte tag); LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, - TValue *val, const TValue *slot); + TValue *val, int aux); LUAI_FUNC void luaV_finishOp (lua_State *L); LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); LUAI_FUNC void luaV_concat (lua_State *L, int total); diff --git a/lua/src/lzio.c b/lua/src/lzio.c index cd0a02d..301df4b 100644 --- a/lua/src/lzio.c +++ b/lua/src/lzio.c @@ -14,6 +14,7 @@ #include "lua.h" +#include "lapi.h" #include "llimits.h" #include "lmem.h" #include "lstate.h" @@ -45,17 +46,25 @@ void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { /* --------------------------------------------------------------- read --- */ + +static int checkbuffer (ZIO *z) { + if (z->n == 0) { /* no bytes in buffer? */ + if (luaZ_fill(z) == EOZ) /* try to read more */ + return 0; /* no more input */ + else { + z->n++; /* luaZ_fill consumed first byte; put it back */ + z->p--; + } + } + return 1; /* now buffer has something */ +} + + size_t luaZ_read (ZIO *z, void *b, size_t n) { while (n) { size_t m; - if (z->n == 0) { /* no bytes in buffer? */ - if (luaZ_fill(z) == EOZ) /* try to read more */ - return n; /* no more input; return number of missing bytes */ - else { - z->n++; /* luaZ_fill consumed first byte; put it back */ - z->p--; - } - } + if (!checkbuffer(z)) + return n; /* no more input; return number of missing bytes */ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); z->n -= m; @@ -66,3 +75,15 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) { return 0; } + +const void *luaZ_getaddr (ZIO* z, size_t n) { + const void *res; + if (!checkbuffer(z)) + return NULL; /* no more input */ + if (z->n < n) /* not enough bytes? */ + return NULL; /* block not whole; cannot give an address */ + res = z->p; /* get block address */ + z->n -= n; /* consume these bytes */ + z->p += n; + return res; +} diff --git a/lua/src/lzio.h b/lua/src/lzio.h index 38f397f..49047c9 100644 --- a/lua/src/lzio.h +++ b/lua/src/lzio.h @@ -32,7 +32,7 @@ typedef struct Mbuffer { #define luaZ_sizebuffer(buff) ((buff)->buffsize) #define luaZ_bufflen(buff) ((buff)->n) -#define luaZ_buffremove(buff,i) ((buff)->n -= (i)) +#define luaZ_buffremove(buff,i) ((buff)->n -= cast_sizet(i)) #define luaZ_resetbuffer(buff) ((buff)->n = 0) @@ -48,6 +48,7 @@ LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ +LUAI_FUNC const void *luaZ_getaddr (ZIO* z, size_t n); /* --------- Private Part ------------------ */ From f24ae8396aeda563cd4a52fde1bc7f54a57cf213 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 18:39:34 +0200 Subject: [PATCH 6/9] Adapt build for new Lua version Co-authored-by: Copilot --- lua/src/Makefile | 15 ++++++++++++++- .../java/org/itsallcode/luava/LowLevelLua.java | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lua/src/Makefile b/lua/src/Makefile index acf33e0..daf3de8 100644 --- a/lua/src/Makefile +++ b/lua/src/Makefile @@ -7,7 +7,7 @@ PLAT= guess CC= gcc -std=gnu99 -CFLAGS= -O2 -Wall -Wextra $(SYSCFLAGS) $(MYCFLAGS) +CFLAGS= -O2 -Wall -Wextra $(SYSCFLAGS) $(MYCFLAGS) -fPIC LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS) LIBS= -lm $(SYSLIBS) $(MYLIBS) @@ -32,6 +32,9 @@ CMCFLAGS= PLATS= guess aix bsd c89 freebsd generic ios linux macosx mingw posix solaris +LUA_SO= liblua.so +LUA_DYLIB= liblua.dylib + LUA_A= liblua.a CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o @@ -56,6 +59,16 @@ o: $(ALL_O) a: $(ALL_A) +so: $(LUA_SO) + +dylib: $(LUA_DYLIB) + +$(LUA_SO): $(CORE_O) $(LIB_O) + $(CC) -o $@ -shared $? + +$(LUA_DYLIB): $(CORE_O) $(LIB_O) + $(CC) -dynamiclib -o $@ $^ $(LIBS) + $(LUA_A): $(BASE_O) $(AR) $@ $(BASE_O) $(RANLIB) $@ diff --git a/src/main/java/org/itsallcode/luava/LowLevelLua.java b/src/main/java/org/itsallcode/luava/LowLevelLua.java index 5c31e28..63aaf2a 100644 --- a/src/main/java/org/itsallcode/luava/LowLevelLua.java +++ b/src/main/java/org/itsallcode/luava/LowLevelLua.java @@ -31,7 +31,7 @@ LowLevelLua forState(final MemorySegment newState) { } void openLibs() { - Lua.luaL_openlibs(state); + Lua.luaL_openselectedlibs(state, ~0, 0); } void pcall(final int nargs, final int nresults) { From 31764e15b67bcf3ca4dda7e36e021e504fa2a879 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 18:44:35 +0200 Subject: [PATCH 7/9] Fix gradle deprecation warning Co-authored-by: Copilot --- buildSrc/src/main/groovy/JextractTask.groovy | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/groovy/JextractTask.groovy b/buildSrc/src/main/groovy/JextractTask.groovy index 18e262e..ac9fe02 100644 --- a/buildSrc/src/main/groovy/JextractTask.groovy +++ b/buildSrc/src/main/groovy/JextractTask.groovy @@ -8,16 +8,19 @@ import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.file.ProjectLayout import org.gradle.process.ExecOperations import javax.inject.Inject @CacheableTask class JextractTask extends DefaultTask { private final ExecOperations execOperations + private final File rootDir @Inject - JextractTask(ExecOperations execOperations) { + JextractTask(ExecOperations execOperations, ProjectLayout projectLayout) { this.execOperations = execOperations + this.rootDir = projectLayout.projectDirectory.asFile } @InputFile @PathSensitive(PathSensitivity.RELATIVE) File jextractBinary @@ -37,8 +40,9 @@ class JextractTask extends DefaultTask { '--header-class-name', 'Lua', "$includeDir/all_lua.h" ] + def workingDirectory = rootDir execOperations.exec { - workingDir project.rootDir + workingDir workingDirectory executable jextractBinary args arguments } From ddcac9606ca8f03d992b965a79122d70eb3d0a1d Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 18:46:41 +0200 Subject: [PATCH 8/9] Upgrade GitHub Actions --- .github/workflows/build.yml | 12 ++++++------ .github/workflows/dependency-submission.yml | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f94c22..c796a52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - java: [23] + java: [25] os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} concurrency: @@ -19,21 +19,21 @@ jobs: cancel-in-progress: true name: "Build on ${{ matrix.os }} with Java ${{ matrix.java }}" env: - DEFAULT_JAVA: 23 + DEFAULT_JAVA: 25 DEFAULT_OS: 'ubuntu-latest' permissions: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: temurin java-version: ${{ matrix.java }} - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v6 - name: Build with Java ${{ matrix.java }} run: ./gradlew build --info --warning-mode=all -PjavaVersion=${{ matrix.java }} - name: Sonar analysis @@ -44,7 +44,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - name: Archive native lib - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: native-lib-${{ matrix.os }} path: build/lua-libs/ diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index c85266a..53ca4b0 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Java - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: 23 + java-version: 25 - name: Generate and submit dependency graph - uses: gradle/actions/dependency-submission@v4 + uses: gradle/actions/dependency-submission@v6 From 3b61408a7210dde343d47c4243d412d411388afd Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Fri, 1 May 2026 18:56:47 +0200 Subject: [PATCH 9/9] Upgrade toolchain plugin --- README.md | 11 +++++++++++ settings.gradle | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bcd781c..9c9bc0a 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,17 @@ This project allows executing Lua scripts from a Java application. It uses [Fore ## Development +### Updating Lua + +Lua source code is checked in `lua/src/`. The following files are patched compared to the original version: + +* Added `lua/src/all_lua.h` +* Modified `lua/src/Makefile` to build Lua as a shared library. + +### Enable Gradle Caching + +To speedup repeated builds, add `org.gradle.caching=true` to `~/.gradle/gradle.properties`. + ### Native Interface Build scripts generate native interface classes in `build/generated/sources/jextract` using [Jextract](https://github.com/openjdk/jextract). Scripts download and cache Jextract automatically during the build. diff --git a/settings.gradle b/settings.gradle index 78f0cc3..9a84e7f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,6 @@ plugins { // Apply the foojay-resolver plugin to allow automatic download of JDKs - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } rootProject.name = 'luava'