diff --git a/.clang_complete b/.clang_complete new file mode 100644 index 0000000000..39a10b07ee --- /dev/null +++ b/.clang_complete @@ -0,0 +1,9 @@ +-Iplatforms/Cross/vm +-Iinclude +-Isrc/vm +-Iplatforms/minheadless/common +-Iplatforms/minheadless/generic +-Iplatforms/minheadless/unix +-Iplatforms/minheadless/windows +-I/usr/include/SDL2 +-DSPURVM diff --git a/.gitattributes b/.gitattributes index dbd586254f..b4ee0c0aa7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,9 @@ /image/*.st eol=lf /image/*.sh eol=lf mvm eol=lf +mvm_configure eol=lf +mvm_configure_variant eol=lf +*.sh eol=lf sq*SCCSVersion.h filter=RevDateURL ident *.changes -diff *.sources -diff diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..bae86f2d04 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,559 @@ +# OpenSmalltalkVM cmake build script. +cmake_minimum_required(VERSION 2.8) +project(OpenSmalltalkVM) + +option(ONLY_CONFIG_H "Only generate config.h" OFF) +option(SPUR_OBJECT_MODEL "Spur Object Model" ON) +option(SISTA_OPTIMIZER "Sista Optimizer" OFF) +option(LOWCODE_EXTENSIONS "Lowcode Extensions" OFF) +option(PHARO_BRANDING "Pharo Branding" ON) +option(SUPPORT_TRADITIONAL_DISPLAY "Enables building a VM with support for a window." ON) +option(COG_JIT "Cog JIT" ON) + +# Check the build type +if (CMAKE_BUILD_TYPE STREQUAL "") + # CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up + # differentiation between debug and release builds. + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif () + +# Turn on warnings +if (MSVC) + # using Visual Studio C++ + set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} /W3") + set(VM_MSVC TRUE) +else() + set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -Wall") + if(UNIX) + set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + endif() + + # Export symbols from applications. + #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--export-dynamic") +endif() + +# Set optimization flags +set(CMAKE_CONFIGURATION_TYPES Debug Release RelWithDebInfo Assert) +if (MSVC) +else() + set(COMMON_FLAGS "") + if(WIN32) + set(COMMON_FLAGS "-falign-loops=16 -falign-jumps=16 -falign-functions=16 -falign-labels=16 -Dsetjmp=_setjmp" + ) + endif() + set(OPTIMIZATION_FLAGS "-O3 -fno-omit-frame-pointer -finline-functions ${COMMON_FLAGS}") + + set(CMAKE_C_FLAGS_DEBUG "-g ${COMMON_FLAGS} -DDEBUGVM=1") + set(CMAKE_CXX_FLAGS_DEBUG "-g ${COMMON_FLAGS} -DDEBUGVM=1") + + set(CMAKE_C_FLAGS_RELEASE "${OPTIMIZATION_FLAGS} -DNDEBUG -DDEBUGVM=0") + set(CMAKE_CXX_FLAGS_RELEASE "${OPTIMIZATION_FLAGS} -DNDEBUG -DDEBUGVM=0") + + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g ${OPTIMIZATION_FLAGS} -DNDEBUG -DDEBUGVM=0") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g ${OPTIMIZATION_FLAGS} -DNDEBUG -DDEBUGVM=0") + + set(CMAKE_C_FLAGS_ASSERT "${OPTIMIZATION_FLAGS} -DDEBUGVM=1") + set(CMAKE_CXX_FLAGS_ASSERT "${OPTIMIZATION_FLAGS} -DDEBUGVM=1") +endif() + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if(NOT VM_MSVC) + set(VM_DEPENDENCIES_LIBRARIES m ${VM_DEPENDENCIES_LIBRARIES}) +endif() + +# Add a build x86_32 version on x86_64 systems. +if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") + option(BUILD_I386_VERSION "Build x86 32 bits version" OFF) + set(SQUEAK_PLATFORM_X86_64 True) +elseif("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "i.86") + set(SQUEAK_PLATFORM_X86_32 True) +endif() + +if(SQUEAK_PLATFORM_X86_64) + if(BUILD_I386_VERSION) + set(SQUEAK_PLATFORM_X86_32 True) + set(SQUEAK_PLATFORM_X86_64 False) + else() + set(VM_64BITS TRUE) + set(SourceFolderName "${SourceFolderName}64") + if(WIN32) + set(FFI_VARIANT_X64_WIN64 True) + else() + set(FFI_VARIANT_X64_SYSV True) + endif() + set(VM_TARGET_CPU "x86_64") + endif() +else() + set(VM_64BITS False) +endif() + +if(SQUEAK_PLATFORM_X86_32) + if (MSVC) + set(CMAKE_CXX_FLAGS "/arch:SSE2") + set(CMAKE_C_FLAGS "/arch:SSE2") + else() + set(CMAKE_ASM-ATT_FLAGS "--32") + set(CMAKE_CXX_FLAGS "-m32 -msse2") + set(CMAKE_C_FLAGS "-m32 -msse2") + endif() + set(FFI_VARIANT_IA32 True) + set(VM_TARGET_CPU "i686") +endif() + +set(VM_TARGET "${CMAKE_SYSTEM}") + +if(WIN32) + set(OS_TYPE "Win32") + set(VM_TARGET_OS "Win32") +elseif(UNIX) + set(OS_TYPE "unix") + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(OS_TYPE "Mac OS") + set(VM_TARGET_OS "1000") # Used to recognise OS X + set(DARWIN True) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(VM_TARGET_OS "linux-gnu") + else() + set(VM_TARGET_OS "${CMAKE_SYSTEM_NAME}") + endif() +endif() + +set(SourceFolderName "") + +# Spur object model + +if(SPUR_OBJECT_MODEL) + #add_definitions(SpurVM=1) + set(SourceFolderName "spur") +endif() + +# Sista optimizer +if(SISTA_OPTIMIZER) + #add_definitions(SistaVM=1) + set(SourceFolderName "${SourceFolderName}sista") +endif() + +# Lowcode extended instructions +if(LOWCODE_EXTENSIONS) + add_definitions(-DLowcodeVM=1) + set(SourceFolderName "${SourceFolderName}lowcode") +endif() + +# Stack interpreter +if(NOT COG_JIT) + message("COG_JIT ${COG_JIT}") + set(SourceFolderName "${SourceFolderName}stack") +endif() + +# 64 bits VM +if(VM_64BITS) + set(SourceFolderName "${SourceFolderName}64") +endif() + +# VM branding +set(VM_EXECUTABLE_NAME squeak) +set(VM_LIBRARY_NAME SqueakVMCore) +set(VM_NAME Squeak) +set(PHARO_VM FALSE) +set(SQUEAK_VM FALSE) + +if(PHARO_BRANDING) + add_definitions(-DPharoVM=1 -DIMMUTABILITY=1) + set(VM_EXECUTABLE_NAME pharo) + set(VM_LIBRARY_NAME PharoVMCore) + set(VM_NAME Pharo) + set(PHARO_VM TRUE) +else() + set(SQUEAK_VM TRUE) +endif() +add_definitions(-DVM_NAME="${VM_NAME}") + + +# Set output dir. +# OpenSmalltalkVM_BUILD_AS_INTERNAL_PROJECT can be used before including this +# project using add_subdirectory() +if(NOT OpenSmalltalkVM_BUILD_AS_INTERNAL_PROJECT) + set(EXECUTABLE_OUTPUT_PATH "${OpenSmalltalkVM_BINARY_DIR}/dist") + set(LIBRARY_OUTPUT_PATH "${OpenSmalltalkVM_BINARY_DIR}/dist") +endif() + +# Cog JIT +if(COG_JIT) + #add_definitions(CogVM=1) +endif() + + +set(SourceFolderName "${SourceFolderName}src") +set(PluginsSourceFolderName "src/plugins") + +message("Source folder name: ${SourceFolderName}") + +# Common defines +add_definitions(-DUSE_GLOBAL_STRUCT=0 + -DNO_ISNAN=1 + -DUSE_INLINE_MEMORY_ACCESSORS +) + +# Configuration +include(CheckIncludeFiles) +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckCSourceCompiles) + +if(NOT WIN32) + include (TestBigEndian) + test_big_endian(WORDS_BIGENDIAN) +else() + set(WORDS_BIGENDIAN False) +endif() + +if(WORDS_BIGENDIAN) + add_definitions(-DLSB_FIRST) +endif() + +set(HAVE_INTERP_H 1) +check_include_files (alloca.h HAVE_ALLOCA_H) +if(HAVE_ALLOCA_H) + check_c_source_compiles(" + #include + + int main() + { + return (int*)alloca(5); + } + " HAVE_ALLOCA) +else() + check_c_source_compiles(" + int main() + { + return (int*)alloca(5); + } + " HAVE_ALLOCA) +endif() + +check_include_files(dirent.h HAVE_DIRENT_H) +check_include_files(features.h HAVE_FEATURES_H) +check_include_files(unistd.h HAVE_UNISTD_H) +check_include_files(ndir.h HAVE_NDIR_H) +check_include_files(sys/ndir.h HAVE_SYS_NDIR_H) +check_include_files(sys/dir.h HAVE_SYS_DIR_H) +check_include_files(sys/filio.h HAVE_SYS_FILIO_H) +check_include_files(sys/time.h HAVE_SYS_TIME_H) + +check_include_files(dlfcn.h HAVE_DLFCN_H) +check_library_exists(dl dlopen "" HAVE_LIBDL) +check_library_exists(dyld dlopen "" HAVE_DYLD) + +# check_include_files(iconv.h HAVE_ICONV_H) +# check_library_exists(iconv iconv "" HAVE_ICONV) +# check_library_exists(ffi ffi_call "" HAVE_LIBFFI) + +# System calls +check_function_exists(atexit AT_EXIT) + +check_function_exists(snprintf HAVE_SNPRINTF) +check_function_exists(__snprintf HAVE___SNPRINTF) +check_function_exists(nanosleep HAVE_NANOSLEEP) + +check_function_exists(mmap HAVE_MMAP) +check_function_exists(kqueue HAVE_KQUEUE) +check_function_exists(select HAVE_SELECT) +check_function_exists(epoll_create1 HAVE_EPOLL) +check_function_exists(epoll_pwait HAVE_EPOLL_PWAIT) + +# Time structures +set(CMAKE_EXTRA_INCLUDE_FILES time.h) +if(HAVE_SYS_TIME_H) + set(CMAKE_EXTRA_INCLUDE_FILES sys/time.h ${CMAKE_EXTRA_INCLUDE_FILES}) +endif() +check_c_source_compiles( +"#include + +int main() +{ + struct tm *tm = 0; + return (int)tm->tm_gmtoff; +} +" + HAVE_TM_GMTOFF +) +check_type_size("struct timezone" HAVE_TIMEZONE) +set(CMAKE_EXTRA_INCLUDE_FILES) + +# Type sizes +if(BUILD_I386_VERSION OR SQUEAK_PLATFORM_X86_32) + set(SIZEOF_INT 4) + set(SIZEOF_LONG 4) + set(SIZEOF_LONG_LONG 8) + set(SIZEOF_VOID_P 4) +else() + check_type_size("int" SIZEOF_INT) + check_type_size("long" SIZEOF_LONG) + check_type_size("long long" SIZEOF_LONG_LONG) + check_type_size("void*" SIZEOF_VOID_P) +endif() + +if("${SIZEOF_LONG}" STREQUAL "8") + set(SQUEAK_INT64_TYPEDEF "long") +elseif("${SIZEOF_LONG_LONG}" STREQUAL "8") + set(SQUEAK_INT64_TYPEDEF "long long") +else() + message(FATAL_ERROR "Failed to find a 64 bits integer type.") +endif() + +if(HAVE_LIBDL) + set(VM_DEPENDENCIES_LIBRARIES dl ${VM_DEPENDENCIES_LIBRARIES}) +endif() +if(HAVE_DYLD) + set(VM_DEPENDENCIES_LIBRARIES dyld ${VM_DEPENDENCIES_LIBRARIES}) +endif() +if(HAVE_ICONV) + set(VM_DEPENDENCIES_LIBRARIES iconv ${VM_DEPENDENCIES_LIBRARIES}) +endif() + +# Add the current directory. +include_directories( + . + "${PROJECT_SOURCE_DIR}/include" + "${PROJECT_SOURCE_DIR}/platforms/Cross/vm" + "${PROJECT_SOURCE_DIR}/platforms/Cross/plugins" + "${SourceFolderName}/vm" +) + +# VM Flavor sources +if(COG_JIT) + set(InterpreterSource ${SourceFolderName}/vm/cointerp.c) + set(VM_FAVLOR_SOURCES + ${SourceFolderName}/vm/cogit.c + ${InterpreterSource} + ) +else() + set(InterpreterSource ${SourceFolderName}/vm/interp.c) + set(VM_FAVLOR_SOURCES + ${InterpreterSource} + ) +endif() + +# Compile the interpreter with less aggresive optimization options. +if(NOT ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")) + set_source_files_properties(${InterpreterSource} PROPERTIES COMPILE_FLAGS -O1) +endif() + +# Find required Apple frameworks +if(APPLE) + find_library(CoreFoundation_LIBRARY CoreFoundation) + find_library(CoreServices_LIBRARY CoreServices) + set(VM_DEPENDENCIES_LIBRARIES + ${CoreServices_LIBRARY} + ${CoreFoundation_LIBRARY} + ${VM_DEPENDENCIES_LIBRARIES}) + # SDL2 and openssl need this + include_directories(/opt/local/include) +endif() + +# Find SDL2 +find_path(SDL2_INCLUDE_DIR + NAMES SDL.h + PATH_SUFFIXES SDL2 +) + +find_path(SDL2_LIBRARY_PATH + NAMES SDL2-2.0.lib SDL2-2.0.a libSDL2-2.0.a libSDL2-2.0.so SDL2.lib SDL2.a libSDL2.a +) + +find_library(SDL2_LIBRARY NAMES SDL2-2.0 SDL2 PATHS ${SDL2_LIBRARY_PATH}) + +set(HAVE_SDL2 False) +if(SDL2_INCLUDE_DIR AND SDL2_LIBRARY) + set(HAVE_SDL2 True) + include_directories("${SDL2_INCLUDE_DIR}") +endif() + +# Choose the window system + +set(VM_WINDOW_SYSTEM) +if(SUPPORT_TRADITIONAL_DISPLAY) + if(HAVE_SDL2) + set(VM_WINDOW_SYSTEM + platforms/minheadless/sdl2-window/sqWindow-SDL2.c + ) + set(VM_DEPENDENCIES_LIBRARIES ${SDL2_LIBRARY} ${VM_DEPENDENCIES_LIBRARIES}) + endif() +endif() + +# Different source categories +set(VM_COMMON_SOURCES + platforms/Cross/vm/sq.h + platforms/Cross/vm/sqAssert.h + platforms/Cross/vm/sqAtomicOps.h + platforms/Cross/vm/sqCircularQueue.h + platforms/Cross/vm/sqCogStackAlignment.h + platforms/Cross/vm/sqExternalSemaphores.c + platforms/Cross/vm/sqHeapMap.c + platforms/Cross/vm/sqMemoryAccess.h + platforms/Cross/vm/sqMemoryFence.h + platforms/Cross/vm/sqNamedPrims.c + platforms/Cross/vm/sqPath.h + platforms/Cross/vm/sqPath.c + platforms/Cross/vm/sqSCCSVersion.h + platforms/Cross/vm/sqTextEncoding.h + platforms/Cross/vm/sqTextEncoding.c + platforms/Cross/vm/sqTicker.c + platforms/Cross/vm/sqVirtualMachine.h + platforms/Cross/vm/sqVirtualMachine.c +) + +include_directories( + platforms/minheadless/common +) + +set(VM_PLATFORM_COMMON_SOURCES + platforms/minheadless/common/sqaio.h + platforms/minheadless/common/sqConfig.h + platforms/minheadless/common/sqEventCommon.h + platforms/minheadless/common/sqNamedPrims.h + platforms/minheadless/common/sqPlatformSpecific.h + platforms/minheadless/common/sqPlatformSpecificCommon.h + platforms/minheadless/common/sqInternalPrimitives.c + platforms/minheadless/common/sqExternalPrimitives.c + platforms/minheadless/common/sqEventCommon.c + platforms/minheadless/common/sqPrinting.c + platforms/minheadless/common/sqWindow-Null.c + platforms/minheadless/common/sqWindow-Dispatch.c + platforms/minheadless/common/sqVirtualMachineInterface.c +) + +if(UNIX) + include_directories( + platforms/minheadless/unix + ) + + set(VM_PLATFORM_SOURCES + platforms/minheadless/unix/aioUnix.c + platforms/minheadless/unix/sqPlatformSpecific-Unix.h + platforms/minheadless/unix/sqPlatformSpecific-Unix.c + platforms/minheadless/unix/sqUnixCharConv.c + platforms/minheadless/unix/sqUnixThreads.c + platforms/minheadless/unix/sqUnixHeartbeat.c + ${VM_PLATFORM_SOURCES} + ) + + if(SPUR_OBJECT_MODEL) + set(VM_PLATFORM_SOURCES + platforms/minheadless/unix/sqUnixSpurMemory.c + ${VM_PLATFORM_SOURCES} + ) + else() + set(VM_PLATFORM_SOURCES + platforms/minheadless/unix/sqUnixMemory.c + ${VM_PLATFORM_SOURCES} + ) + endif() + + set(VM_DEPENDENCIES_LIBRARIES pthread ${VM_DEPENDENCIES_LIBRARIES}) + if(APPLE) + add_definitions(-DBUILD_FOR_OSX=1) + else() + add_definitions(-D_GNU_SOURCE) + endif() +elseif(WIN32) + include_directories( + platforms/minheadless/windows + ) + + set(VM_PLATFORM_SOURCES + platforms/minheadless/windows/sqWin32Alloc.h + platforms/minheadless/windows/sqWin32HandleTable.h + platforms/minheadless/windows/sqPlatformSpecific-Win32.h + platforms/minheadless/windows/sqPlatformSpecific-Win32.c + platforms/minheadless/windows/sqWin32.h + platforms/minheadless/windows/sqWin32Alloc.c + platforms/minheadless/windows/sqWin32SpurAlloc.c + platforms/minheadless/windows/sqWin32Backtrace.c + platforms/minheadless/windows/sqWin32Common.c + platforms/minheadless/windows/sqWin32Directory.c + platforms/minheadless/windows/sqWin32Heartbeat.c + platforms/minheadless/windows/sqWin32Threads.c + platforms/minheadless/windows/sqWin32Time.c + ${VM_PLATFORM_SOURCES} + ) + add_definitions( + -D_CRT_SECURE_NO_WARNINGS + -DUNICODE + -D_UNICODE + -DWIN32_FILE_SUPPORT + -DNO_SERVICE + -DNO_STD_FILE_SUPPORT + -Dsetjmp=_setjmp + -D_WIN32_WINNT=0x501 + -DWINVER=0x501 + -DWIN32=1 + ) + set(VM_DEPENDENCIES_LIBRARIES Winmm ${VM_DEPENDENCIES_LIBRARIES}) + if(SQUEAK_PLATFORM_X86_32) + if(MSVC) +# add_definitions(-DALLOCA_LIES_SO_USE_GETSP=0) + else() + endif() + add_definitions( + -DX86 + ) + endif() +else() + set(VM_PLATFORM_SOURCES + platforms/minheadless/generic/sqPlatformSpecific-Generic.h + platforms/minheadless/generic/sqPlatformSpecific-Generic.c + ${VM_PLATFORM_SOURCES} + ) + +endif() + +set(VM_SOURCES + ${VM_COMMON_SOURCES} + ${VM_FAVLOR_SOURCES} + ${VM_PLATFORM_COMMON_SOURCES} + ${VM_PLATFORM_SOURCES} + ${VM_WINDOW_SYSTEM} +) + +source_group("VM Common Sources" FILES ${VM_COMMON_SOURCES}) +source_group("VM Flavor Sources" FILES ${VM_FAVLOR_SOURCES}) +source_group("VM Platform Common Sources" FILES ${VM_PLATFORM_COMMON_SOURCES}) +source_group("VM Platform Sources" FILES ${VM_PLATFORM_SOURCES}) +source_group("VM Window System" FILES ${VM_WINDOW_SYSTEM}) +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Plugins.cmake") + +# Generate the config dot h. +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/platforms/minheadless/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) +if(NOT ONLY_CONFIG_H) + include_directories(${CMAKE_BINARY_DIR}) + + # Build the VM core library + set(VM_CORE_LIBRARY_TYPE STATIC) + #if(WIN32) + # set(VM_CORE_LIBRARY_TYPE SHARED) + #endif() + + if("${VM_CORE_LIBRARY_TYPE}" STREQUAL "STATIC") + add_definitions(-DBUILD_SQUEAK_STATIC) + endif() + + add_library(${VM_LIBRARY_NAME} ${VM_CORE_LIBRARY_TYPE} ${VM_SOURCES} ${VM_INTERNAL_PLUGIN_SOURCES}) + + target_compile_definitions(${VM_LIBRARY_NAME} PRIVATE + -DSQUEAK_BUILTIN_PLUGIN + -DBUILD_VM_CORE + ) + + # Build the VM executable(s) + add_executable(${VM_EXECUTABLE_NAME} platforms/minheadless/common/sqMain.c) + target_link_libraries(${VM_EXECUTABLE_NAME} ${VM_LIBRARY_NAME} ${VM_DEPENDENCIES_LIBRARIES}) + + if(WIN32) + add_executable(${VM_EXECUTABLE_NAME}w WIN32 platforms/minheadless/windows/sqWin32Main.c) + target_link_libraries(${VM_EXECUTABLE_NAME}w ${VM_LIBRARY_NAME} ${VM_DEPENDENCIES_LIBRARIES}) + endif() +endif() diff --git a/build.linux32x86/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm b/build.linux32x86/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm new file mode 100755 index 0000000000..e27ab917c2 --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm @@ -0,0 +1,37 @@ +#!/bin/bash -e +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# assert VM with VM profiler and itimer heartbeat +INSTALLDIR=assert/phphcogspurlinux +OPT="-g3 -O1 -fwrapv -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDEBUGVM=0" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG diff --git a/build.linux32x86/pharo.cog.spur.minheadless/build.assert/mvm b/build.linux32x86/pharo.cog.spur.minheadless/build.assert/mvm new file mode 100755 index 0000000000..594fcd77ae --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/build.assert/mvm @@ -0,0 +1,50 @@ +#!/bin/bash -e +# PharoVM with VM profiler and threaded heartbeat +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" + +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=phcogspurlinuxht + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) + test -f Makefile && make clean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +LDFLAGS="-L../../../.thirdparty-cache/linux/i386/lib" \ +CFLAGS="-I../../../.thirdparty-cache/linux/i386/include -I../../.thirdparty-cache/linux/i386/include/SDL2" \ + cmake \ + -DCMAKE_BUILD_TYPE=Assert \ + -DBUILD_I386_VERSION=ON \ + -DSDL2_INCLUDE_DIR=../../../.thirdparty-cache/linux/i386/include/SDL2 \ + -DSDL2_LIBRARY_PATH=../../../.thirdparty-cache/linux/i386/lib \ + ../../.. +make + +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +cp dist/* $productDir +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" \ No newline at end of file diff --git a/build.linux32x86/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm b/build.linux32x86/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm new file mode 100755 index 0000000000..382457c999 --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm @@ -0,0 +1,37 @@ +#!/bin/bash -e +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# debug Spur VM with VM profiler and itimer heartbeat +INSTALLDIR=debug/phphcogspurlinux +OPT="-g3 -O0 -fwrapv -DDEBUGVM=1" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG diff --git a/build.linux32x86/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm b/build.linux32x86/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm new file mode 100755 index 0000000000..7776819e2e --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm @@ -0,0 +1,60 @@ +#!/bin/bash -e +# PharoVM with VM profiler and itimer heartbeat + +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac + +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" +INSTALLDIR=phcogspurlinux + +# Some gcc versions create a broken VM using -O2 +case `gcc -v 2>&1 | grep version | sed 's/gcc version *//'` in +3.4.*) OPT="-g -O1 -fwrapv -DNDEBUG -DDEBUGVM=0";; +*) OPT="-g -O2 -DNDEBUG -DDEBUGVM=0";; +esac +OPT="$OPT -DPharoVM -DIMMUTABILITY=1" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" diff --git a/build.linux32x86/pharo.cog.spur.minheadless/build/mvm b/build.linux32x86/pharo.cog.spur.minheadless/build/mvm new file mode 100755 index 0000000000..75128680be --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/build/mvm @@ -0,0 +1,51 @@ +#!/bin/bash -e +# PharoVM with VM profiler and threaded heartbeat +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" + +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=phcogspurlinuxht + + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) + test -f Makefile && make clean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +LDFLAGS="-L../../../.thirdparty-cache/linux/i386/lib" \ +CFLAGS="-I../../../.thirdparty-cache/linux/i386/include -I../../.thirdparty-cache/linux/i386/include/SDL2" \ + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_I386_VERSION=ON \ + -DSDL2_INCLUDE_DIR=../../../.thirdparty-cache/linux/i386/include/SDL2 \ + -DSDL2_LIBRARY_PATH=../../../.thirdparty-cache/linux/i386/lib \ + ../../.. +make + +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +cp dist/* $productDir +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" \ No newline at end of file diff --git a/build.linux32x86/pharo.cog.spur.minheadless/makeallclean b/build.linux32x86/pharo.cog.spur.minheadless/makeallclean new file mode 100755 index 0000000000..a7cac6bd4e --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/makeallclean @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") + done +fi diff --git a/build.linux32x86/pharo.cog.spur.minheadless/makealldirty b/build.linux32x86/pharo.cog.spur.minheadless/makealldirty new file mode 100755 index 0000000000..11f39e8808 --- /dev/null +++ b/build.linux32x86/pharo.cog.spur.minheadless/makealldirty @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") + done +fi diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm new file mode 100755 index 0000000000..e27ab917c2 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build.assert.itimerheartbeat/mvm @@ -0,0 +1,37 @@ +#!/bin/bash -e +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# assert VM with VM profiler and itimer heartbeat +INSTALLDIR=assert/phphcogspurlinux +OPT="-g3 -O1 -fwrapv -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDEBUGVM=0" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build.assert/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build.assert/mvm new file mode 100755 index 0000000000..8444b944c9 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build.assert/mvm @@ -0,0 +1,41 @@ +#!/bin/bash -e +# PharoVM with VM profiler and threaded heartbeat +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" +INSTALLDIR=phcogspurlinuxht + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) + test -f Makefile && make clean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +LDFLAGS="-L../../../.thirdparty-cache/linux/x86_64/lib" \ +CFLAGS="-I../../../.thirdparty-cache/linux/x86_64/include -I../../.thirdparty-cache/linux/x86_64/include/SDL2" \ + cmake \ + -DCMAKE_BUILD_TYPE=Assert \ + -DBUILD_I386_VERSION=OFF \ + -DSDL2_INCLUDE_DIR=../../../.thirdparty-cache/linux/x86_64/include/SDL2 \ + -DSDL2_LIBRARY_PATH=../../../.thirdparty-cache/linux/x86_64/lib \ + ../../.. +make + +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +cp dist/* $productDir +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" \ No newline at end of file diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm new file mode 100755 index 0000000000..382457c999 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build.debug.itimerheartbeat/mvm @@ -0,0 +1,37 @@ +#!/bin/bash -e +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac +# debug Spur VM with VM profiler and itimer heartbeat +INSTALLDIR=debug/phphcogspurlinux +OPT="-g3 -O0 -fwrapv -DDEBUGVM=1" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build.debug/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build.debug/mvm new file mode 100755 index 0000000000..cc883091a0 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build.debug/mvm @@ -0,0 +1,41 @@ +#!/bin/bash -e +# PharoVM with VM profiler and threaded heartbeat +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" +INSTALLDIR=phcogspurlinuxht + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) + test -f Makefile && make clean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +LDFLAGS="-L../../../.thirdparty-cache/linux/x86_64/lib" \ +CFLAGS="-I../../../.thirdparty-cache/linux/x86_64/include -I../../.thirdparty-cache/linux/x86_64/include/SDL2" \ + cmake \ + -DCMAKE_BUILD_TYPE=Debug \ + -DBUILD_I386_VERSION=OFF \ + -DSDL2_INCLUDE_DIR=../../../.thirdparty-cache/linux/x86_64/include/SDL2 \ + -DSDL2_LIBRARY_PATH=../../../.thirdparty-cache/linux/x86_64/lib \ + ../../.. +make + +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +cp dist/* $productDir +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" \ No newline at end of file diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm new file mode 100755 index 0000000000..7776819e2e --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build.itimerheartbeat/mvm @@ -0,0 +1,60 @@ +#!/bin/bash -e +# PharoVM with VM profiler and itimer heartbeat + +case "`uname -m`" in # ensure we see x86 as machine type +i*86) ;; # we're good +*) if type i386 2>&1 >/dev/null; then + echo "Re-exec as x86" + exec i386 "$0" "$@" +fi ;; +esac + +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" +INSTALLDIR=phcogspurlinux + +# Some gcc versions create a broken VM using -O2 +case `gcc -v 2>&1 | grep version | sed 's/gcc version *//'` in +3.4.*) OPT="-g -O1 -fwrapv -DNDEBUG -DDEBUGVM=0";; +*) OPT="-g -O2 -DNDEBUG -DDEBUGVM=0";; +esac +OPT="$OPT -DPharoVM -DIMMUTABILITY=1" + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac +test -f plugins.int || (test -f ../plugins.int && cp -p ../plugins.int . || cp -p ../../plugins.int .) +test -f plugins.ext || (test -f ../plugins.ext && cp -p ../plugins.ext . || cp -p ../../plugins.ext .) + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +test -f config.h || ../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=spursrc \ + CC="gcc -m32" \ + CXX="g++ -m32" \ + CFLAGS="$OPT -msse2 -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" \ + LIBS="-lpthread -luuid -Wl,-rpath,'\$\$ORIGIN' " \ + LDFLAGS=-Wl,-z,now +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" diff --git a/build.linux64x64/pharo.cog.spur.minheadless/build/mvm b/build.linux64x64/pharo.cog.spur.minheadless/build/mvm new file mode 100755 index 0000000000..afa1dee6d7 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/build/mvm @@ -0,0 +1,41 @@ +#!/bin/bash -e +# PharoVM with VM profiler and threaded heartbeat +THIRDPARTYLIBS="libsdl2 openssl libssh2 libgit2" +INSTALLDIR=phcogspurlinuxht + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) + test -f Makefile && make clean +# for lib in ${THIRDPARTYLIBS}; do +# ../../third-party/mvm ${lib} clean +# done +esac + +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} +done + +LDFLAGS="-L../../../.thirdparty-cache/linux/x86_64/lib" \ +CFLAGS="-I../../../.thirdparty-cache/linux/x86_64/include -I../../.thirdparty-cache/linux/x86_64/include/SDL2" \ + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_I386_VERSION=OFF \ + -DSDL2_INCLUDE_DIR=../../../.thirdparty-cache/linux/x86_64/include/SDL2 \ + -DSDL2_LIBRARY_PATH=../../../.thirdparty-cache/linux/x86_64/lib \ + ../../.. +make + +productDir=`find ../../../products/$INSTALLDIR -name "5.0*"` +productDir=`(cd $productDir;pwd)` +for lib in ${THIRDPARTYLIBS}; do + ../../third-party/mvm ${lib} install $productDir +done +cp dist/* $productDir +../../editpharoinstall.sh ../../../products/$INSTALLDIR "$@" \ No newline at end of file diff --git a/build.linux64x64/pharo.cog.spur.minheadless/makeallclean b/build.linux64x64/pharo.cog.spur.minheadless/makeallclean new file mode 100755 index 0000000000..a7cac6bd4e --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/makeallclean @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") + done +fi diff --git a/build.linux64x64/pharo.cog.spur.minheadless/makealldirty b/build.linux64x64/pharo.cog.spur.minheadless/makealldirty new file mode 100755 index 0000000000..11f39e8808 --- /dev/null +++ b/build.linux64x64/pharo.cog.spur.minheadless/makealldirty @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") + done +fi diff --git a/build.macos32x86/common.minheadless/Makefile.app b/build.macos32x86/common.minheadless/Makefile.app new file mode 100644 index 0000000000..562005de81 --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.app @@ -0,0 +1,190 @@ +############################################################################# +# Generic Makefile for VM app bundle +# Do make getversion to get make -n to work +# +# This is designed to be invoked by Makefile in a specific build directory via +# include ../common.minheadless/Makefile.app +# +# Parameters: +# VMSRCDIR defines the location of the VM source to build. Required. +# +# COGDEFS supply any command-line defines to use, and may be null. +# +# The name of the VM to build. Optional. Defaults to Squeak +# +# SOURCEFILE the Smalltalk source file to link into this directory. Optional. +# +# APPSOURCE the Smalltalk source file to link into the app Resource. Optional. +# +# PLUGINSRCDIR defines the location of the plugin source, the subsets of which +# selected by plugins.int and plugins.ext will be built. Optional. Defaults to +# ../../src +# +# CONFIGURATION defines what version of the VM to build, product, assert or +# debug. Optional. Defaults to product. The default is overridden in mvm script + +ifeq ($(APPNAME),) +APPNAME:=Cocoa +endif +ifeq ($(APPNAMEDEF),) +APPNAMEDEF:=$(APPNAME)Fast +endif +ifeq ($(APPIDENTIFIER),) +APPIDENTIFIER:=org.opensmalltalk.$(APPNAME) +endif +ifeq ($(USEPLUGINASDYLIB),) +USEPLUGINASDYLIB:=FALSE +endif + +ifeq ($(CONFIGURATION),debug) + APP:=$(APPNAME)Debug.app + VM_IDENTIFIER:=$(APPIDENTIFIER)Debug +else ifeq ($(CONFIGURATION),assert) + APP:=$(APPNAME)Assert.app + VM_IDENTIFIER:=$(APPIDENTIFIER)Assert +else # default CONFIGURATION=product => $(APPNAMEDEF).app + APP:=$(APPNAMEDEF).app + VM_IDENTIFIER:=$(APPIDENTIFIER) +endif + +default: $(APP) + +include ../common.minheadless/Makefile.vm +include ../common.minheadless/Makefile.lib.extra +include ../common.minheadless/Makefile.sources + +cleanall: cleanapp cleanastapp cleandbgapp cleanallvm + +cleanapp: + rm -rf $(APPNAMEDEF).app + +cleanastapp: + rm -rf $(APPNAME)Assert.app + +cleandbgapp: + rm -rf $(APPNAME)Debug.app + +VMEXE:=$(APP)/Contents/MacOS/$(VM) +VMPLIST:=$(APP)/Contents/Info.plist + +ifeq ($(USEPLUGINASDYLIB),FALSE) +VMBUNDLES:=$(addprefix $(APP)/Contents/Resources/, $(addsuffix .bundle, $(EXTERNAL_PLUGINS))) +else ifeq ($(USEPLUGINASDYLIB),TRUE) +VMPLUGINDYLIBS:=$(addprefix $(APP)/Contents/MacOS/Plugins/lib, $(addsuffix .dylib, $(EXTERNAL_PLUGINS))) +else +$(error USEPLUGINASDYLIB has to be TRUE or FALSE) +endif + +ifneq ($(THIRDPARTYLIBS),) +THIRDPARTYPREREQS:=$(THIRDPARTYINSTALLDIR) $(THIRDPARTYOUTDIR) $(THIRDPARTYCACHEDIR) +endif + +OSXICONS:=$(OSXDIR)/$(VM).icns $(wildcard $(OSXDIR)/$(SYSTEM)*.icns) +VMICONS:=$(addprefix $(APP)/Contents/Resources/,$(notdir $(OSXICONS))) +VMMENUNIB:=$(APP)/Contents/Resources/English.lproj/MainMenu.nib +VMLOCALIZATION:=$(APP)/Contents/Resources/English.lproj/Localizable.strings +SOURCES:= +ifneq ($(SOURCEFILE),) +SOURCES:=./$(SOURCEFILE) +endif +ifneq ($(APPSOURCE),) +SOURCES:=$(SOURCES) $(APP)/Contents/Resources/$(APPSOURCE) +$(APP)/Contents/Resources/$(APPSOURCE): $(SOURCESDIR)/$(APPSOURCE) + test -f $@ || ln $(SOURCESDIR)/$(notdir $@) $@ +endif + +$(APP): cleanbundles $(THIRDPARTYPREREQS) $(VMEXE) $(VMBUNDLES) $(VMPLUGINDYLIBS) \ + $(VMPLIST) $(VMLOCALIZATION) $(VMMENUNIB) $(VMICONS) \ + $(SOURCES) $(THIRDPARTYLIBS) $(APPPOST) signapp touchapp + +# Bundles with missing prerequisites won't be built. But we need to force the +# attempt to make them every time in case the prerequisites /have/ been built. +# to do this we must both delete the bundles and touch any ignore files, upon +# which the bundle build depends. +cleanbundles: + -rm -rf $(APP)/Contents/Resources/*.bundle + -touch $(OBJDIR)/*.ignore + +$(VMEXE): $(OBJDIR)/$(VM) + @mkdir -p $(APP)/Contents/MacOS + cp -p $(OBJDIR)/$(VM) $(APP)/Contents/MacOS + +$(APP)/Contents/Resources/%.bundle: $(BLDDIR)/vm/%.bundle + @mkdir -p $(APP)/Contents/Resources + @if [ -f $(basename $<).ignore ]; then \ + echo $(notdir $<) is being ignored; \ + rm -rf $^; \ + else \ + echo cp -pR $< $(APP)/Contents/Resources; \ + cp -pR $< $(APP)/Contents/Resources; \ + fi + +$(APP)/Contents/MacOS/Plugins/%.dylib: $(BLDDIR)/vm/%.dylib + @mkdir -p $(APP)/Contents/MacOS/Plugins + cp -p $< $(APP)/Contents/MacOS/Plugins + + +$(VMPLIST): $(OSXDIR)/$(SYSTEM)-Info.plist getversion + -mkdir -p $(APP)/Contents + cp -p $< $@ + sed -i '' '\ + s!$$(VERSION)!$(shell ./getversion VERSION_TAG)!g;\ + s!$$(VERSION_NUMBER)!$(shell ./getversion VERSION_NUMBER)!g;\ + s!$$(VERSION_TAG)!$(shell ./getversion VERSION_TAG)!g;\ + s!$$(VIRTUAL_MACHINE_NICKNAME)!$(shell ./getversion VIRTUAL_MACHINE_NICKNAME)!g;\ + s!$$(VM_NICKNAME)!$(shell ./getversion VM_NICKNAME)!g;\ + s!$$(VM_MAJOR)!$(shell ./getversion VM_MAJOR)!g;\ + s!$$(VM_MINOR)!$(shell ./getversion VM_MINOR)!g;\ + s!$$(VM_IDENTIFIER)!$(VM_IDENTIFIER)!g;\ + ' $@ + +$(VMLOCALIZATION): $(OSXCOMMONDIR)/English.lproj/$(SYSTEM)-Localizable.strings + @mkdir -p $(dir $@) + cp -p $< $@ + +$(VMMENUNIB): $(PLATDIR)/iOS/vm/English.lproj/MainMenu.xib + @mkdir -p $(dir $@) + $(XCUB)/ibtool --errors --warnings --notices --module $(VM) \ + --minimum-deployment-target $(TARGET_VERSION_MIN) \ + --auto-activate-custom-fonts --output-format human-readable-text \ + --compile $(VMMENUNIB) \ + $(PLATDIR)/iOS/vm/English.lproj/MainMenu.xib + +$(APP)/Contents/Resources/%.icns: $(OSXDIR)/%.icns + @mkdir -p $(APP)/Contents/Resources + cp -p $< $(APP)/Contents/Resources + +# To sign the app, set SIGNING_IDENTITY in the environment, e.g. +# export SIGNING_IDENTITY="Developer ID Application: Eliot Miranda" +# +ifeq ($(SIGNING_IDENTITY),) +signapp: + echo "No signing identity found (SIGNING_IDENTITY unset). Not signing app." +else +signapp: + codesign -f --deep -s "$(SIGNING_IDENTITY)" $(APP) +endif + +touchapp: + touch $(APP) + +# source installation +%.sources: ../../sources/%.sources + ln $< $@ + +$(APP)/Contents/Resources/%.sources: ../../sources/%.sources + ln $< $@ + +print-app-settings: + @echo ---------------- Makefile.app settings ------------------ + @echo APP=$(APP) + @echo VMEXE=$(VMEXE) + @echo VMBUNDLES=$(VMBUNDLES) + @echo VMPLUGINDYLIBS=$(VMPLUGINDYLIBS) + @echo VMPLIST=$(VMPLIST) + @echo VMICONS=$(VMICONS) + @echo SIGNING_IDENTITY=$(SIGNING_IDENTITY) + @echo SOURCEFILE=$(SOURCEFILE) + @echo APPSOURCE=$(APPSOURCE) + @echo SOURCES=$(SOURCES) + @echo ----------------------------------------------------- diff --git a/build.macos32x86/common.minheadless/Makefile.app.newspeak b/build.macos32x86/common.minheadless/Makefile.app.newspeak new file mode 100644 index 0000000000..8788465d6c --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.app.newspeak @@ -0,0 +1,29 @@ +############################################################################## +# Generic Makefile for Mac OS X Newspeak or Glue Cocoa VM +# + +# The caller should set VMSRCDIR to point to the relevant VM source +# e.g. VMSRCDIR:= ../../nsspursrc/vm + +VM:=NewspeakVirtualMachine +SYSTEM:=Newspeak +APPSOURCE?=SqueakV50.sources +SOURCEFILE?=SqueakV50.sources + +APPPOST:=renameExe renameIcon + +# Now include the Makefile proper, which is common to all Mac OS builds. +# +include ../common/Makefile.app + +# Rename NewspeakVirtualMachine to the problematic Newspeak Virtual Machine + +renameExe: $(APP)/Contents/MacOS/NewspeakVirtualMachine + mv $(APP)/Contents/MacOS/NewspeakVirtualMachine \ + "$(APP)/Contents/MacOS/Newspeak Virtual Machine" + +# Likewise, rename NewspeakVirtualMachine.icns + +renameIcon: $(APP)/Contents/Resources/NewspeakVirtualMachine.icns + mv $(APP)/Contents/Resources/NewspeakVirtualMachine.icns \ + "$(APP)/Contents/Resources/Newspeak Virtual Machine.icns" diff --git a/build.macos32x86/common.minheadless/Makefile.app.squeak b/build.macos32x86/common.minheadless/Makefile.app.squeak new file mode 100644 index 0000000000..c9a315a129 --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.app.squeak @@ -0,0 +1,22 @@ +############################################################################## +# Generic Makefile for Mac OS X Squeak Cog Cocoa VM +# + +# The caller should set VMSRCDIR to point to the relevant VM source +# e.g. VMSRCDIR:= ../../spursrc/vm + +# Produce Squeak.app, SqueakAssert.app & SqueakDebug.app +APPNAME:=Squeak +APPNAMEDEF:=$(APPNAME) +APPIDENTIFIER:=org.squeak.$(APPNAME) + +APPPOST:=overwriteSqueakIcon + +# Now include the Makefile proper, which is common to all Mac OS builds. +# +include ../common/Makefile.app + +# Replace the standard yellow VM icon with the green one used for Cog VMs + +overwriteSqueakIcon: $(APP)/Contents/Resources/Squeak.icns + cp -p $(OSXDIR)/GreenCogSqueak.icns $(APP)/Contents/Resources/Squeak.icns diff --git a/build.macos32x86/common.minheadless/Makefile.clangversion b/build.macos32x86/common.minheadless/Makefile.clangversion new file mode 100644 index 0000000000..8daa30d3a3 --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.clangversion @@ -0,0 +1,26 @@ +############################################################################# +# Determine clang version and set CLANG_7_3_OR_ABOVE & CLANG_7_2_OR_BELOW as so +# + +XCODETOOLCHAINS := /Applications/Xcode.app/Contents/Developer/Toolchains +XCODETOOLCHAIN := $(XCODETOOLCHAINS)/XcodeDefault.xctoolchain + +# /usr/bin/clang, a.k.a. $(XCODETOOLCHAIN)/usr/bin/clang +CC := clang +# convoluted, but clang -version writes to /dev/tty, /not/ stdout :-( +CLVERSION:= $(shell ls $(XCODETOOLCHAIN)/usr/lib/clang) +CLVERMAJ := $(shell echo $(CLVERSION) | cut -f1 -d.) +CLVERMIN := $(shell echo $(CLVERSION) | cut -f2 -d.) + +# One glaring deficiency of GNU Make is the absence of arithmetic support +ifneq ($(filter 0 1 2 3 4 5 6,$(CLVERMAJ)),) + CLANG_7_2_OR_BELOW:=true +else ifeq ($(CLVERMAJ), 7) + ifneq ($(filter 0 1 2,$(CLVERMIN)),) + CLANG_7_2_OR_BELOW:=true + else + CLANG_7_3_OR_ABOVE:=true + endif +else + CLANG_7_3_OR_ABOVE:=true +endif diff --git a/build.macos32x86/common.minheadless/Makefile.flags b/build.macos32x86/common.minheadless/Makefile.flags new file mode 100644 index 0000000000..d07749861b --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.flags @@ -0,0 +1,56 @@ +############################################################################# +# Compilation flags & paths for Mac OS X +# +# These are derived from Xcode v 6.2 for Apple LLVM version 6.0 (clang-600.0.57) + +XCODE:=/Applications/Xcode.app/Contents/Developer +XCUB:=$(XCODE)/usr/bin +SDKsDIR:=$(XCODE)/Platforms/MacOSX.platform/Developer/SDKs +#Build the oldest SDK installed +SDKs:=MacOSX10.9.sdk MacOSX10.10.sdk MacOSX10.11.sdk MacOSX10.12.sdk +SDK:=$(firstword $(realpath $(addprefix $(SDKsDIR)/, $(SDKs)))) +TARGET_ARCH:=i386 +TARGET_VERSION_MIN:=10.7 + +print-sdks: + @echo ---------------- Makefile.flags settings ------------------ + @echo SDKs=$(realpath $(addprefix $(SDKsDIR)/, $(SDKs))) + @echo SDK=$(SDK) + @echo ----------------------------------------------------- + +# N.B. ARC isn't supported by the os-x 32-bit legacy Objective-C runtime kernel. +# ARC is supported only on 64-bits, and then only for the 10.7 SDK and later. +include ../common.minheadless/Makefile.clangversion +ifdef CLANG_7_3_OR_ABOVE + OBJC_CODE_MODEL := -fobjc-weak +else + OBJC_CODE_MODEL := -fno-objc-arc +endif +CFLAGS:=$(CFLAGS) -DBUILD_FOR_OSX=1 \ + -arch $(TARGET_ARCH) \ + -mmacosx-version-min=$(TARGET_VERSION_MIN) -msse4.2 \ + -fvisibility=default -fwrapv \ + -fmacro-backtrace-limit=0 -fdiagnostics-show-note-include-stack \ + -fmessage-length=0 -fpascal-strings -fasm-blocks -fstrict-aliasing \ + $(OBJC_CODE_MODEL) \ + $(XCFLAGS) \ + -isysroot $(SDK) + +BFLAGS:=-arch $(TARGET_ARCH) -bundle -isysroot $(SDK) $(EXTRABFLAGS) +DYFLAGS:=-arch $(TARGET_ARCH) -shared -isysroot $(SDK) $(EXTRADYFLAGS) + +WARNINGS:= -Wno-missing-field-initializers -Wno-missing-prototypes \ + -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function \ + -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value \ + -Wno-empty-body -Wno-uninitialized -Wno-unknown-pragmas -Wno-shadow \ + -Wno-four-char-constants -Wno-conversion -Wno-constant-conversion \ + -Wno-int-conversion -Wno-bool-conversion -Wno-enum-conversion \ + -Wno-sign-conversion -Wno-shorten-64-to-32 -Wpointer-sign -Wno-newline-eof \ + -Wno-trigraphs -Wdeprecated-declarations + +FRAMEWORKS:=-fobjc-link-runtime \ + -framework Foundation -framework OpenGL -framework Cocoa \ + -framework AudioToolbox -framework CoreAudio -framework QuickTime \ + -framework SystemConfiguration \ + -framework ApplicationServices -framework Security \ + -framework QuartzCore diff --git a/build.macos32x86/common.minheadless/Makefile.lib.extra b/build.macos32x86/common.minheadless/Makefile.lib.extra new file mode 100644 index 0000000000..ede8fa839c --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.lib.extra @@ -0,0 +1,33 @@ +# +# THIRDPARTYLIBS The libraries to build (you define them in ./third-party/LIB/Makefile.lib) +# THIRDPARTYDIR Where to build libraries +# THIRDPARTYOUTDIR Where to first install libraries (output place) +# THIRDPARTYCACHEDIR Where to download thirdparty libraries +# +# REQUIRES: +# USEPLUGINASDYLIB Used to define where to place built libraries (default is FALSE (or absent)). +# + +THIRDPARTYDIR?=$(BLDDIR)/third-party +THIRDPARTYLIBDIR?=$(THIRDPARTYOUTDIR)/lib +THIRDPARTYINCLUDEDIR?=$(THIRDPARTYOUTDIR)/include +THIRDPARTYCACHEDIR?=../../.thirdparty-cache +THIRDPARTYOUTDIR?=$(abspath $(THIRDPARTYCACHEDIR)/macOS/i386) + +ifeq ($(USEPLUGINASDYLIB),TRUE) +THIRDPARTYINSTALLDIR:=$(APP)/Contents/MacOS/Plugins +else +THIRDPARTYINSTALLDIR:=$(APP)/Contents/Resources +endif + +$(THIRDPARTYDIR): + if [ ! -d $(THIRDPARTYDIR) ]; then mkdir -p $(THIRDPARTYDIR); fi + +$(THIRDPARTYOUTDIR): $(THIRDPARTYDIR) + if [ ! -d $(THIRDPARTYOUTDIR) ]; then mkdir -p $(THIRDPARTYOUTDIR); fi + +$(THIRDPARTYINSTALLDIR): + if [ ! -d $(THIRDPARTYINSTALLDIR) ]; then mkdir -p $(THIRDPARTYINSTALLDIR); fi + +$(THIRDPARTYCACHEDIR): + if [ ! -d $(THIRDPARTYCACHEDIR) ]; then mkdir -p $(THIRDPARTYCACHEDIR); fi \ No newline at end of file diff --git a/build.macos32x86/common.minheadless/Makefile.plugin b/build.macos32x86/common.minheadless/Makefile.plugin new file mode 100644 index 0000000000..7c6205352b --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.plugin @@ -0,0 +1,243 @@ +############################################################################# +# Generic Makefile for plugins on Cocoa Mac OS X +# Do make getversion to get make -n to work +# +# The following variables can be overridden in the platform Makefile, e.g. +# platforms/iOS/plugins/AioPlugin/Makefile. Look at +# platforms/iOS/plugins/*/Makefile for examples. +# +# CFLAGS adds additional compilation flags to source file compilation +# XCFLAGS adds additional compilation flags after all other flags +# EXCLUDESRC defines patterns of source files to not compile, e.g. %/dump.c +# EXTRALIBS defines additional libraries to link into an external plugin bundle +# INCDIRS defines additonal directories in which to search for includes +# LIBSRC overrides the set of files to compile (if EXCLUDESRC inconvenient) +# LINK_WITH_CPP set to non-empty to link external plugin bundle with c++ runtime +# SRCDIRS supplies additional directories containing files to compile. +# PREREQUISITES supplies names of files that must exist to make lib or bundle +# THIRDPARTYLIBS supplies names for third-party libraries that needs to be built + +$(info $$(LIBNAME): $(LIBNAME)) + +# VM config flags. +ifeq ($(CONFIGURATION),product) +OFLAGS:= -g -Os +AT_MOST_OPT_LEVEL_ONE:=-O1 +DEBUGVM=-DDEBUGVM=0 -DNDEBUG=1 +BUILD:=build +else ifeq ($(CONFIGURATION),assert) +OFLAGS:= -g -O1 -fno-omit-frame-pointer +AT_MOST_OPT_LEVEL_ONE:=-O1 +DEBUGVM=-DDEBUGVM=0 +BUILD:=buildast +else +OFLAGS:= -g -O0 -fno-omit-frame-pointer +AT_MOST_OPT_LEVEL_ONE:=-O0 +DEBUGVM=-DDEBUGVM=1 +BUILD:=builddbg +endif + +CFLAGS:= $(CFLAGS) $(OFLAGS) $(COGDEFS) $(DEBUGVM) $(XDEFS) + +# The following are the four key locations (set via invocation): +# PLUGINSRCDIR: Where is the root of the src/plugins source tree? +# VMSRCDIR: Where is the root of the src vm source tree? +# PLATDIR: Where is the root of the platforms tree? + +############################################################################# +# Standard directory locations: +# CROSSDIR: The location of the cross platform sources +# OSXPLGDIR: The location of the iOS sources +# MAKERDIR: The location of the VMMaker generated sources +# BUILDDIR: The location where the plugin is built +# +#PLUGINSRCDIR:= ../../src/plugins + +ifeq ($(USEPLUGINASDYLIB),TRUE) +# I do not like to use lib "as is" because that will strip all "lib" occurences, but I do not +# find any better solution :( +LIBDIR:=$(subst lib,,$(LIBNAME)) +else +LIBDIR:=$(LIBNAME) +endif + +CROSSDIR:= $(PLATDIR)/Cross/plugins/$(LIBDIR) +OSXDIR:= $(PLATDIR)/iOS/vm/OSX +OSXPLGDIR:= $(PLATDIR)/iOS/plugins/$(LIBDIR) +UNIXDIR:= $(PLATDIR)/unix/vm +MAKERDIR:= $(PLUGINSRCDIR)/$(LIBDIR) +BUILDDIR:= $(BUILD)/$(LIBDIR) + +# Support directory locations +CROSSVMDIR:=$(PLATDIR)/Cross/vm +OSXVMDIR:=$(PLATDIR)/iOS/vm/OSX +MAKERVMDIR:=$(VMSRCDIR) + +# INCDIRS are where include files are searched for. A superset of SRCDIRS. +INCDIRS:= $(MAKERVMDIR) $(CROSSVMDIR) $(MAKERDIR) $(OSXPLGDIR) $(OSXVMDIR) $(CROSSDIR) $(INCDIRS) +INCLUDES:= $(addprefix -I,. $(INCDIRS)) + +############################################################################# +# If no source files were given, use standard set +# + +SRCDIRS:=$(SRCDIRS) $(MAKERDIR) $(CROSSDIR) $(OSXPLGDIR) +LIBSRC?= $(foreach d,$(SRCDIRS),$(wildcard $(d)/*.c) $(wildcard $(d)/*.cpp) $(wildcard $(d)/*.m)) +ifneq ($(EXCLUDESRC),) +LIBSRC:=$(filter-out $(EXCLUDESRC),$(LIBSRC)) +endif + +############################################################################# +# bundle settings +# +# Note: By default DLLTOOL/DLLWRAP do the work for everything related to plugins +# but if LINK_WITH_GCC we use gcc and if LINK_WITH_GPP we use g++. +# +DLLTOOL:= dlltool +DLLWRAP:= dllwrap -mno-cygwin +#OPTSTRIP:= strip # for production +OPTSTRIP:= echo not doing strip + +############################################################################# +# Plugin settings +# +OBJDIR:= $(BUILD)/$(LIBNAME) +VMDIR:= $(BUILD)/vm +PLUGINLIB:= $(VMDIR)/$(LIBNAME).lib +PLUGINBUNDLE:= $(VMDIR)/$(LIBNAME).bundle +PLUGINEXE:= $(PLUGINBUNDLE)/Contents/MacOS/$(LIBNAME) + +PLUGINDYLIB:= $(VMDIR)/$(LIBNAME).dylib + +# https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html +PLUGINPLIST:= $(PLUGINBUNDLE)/Contents/Info.plist +PLUGINICONS:= $(PLUGINBUNDLE)/Contents/Resources/SqueakPlugin.icns +LIBOBJ?=$(notdir $(LIBSRC)) +LIBOBJ:=$(patsubst %.c,%.o,$(patsubst %.m,%.o,$(patsubst %.cpp,%.o,$(LIBOBJ)))) +LIBOBJ:=$(addprefix $(OBJDIR)/,$(LIBOBJ)) + +VPATH:= $(SRCDIRS) + +# If plugins change from internal to external they must be recompiled. +$(BUILD)/$(LIBNAME)/$(LIBNAME).o: plugins.int plugins.ext + +############################################################################# +# Rules for automated builds + +include ../common.minheadless/Makefile.rules + +all: $(PLUGINLIB) $(PLUGINBUNDLE) $(PLUGINDYLIB) + +print-settings: + @echo ---------------- Makefile.plugin settings ------------------ + @echo PWD=$(shell pwd) + @echo MAKEFILE=$(MAKEFILE) + @echo VPATH=$(VPATH) + @echo INCLUDES=$(INCLUDES) + @echo CFLAGS=$(CFLAGS) + @echo PLUGINSRCDIR=$(PLUGINSRCDIR) + @echo VMSRCDIR=$(VMSRCDIR) + @echo CROSSDIR=$(CROSSDIR) + @echo OSXPLGDIR=$(OSXPLGDIR) + @echo OSXDIR=$(OSXDIR) + @echo MAKERDIR=$(MAKERDIR) + @echo CROSSSRC=$(CROSSSRC) + @echo OSXSRC=$(OSXSRC) + @echo MAKERSRC=$(MAKERSRC) + @echo SRCDIRS=$(SRCDIRS) + @echo INCDIRS=$(INCDIRS) + @echo LIBSRC=$(LIBSRC) + @echo LIBOBJ=$(LIBOBJ) + @echo BUILD=$(BUILD) + @echo OBJDIR=$(OBJDIR) + @echo VMDIR=$(VMDIR) + @echo PLUGINLIB=$(PLUGINLIB) + @echo PLUGINEXE=$(PLUGINEXE) + @echo PLUGINBUNDLE=$(PLUGINBUNDLE) + @echo deps=$(patsubst %,deps/%.d,$(notdir $(basename $(LIBSRC)))) + @echo ----------------------------------------------------- + +$(OBJDIR): + mkdir -p $(OBJDIR) + +$(VMDIR): + mkdir -p $(VMDIR) + +# If any prerequisites are declared all must exist to continue +ifeq ($(realpath $(PREREQUISITES)),$(abspath $(PREREQUISITES))) + +$(PLUGINLIB): $(PLUGINREQS) $(VMDIR) $(OBJDIR) $(LIBOBJ) + -rm $(PLUGINLIB) + ar -rc $(PLUGINLIB) $(LIBOBJ) + +# Either link with normal compiler/linker or with cpp compiler/linker. If +# LINK_WITH_CPP is set, use e.g. clang++ +$(PLUGINBUNDLE): $(PLUGINREQS) $(PLUGINEXE) $(PLUGINICONS) $(PLUGINPLIST) + +$(PLUGINEXE): $(PLUGINREQS) $(VMDIR) $(OBJDIR) $(LIBOBJ) + mkdir -p $(PLUGINBUNDLE)/Contents/MacOS +ifneq ($(LINK_WITH_CPP),) + $(LDCXX) \ + $(BFLAGS) \ + -Wl,-bundle_loader,$(VMDIR)/$(VM) \ + $(LIBOBJ) $(EXTRALIBS) \ + -o $(PLUGINBUNDLE)/Contents/MacOS/$(LIBNAME) +else + $(LD) \ + $(BFLAGS) \ + -Wl,-bundle_loader,$(VMDIR)/$(VM) \ + $(LIBOBJ) $(EXTRALIBS) \ + -o $(PLUGINBUNDLE)/Contents/MacOS/$(LIBNAME) +endif + +$(PLUGINICONS): $(OSXDIR)/SqueakPlugin.icns + mkdir -p $(PLUGINBUNDLE)/Contents/Resources + cp -p $< $@ + +$(PLUGINPLIST): $(PLATDIR)/iOS/plugins/Info.plist getversion + -mkdir -p $(PLUGINBUNDLE)/Contents + cp -p $< $@ + sed -i '' '\ + s!$$(VERSION)!$(shell ./getversion VERSION_TAG)!g;\ + s!$$(LIBNAME)!$(LIBNAME)!g;\ + ' $@ + +$(PLUGINDYLIB): $(PLUGINREQS) $(VMDIR) $(OBJDIR) $(LIBOBJ) +ifneq ($(LINK_WITH_CPP),) + $(LDCXX) \ + $(DYFLAGS) \ + -install_name @executable_path/Plugins/$(LIBNAME).dylib \ + $(LIBOBJ) $(EXTRALIBS) \ + -o $(VMDIR)/$(LIBNAME).dylib +else + $(LD) \ + $(DYFLAGS) \ + -install_name @executable_path/Plugins/$(LIBNAME).dylib \ + $(LIBOBJ) $(EXTRALIBS) \ + -o $(VMDIR)/$(LIBNAME).dylib +endif + +getversion: + make -f ../common.minheadless/Makefile.vm getversion + +else # ifeq ($(realpath $(PREREQUISITES)),$(PREREQUISITES)) +# If any prerequisites are missing simply create a .ignore file + +#$(info $$(PREREQUISITES): $(abspath $(PREREQUISITES))) +#$(info $$(realpath $$(PREREQUISITES)): $(realpath $(PREREQUISITES))) + +$(PLUGINLIB): FORCE + $(warning $(PLUGINLIB) has missing prerequisites. Not building.) + echo >$(basename $(PLUGINLIB)).ignore + +$(PLUGINBUNDLE): FORCE + $(warning $(PLUGINBUNDLE) has missing prerequisites. Not building.) + echo >$(basename $(PLUGINBUNDLE)).ignore + +$(PLUGINDYLIB): FORCE + $(warning $(PLUGINDYLIB) has missing prerequisites. Not building.) + echo >$(basename $(PLUGINDYLIB)).ignore + +FORCE: + +endif diff --git a/build.macos32x86/common.minheadless/Makefile.rules b/build.macos32x86/common.minheadless/Makefile.rules new file mode 100644 index 0000000000..e9a4fce9da --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.rules @@ -0,0 +1,34 @@ +############################################################################# +# Compilation rules for Mac OS X +# +# See http://make.mad-scientist.net/papers/advanced-auto-dependency-generation +# for an explanation of the dependency management scheme. + +# /usr/bin/clang, a.k.a. /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang +CC := clang +LD := clang +LDCXX := clang++ # For linking c++ bundles + +include ../common.minheadless/Makefile.flags + +DEPFLAGS = -MT $@ -MMD -MP -MF deps/$(*F).Td +ALLFLAGS = $(DEPFLAGS) $(WARNINGS) $(CFLAGS) +POSTCOMPILE = sed '/^$$/d' deps/$(*F).d; rm deps/$(*F).Td; touch -r $< deps/$(*F).d + +$(OBJDIR)/%.o: %.c deps/%.d $(MAKEFILE) + $(CC) -x c $(ALLFLAGS) $(INCLUDES) -c $< -o $@ + $(POSTCOMPILE) + +$(OBJDIR)/%.o: %.m deps/%.d $(MAKEFILE) + $(CC) -x objective-c $(ALLFLAGS) $(INCLUDES) -c $< -o $@ + $(POSTCOMPILE) + +$(OBJDIR)/%.o: %.cpp deps/%.d $(MAKEFILE) + $(CC) -x c++ $(ALLFLAGS) $(INCLUDES) -c $< -o $@ + $(POSTCOMPILE) + +deps/%.d: ; + +.PRECIOUS: deps/%.d + +-include $(patsubst %,deps/%.d,$(notdir $(basename $(VMSRC) $(LIBSRC)))) diff --git a/build.macos32x86/common.minheadless/Makefile.sources b/build.macos32x86/common.minheadless/Makefile.sources new file mode 100644 index 0000000000..a77f5e2a19 --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.sources @@ -0,0 +1,24 @@ +############################################################################# +# Generic Makefile for downloading sources + +SOURCESDIR:=../../sources + +$(SOURCESDIR): + mkdir -p $(SOURCESDIR) + +%.sources: $(SOURCESDIR) $(SOURCESDIR)/%.sources + test -f $@ || ln $(SOURCESDIR)/$(notdir $@) $@ + +$(SOURCESDIR)/PharoV50.sources: + curl http://files.pharo.org/get-files/50/sources.zip -o $@.zip + (cd $(SOURCESDIR); unzip $(@F).zip) + chmod 444 $@ + -rm $@.zip + +$(SOURCESDIR)/SqueakV50.sources \ +$(SOURCESDIR)/SqueakV46.sources \ +$(SOURCESDIR)/SqueakV41.sources: + curl http://ftp.squeak.org/sources_files/$(@F).gz -o $@.gz + (cd $(SOURCESDIR); gunzip $(@F).gz) + chmod 444 $@ + -rm $@.zip diff --git a/build.macos32x86/common.minheadless/Makefile.vm b/build.macos32x86/common.minheadless/Makefile.vm new file mode 100644 index 0000000000..73fd14781c --- /dev/null +++ b/build.macos32x86/common.minheadless/Makefile.vm @@ -0,0 +1,299 @@ +############################################################################# +# Generic Makefile for Mac OS VM +# Do make getversion to get makwe -n to work +# +# This is designed to be invoked by Makefile in a specific build directory via +# include ../common.minheadless/Makefile.app +# +# Parameters: +# VMSRCDIR defines the locaton of the VM source to build. Required. +# +# COGDEFS supply any command-line defines to use, and may be null. +# +# The name of the VM to build, e.g. Squeak or "Newspeak Virtual Machine" +# and its short name, e.g. Squeak or Newspeak, used to choose app icon files. + +VM?=Squeak +SYSTEM?=$(VM) + +# PLUGINSRCDIR defines the locaton of the plugin source, the subsets of which +# selected by plugins.int and plugins.ext will be built. + +PLUGINSRCDIR?=../../src/plugins + +# CONFIGURATION defines what version of the VM to build, product, assert or +# debug. The default is overridden in the mvm script + +CONFIGURATION?=product + + +############################################################################# +# Build directories +# + +ifeq ($(CONFIGURATION),assert) + BUILD:=buildast +else ifeq ($(CONFIGURATION),debug) + BUILD:=builddbg +else # default CONFIGURATION=product + BUILD:=build +endif +$(shell mkdir -p deps >/dev/null) # deps is the dependencies directory +BLDDIR:= $(BUILD) +OBJDIR:= $(BLDDIR)/vm + +PLATDIR:=../../platforms +CROSSDIR:=$(PLATDIR)/Cross/vm +OSXDIR:=$(PLATDIR)/minheadless/unix +OSXCOMMONDIR:=$(PLATDIR)/minheadless/common +OSXSDLDIR:=$(PLATDIR)/minheadless/sdl2-window +OSXPLUGINSDIR:=$(PLATDIR)/iOS/plugins + +MAKERSRC:=$(wildcard $(VMSRCDIR)/gcc3x-*interp.c $(VMSRCDIR)/cogit.c) +CROSSSRC:= $(wildcard $(CROSSDIR)/*.c) +OSXSRC=$(wildcard $(OSXDIR)/*.c) $(wildcard $(OSXCOMMONDIR)/*.c) \ + $(wildcard $(OSXDIR)/*.m) $(wildcard $(OSXCOMMONDIR)/*.m) +OSXSDLSRC:= $(wildcard $(OSXSDLDIR)/*.c) +VMSRC:= $(MAKERSRC) $(CROSSSRC) $(OSXSRC) $(OSXSDLSRC) +VMOBJ:= $(notdir $(VMSRC)) +VMOBJ:= $(VMOBJ:.c=.o) +VMOBJ:= $(VMOBJ:.m=.o) +VMOBJ:= $(VMOBJ:.cpp=.o) +VMOBJ:= $(addprefix $(OBJDIR)/,$(VMOBJ)) + +# N.B. Most important that OSXCOMMONDIR precede OSXDIR so that the above +# exclusion of OSXDIR/sqSqueakMainApplication+screen.m works, since there +# exists a OSXCOMMONDIR/sqSqueakMainApplication+screen.m. +SRCDIRS:=$(VMSRCDIR) $(OSXCOMMONDIR) $(OSXDIR) $(CROSSDIR) $(OSXSDLDIR) +VPATH:=$(SRCDIRS) +# Additonal includes are required, eg for SoundPlugin/sqSqueakSoundCoreAudio.h +PluginIncludes := FilePlugin HostWindowPlugin SoundPlugin +OSPluginIncludes := FilePlugin HostWindowPlugin SoundPlugin +BLDDIRINC=$(BLDDIR)/include +SRCDIRS:=$(SRCDIRS) \ + $(BLDDIRINC) \ + $(PLATDIR)/unix/vm \ + $(PLATDIR)/../include \ + $(addprefix $(PLATDIR)/Cross/plugins/,$(PluginIncludes)) \ + $(addprefix $(OSXPLUGINSDIR)/,$(OSPluginIncludes)) + + +# The internal (.lib) and external (.bundle) plugins +# +include plugins.int +include plugins.ext + +LIBS:= $(addprefix $(OBJDIR)/, $(addsuffix .lib, $(INTERNAL_PLUGINS))) +BUNDLES:= $(addprefix $(OBJDIR)/, $(addsuffix .bundle, $(EXTERNAL_PLUGINS))) + +# VM config flags. +ifeq ($(CONFIGURATION),debug) +OFLAGS:= -g -O0 -fno-omit-frame-pointer +DEBUGVM=-DDEBUGVM=1 +INTERPFLAGS:=-DAllocationCheckFiller=0xADD4E55 -D'VM_LABEL(foo)=0' +else ifeq ($(CONFIGURATION),assert) +OFLAGS:= -g -O1 -fno-omit-frame-pointer +DEBUGVM=-DDEBUGVM=0 +INTERPFLAGS:=-DAllocationCheckFiller=0xADD4E55 -D'VM_LABEL(foo)=0' +else # default CONFIGURATION=product +OFLAGS:= -g -Os +DEBUGVM=-DDEBUGVM=0 -DNDEBUG=1 # Define NDEBUG for production to exclude asserts +INTERPFLAGS:=-D'VM_LABEL(foo)=0' +endif + +TZ:=$(shell date +%Z) +DEFS:= $(COGDEFS) -DUSE_GLOBAL_STRUCT=0 -DNO_ISNAN=1 \ + -DUSE_INLINE_MEMORY_ACCESSORS -D'TZ="$(TZ)"' \ + -DVM_NAME=\"$(APPNAME)\" \ + $(INTERPFLAGS) + +# DEFS:= $(COGDEFS) -DUSE_GLOBAL_STRUCT=0 -DNO_ISNAN=1 \ +# -DUSE_CORE_GRAPHICS=1 \ +# -DUSE_INLINE_MEMORY_ACCESSORS -D'TZ="$(TZ)"' \ +# $(INTERPFLAGS) + +XDEFS:= -DSQUEAK_BUILTIN_PLUGIN +CFLAGS:= $(OFLAGS) $(COGDEFS) $(DEBUGVM) $(DEFS) $(XDEFS) +INCLUDES:= $(addprefix -I,. $(SRCDIRS)) + +############################################################################# +# Linker settings +# +LD:= gcc + +############################################################################# +# +SVNREV:= $(shell sed -e "s/^static.*GitRawRevisionString.*Rev: \([0-9][0-9]*\).*/\\1/p" -e d $(PLATDIR)/Cross/vm/sqSCCSVersion.h) + + +############################################################################# +# Common build rules +# +include ../common.minheadless/Makefile.rules + +svnver: + @-echo $(SVNREV) + +getversion: $(OSXCOMMONDIR)/version.c deps/version.d $(CROSSDIR)/sqSCCSVersion.h + $(CC) -x c -DVERSION_PROGRAM=1 $(ALLFLAGS) $(INCLUDES) $< -o $@ + $(POSTCOMPILE) + +-include deps/version.d + +product: + $(MAKE) -f $(MAKEFILE_LIST) CONFIGURATION=product $(@,product=) default + +assert: + $(MAKE) -f $(MAKEFILE_LIST) CONFIGURATION=assert $(@,assert=) default + +debug: + $(MAKE) -f $(MAKEFILE_LIST) CONFIGURATION=debug $(@,debug=) default + +cleanallvm: cleanvm cleanvmast cleanvmdbg cleangv + +cleanvm: + rm -rf sqNamedPrims.h build deps + +cleanvmast: + rm -rf sqNamedPrims.h buildast + +cleanvmdbg: + rm -rf sqNamedPrims.h builddbg + +cleangv: + rm -rf getversion getversion.* + +print-source: + @echo ---------------- Makefile.vm sources ------------------ + @echo CROSSSRC=$(CROSSSRC) + @echo OSXSRC=$(OSXSRC) + @echo MAKERSRC=$(MAKERSRC) + @echo UNIXSRC=$(UNIXSRC) + @echo VMSRC=$(VMSRC) + +print-settings: + @echo ---------------- Makefile.vm settings ------------------ + @echo CONFIGURATION=$(CONFIGURATION) + @echo VPATH=$(VPATH) + @echo INCLUDES=$(INCLUDES) + @echo CFLAGS=$(CFLAGS) + @echo INTERNAL_PLUGINS=$(INTERNAL_PLUGINS) + @echo EXTERNAL_PLUGINS=$(EXTERNAL_PLUGINS) + @echo OBJDIR=$(OBJDIR) + @echo LIBS=$(LIBS) + @echo BUNDLES=$(BUNDLES) + @echo deps=$(patsubst %,deps/%.d,$(notdir $(basename $(VMSRC)))) + @echo ----------------------------------------------------- + +print-objects: + @echo ---------------- Makefile.vm objects ------------------ + @echo VMOBJ=$(VMOBJ) + @echo ----------------------------------------------------- + +ignore := $(addsuffix .%, $(basename $(wildcard $(BUILD)/vm/*.ignore))) + +$(OBJDIR)/$(VM): headers_built $(EXTRAVMLIBS) $(OBJDIR) $(VMOBJ) $(LIBS) + $(CC) -arch $(TARGET_ARCH) -o $(OBJDIR)/version.o $(CFLAGS) $(INCLUDES) $(DEFS) -c $(OSXCOMMONDIR)/version.c + $(CC) -arch $(TARGET_ARCH) -isysroot $(SDK) $(LDFLAGS) $(FRAMEWORKS) \ + -o $(OBJDIR)/$(VM) $(VMOBJ) $(filter-out $(call ignore), $(LIBS)) + +$(OBJDIR): + @-mkdir -p $(BLDDIR) + mkdir -p $(OBJDIR) +ifneq ($(INTERNAL_PLUGINS),) + mkdir -p $(addprefix $(BLDDIR)/, $(INTERNAL_PLUGINS)) +endif +ifneq ($(EXTERNAL_PLUGINS),) + mkdir -p $(addprefix $(BLDDIR)/, $(EXTERNAL_PLUGINS)) +endif + +############################################################################# +# Building plugins +# +.PHONY: $(OBJDIR)/%.lib $(OBJDIR)/%.bundle $(OBJDIR)/%.dylib + +ifeq ($(USEPLUGINASDYLIB),TRUE) +plugin-makefile = $(firstword $(realpath $(OSXPLUGINSDIR)/$(subst lib,,$(1))/Makefile ../common.minheadless/Makefile.plugin)) +else +plugin-makefile = $(firstword $(realpath $(OSXPLUGINSDIR)/$(1)/Makefile ../common.minheadless/Makefile.plugin)) +endif + +# Internal plugin. Build as lib then link in lib +# Check for Makefile in iOS plugins directory otherwise use default Makefile +# N.B. PLATDIR *must* be a relative path for this to work +$(OBJDIR)/%.lib: FORCE + @-mkdir -p $(BLDDIR)/$(*F) + test $@ -ot $(call plugin-makefile,$(*F)) && rm -rf $(BUILD)/vm/$(*F).* || true + rm -f $(BUILD)/vm/$(*F).ignore + $(MAKE) $(MFLAGS) BUILD=$(BUILD) \ + -f $(call plugin-makefile,$(*F)) MAKEFILE=$(call plugin-makefile,$(*F)) \ + CONFIGURATION=$(CONFIGURATION) ARCH=$(TARGET_ARCH) \ + PLATDIR=$(PLATDIR) PLUGINSRCDIR=$(PLUGINSRCDIR) VMSRCDIR=$(VMSRCDIR) \ + LIBNAME=$(*F) COGDEFS="$(COGDEFS)" XDEFS=-DSQUEAK_BUILTIN_PLUGIN \ + $(PLUGINHACK) \ + $(OBJDIR)/$(*F).lib + +prereqs/%.lib: + @-ls -rlt $(call plugin-makefile,$(*F)) $(wildcard $(*F).ignore) \ + $(wildcard $(PLUGINSRCDIR)/$(*F)/*.c) \ + $(wildcard $(PLATDIR)/Cross/plugins/$(*F)/*.*) \ + $(wildcard $(OSXPLUGINSDIR)/$(*F)/*.*) + +# It would be nice to have this abbreviation but it creates havoc eem 2/2016 +#%.lib: $(OBJDIR)/%.lib +# make $(MAKEFLAGS) $< + +# External plugin. Build as bundle and copy to vm dir ($(OBJDIR)). +# Check for Makefile in iOS plugins directory otherwise use default Makefile +$(OBJDIR)/%.bundle: FORCE + @-mkdir -p $(BLDDIR)/$(*F) + test $@ -ot $(call plugin-makefile,$(*F)) && rm -rf $(BUILD)/vm/$(*F).* || true + rm -f $(BUILD)/vm/$(*F).ignore + $(MAKE) $(MFLAGS) BUILD=$(BUILD) \ + -f $(call plugin-makefile,$(*F)) MAKEFILE=$(call plugin-makefile,$(*F)) \ + CONFIGURATION=$(CONFIGURATION) ARCH=$(TARGET_ARCH) \ + PLATDIR=$(PLATDIR) PLUGINSRCDIR=$(PLUGINSRCDIR) VMSRCDIR=$(VMSRCDIR) \ + LIBNAME=$(*F) COGDEFS="$(COGDEFS)" \ + APP=$(APP) VM=$(VM) BLDDIR=$(BLDDIR) \ + THIRDPARTYOUTDIR=$(THIRDPARTYOUTDIR) \ + $(PLUGINHACK) \ + $(OBJDIR)/$(*F).bundle + +# External plugin. Made in the "Pharo way": as a simple dylib to be included in +# Plugins directory +$(OBJDIR)/%.dylib: FORCE + @-mkdir -p $(BLDDIR)/$(*F) + test $@ -ot $(call plugin-makefile,$(*F)) && rm -rf $(BUILD)/vm/$(*F).* || true + rm -f $(BUILD)/vm/$(*F).ignore + $(MAKE) $(MFLAGS) BUILD=$(BUILD) \ + -f $(call plugin-makefile,$(*F)) MAKEFILE=$(call plugin-makefile,$(*F)) \ + CONFIGURATION=$(CONFIGURATION) ARCH=$(TARGET_ARCH) \ + PLATDIR=$(PLATDIR) PLUGINSRCDIR=$(PLUGINSRCDIR) VMSRCDIR=$(VMSRCDIR) \ + LIBNAME=$(*F) COGDEFS="$(COGDEFS)" \ + APP=$(APP) VM=$(VM) BLDDIR=$(BLDDIR) USEPLUGINASDYLIB=TRUE \ + THIRDPARTYOUTDIR=$(THIRDPARTYOUTDIR) \ + $(PLUGINHACK) \ + $(OBJDIR)/$(*F).dylib + +# It would be nice to have this abbreviation but it creates havoc eem 2/2016 +#%.bundle: $(OBJDIR)/%.bundle +# make $(MAKEFLAGS) $< + +FORCE: + +.PRECIOUS: $(OBJDIR)/%.lib $(OBJDIR)/%.bundle + +############################################################################# +# Extra specific dependencies +# +$(BLDDIRINC)/sqInternalPlugins.inc: plugins.int + mkdir -p $(BLDDIRINC) + ../common.minheadless/mkInternalPluginsList.sh plugins.int > $(BLDDIRINC)/sqInternalPlugins.inc + +headers_built: $(BLDDIRINC)/config.h $(BLDDIRINC)/sqInternalPlugins.inc + +$(BLDDIRINC)/config.h: + mkdir -p $(BLDDIRINC) + mkdir -p CMakeFiles + cd CMakeFiles && cmake -DONLY_CONFIG_H=ON -DBUILD_I386_VERSION=ON ../../.. + mv CMakeFiles/config.h $(BLDDIRINC) diff --git a/build.macos32x86/common.minheadless/mkInternalPluginsList.sh b/build.macos32x86/common.minheadless/mkInternalPluginsList.sh new file mode 100755 index 0000000000..935c006880 --- /dev/null +++ b/build.macos32x86/common.minheadless/mkInternalPluginsList.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Generate a sqInternalPlugins.inc file from plugins.int. The plugins.int used should +# be the one and only argument. +if [ $# != 1 -o ! -f "$1" ]; then + echo usage $0 plugins.int \>sqNamedPrims.h 1>&2 + exit 1 +fi +for p in `grep -v '^#' "$1" | sed 's/INTERNAL_PLUGINS = //' | sed 's/ *\\\\//'` +do + test -n "$p" && echo "INTERNAL_PLUGIN("$p")" +done diff --git a/build.macos32x86/common.minheadless/mkNamedPrims.sh b/build.macos32x86/common.minheadless/mkNamedPrims.sh new file mode 100755 index 0000000000..314f5b22c6 --- /dev/null +++ b/build.macos32x86/common.minheadless/mkNamedPrims.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Generate a sqNamedPrims.h file from plugins.int. The plugins.int used should +# be the one and only argument. +if [ $# != 1 -o ! -f "$1" ]; then + echo usage $0 plugins.int \>sqNamedPrims.h 1>&2 + exit 1 +fi +echo "/* Automatically generated on "`date`" */" +echo "extern sqExport vm_exports[];"; +echo "extern sqExport os_exports[];"; +for p in `grep -v '^#' "$1" | sed 's/INTERNAL_PLUGINS = //' | sed 's/ *\\\\//'` +do + test -n "$p" && echo "extern sqExport "$p"_exports[];" +done +echo +echo "sqExport *pluginExports[] = {" +echo " vm_exports," +echo " os_exports," +for p in `grep -v '^#' "$1" | sed 's/INTERNAL_PLUGINS = //' | sed 's/ *\\\\//'` +do + echo " "$p"_exports," +done +echo " NULL" +echo "};" diff --git a/build.macos32x86/pharo.cog.spur.minheadless/Makefile b/build.macos32x86/pharo.cog.spur.minheadless/Makefile new file mode 100644 index 0000000000..acf8817649 --- /dev/null +++ b/build.macos32x86/pharo.cog.spur.minheadless/Makefile @@ -0,0 +1,35 @@ +############################################################################## +# Makefile for Mac OS X Cog Spur Pharo Cocoa VM using clang and gnu make 3.81 +# Do make init to allow make -n to function. +# + +VMSRCDIR:=../../spursrc/vm +VM:=Pharo +COGDEFS:= -DPharoVM=1 -DIMMUTABILITY=1 +APPNAME:=Pharo +APPNAMEDEF:=$(APPNAME) +APPIDENTIFIER:=org.pharo.$(APPNAME) +USEPLUGINASDYLIB:=TRUE +THIRDPARTYLIBS:=pkgconfig freetype2 openssl libssh2 libgit2 libsdl2 pixman libpng cairo +EXTRAVMLIBS=libsdl2 + +# Now include the Makefile proper, which is common to all Mac OS builds. +# +include ../common.minheadless/Makefile.app + +# third-party libraries +# +include ../third-party/Makefile.pkgconfig +include ../third-party/Makefile.freetype2 +include ../third-party/Makefile.openssl +include ../third-party/Makefile.libssh2 +include ../third-party/Makefile.libgit2 +include ../third-party/Makefile.libsdl2 +include ../third-party/Makefile.pixman +include ../third-party/Makefile.libpng +include ../third-party/Makefile.cairo + +# extras for headless VM to open world with SDL2 +# +CFLAGS+= -I$(THIRDPARTYINCLUDEDIR)/SDL2 -DHAVE_SDL2=1 +LDFLAGS+= -L$(THIRDPARTYOUTDIR)/lib -lSDL2 \ No newline at end of file diff --git a/build.macos32x86/pharo.cog.spur.minheadless/mvm b/build.macos32x86/pharo.cog.spur.minheadless/mvm new file mode 100755 index 0000000000..83f6c219ac --- /dev/null +++ b/build.macos32x86/pharo.cog.spur.minheadless/mvm @@ -0,0 +1,32 @@ +#!/bin/bash -e +A=;D=;F= +if [ $# = 0 ]; then + A=1;D=1;F=1 +else + while getopts 'ASTadf?' opt "$@"; do + case $opt in + A) A=1;D=1;F=1;; + S) echo -S not yet implemented\; use -A for now 1>&1; exit 1;; + T) echo -T not yet implemented\; use -A for now 1>&1; exit 1;; + a) A=1;; + d) D=1;; + f) F=1;; + *) echo usage $0 [-A] [-a\ [-d] [-f]; exit 0;; + esac + done + if [ "$1" = -- ]; then + if [ "$A$D$F" = "" ]; then + A=1;D=1;F=1 + fi + fi + shift `expr $OPTIND - 1` +fi +if [ -n "$D" ]; then + make $@ debug 2>&1 | tee LOGD ; test ${PIPESTATUS[0]} -eq 0 +fi +if [ -n "$A" ]; then + make $@ assert 2>&1 | tee LOGA ; test ${PIPESTATUS[0]} -eq 0 +fi +if [ -n "$F" ]; then + make $@ 2>&1 | tee LOGF ; test ${PIPESTATUS[0]} -eq 0 +fi diff --git a/build.macos32x86/pharo.cog.spur.minheadless/plugins.ext b/build.macos32x86/pharo.cog.spur.minheadless/plugins.ext new file mode 100644 index 0000000000..b8c0eb77db --- /dev/null +++ b/build.macos32x86/pharo.cog.spur.minheadless/plugins.ext @@ -0,0 +1,27 @@ +# Copied, perhaps edited, from ../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +ClipboardExtendedPlugin \ +CroquetPlugin \ +FT2Plugin \ +FloatMathPlugin \ +JPEGReadWriter2Plugin \ +JPEGReaderPlugin \ +LocalePlugin \ +Mpeg3Plugin \ +RePlugin \ +SqueakSSL \ +SurfacePlugin \ +UUIDPlugin \ +SerialPlugin \ +ObjectiveCPlugin \ +SDL2DisplayPlugin \ +EventsHandlerPlugin \ +# TEMPORARY REMOVE +# B3DAcceleratorPlugin \ +# could work, but I don't know if relevant: +# TestOSAPlugin \ +# not working (because of Cocoa vs Carbon): +# MIDIPlugin \ +# QuicktimePlugin \ +# JoystickTabletPlugin diff --git a/build.macos32x86/pharo.cog.spur.minheadless/plugins.int b/build.macos32x86/pharo.cog.spur.minheadless/plugins.int new file mode 100644 index 0000000000..50fe47f245 --- /dev/null +++ b/build.macos32x86/pharo.cog.spur.minheadless/plugins.int @@ -0,0 +1,28 @@ +# Copied, perhaps edited, from ../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AioPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BMPReadWriterPlugin \ +BitBltPlugin \ +DSAPrims \ +DropPlugin \ +FFTPlugin \ +FilePlugin \ +FloatArrayPlugin \ +GeniePlugin \ +IA32ABI \ +LargeIntegers \ +Matrix2x3Plugin \ +MiscPrimitivePlugin \ +SecurityPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SqueakFFIPrims \ +StarSqueakPlugin \ +UnixOSProcessPlugin \ +VMProfileMacSupportPlugin \ +ZipPlugin \ +# Klatt \ No newline at end of file diff --git a/build.minheadless.cmake/x64/common/Toolchain-mingw32-cygwin.cmake b/build.minheadless.cmake/x64/common/Toolchain-mingw32-cygwin.cmake new file mode 100644 index 0000000000..5753b26565 --- /dev/null +++ b/build.minheadless.cmake/x64/common/Toolchain-mingw32-cygwin.cmake @@ -0,0 +1,21 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME Windows) +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) + +# specify the cross compiler +set(CMAKE_TOOLCHAIN_PREFIX x86_64-w64-mingw32) + +SET(CMAKE_C_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-gcc) +SET(CMAKE_CXX_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-g++) +SET(CMAKE_RC_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-windres) +SET(CMAKE_SYSTEM_PROCESSOR x86_64) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32/sys-root/mingw/) + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/build.minheadless.cmake/x64/common/configure_variant.sh b/build.minheadless.cmake/x64/common/configure_variant.sh new file mode 100644 index 0000000000..5683c4ae49 --- /dev/null +++ b/build.minheadless.cmake/x64/common/configure_variant.sh @@ -0,0 +1,18 @@ +#!/bin/sh +VARIANT_FOLDER="$1" +VARIANT_NAME="$2" +OS_NAME="`uname -o`" +GENERATOR_NAME="Unix Makefiles" + +rm -rf "./$VARIANT_FOLDER" +mkdir "./$VARIANT_FOLDER" +cd "./$VARIANT_FOLDER" + +if [ "$OS_NAME" = "Msys" ]; then + GENERATOR_NAME="MSYS Makefiles" +fi + +if [ "$OS_NAME" = "Cygwin" ]; then + export CC="x86_64-w64-mingw32-gcc" + export CXX="x86_64-w64-mingw32-g++" +fi diff --git a/build.minheadless.cmake/x64/pharo.cog.spur/Makefile b/build.minheadless.cmake/x64/pharo.cog.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.cog.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x64/pharo.cog.spur/mvm b/build.minheadless.cmake/x64/pharo.cog.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.cog.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure b/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure_variant b/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure_variant new file mode 100755 index 0000000000..1b74f683d5 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.cog.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DPHARO_BRANDING=On -DSPUR_OBJECT_MODEL=On -DCOG_JIT=On diff --git a/build.minheadless.cmake/x64/pharo.stack.spur/Makefile b/build.minheadless.cmake/x64/pharo.stack.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.stack.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x64/pharo.stack.spur/mvm b/build.minheadless.cmake/x64/pharo.stack.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.stack.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure b/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure_variant b/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure_variant new file mode 100755 index 0000000000..bb6d39352a --- /dev/null +++ b/build.minheadless.cmake/x64/pharo.stack.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DPHARO_BRANDING=On -DSPUR_OBJECT_MODEL=On -DCOG_JIT=Off diff --git a/build.minheadless.cmake/x64/squeak.cog.spur/Makefile b/build.minheadless.cmake/x64/squeak.cog.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.cog.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x64/squeak.cog.spur/mvm b/build.minheadless.cmake/x64/squeak.cog.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.cog.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure b/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure_variant b/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure_variant new file mode 100755 index 0000000000..7cd96ac761 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.cog.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DPHARO_BRANDING=Off -DSPUR_OBJECT_MODEL=On -DCOG_JIT=On diff --git a/build.minheadless.cmake/x64/squeak.stack.spur/Makefile b/build.minheadless.cmake/x64/squeak.stack.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.stack.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x64/squeak.stack.spur/mvm b/build.minheadless.cmake/x64/squeak.stack.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.stack.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure b/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure_variant b/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure_variant new file mode 100755 index 0000000000..2925e33155 --- /dev/null +++ b/build.minheadless.cmake/x64/squeak.stack.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DPHARO_BRANDING=Off -DSPUR_OBJECT_MODEL=On -DCOG_JIT=Off diff --git a/build.minheadless.cmake/x86/common/Toolchain-mingw32-cygwin.cmake b/build.minheadless.cmake/x86/common/Toolchain-mingw32-cygwin.cmake new file mode 100644 index 0000000000..98aabf2f0e --- /dev/null +++ b/build.minheadless.cmake/x86/common/Toolchain-mingw32-cygwin.cmake @@ -0,0 +1,21 @@ +# this one is important +SET(CMAKE_SYSTEM_NAME Windows) +#this one not so much +SET(CMAKE_SYSTEM_VERSION 1) + +# specify the cross compiler +set(CMAKE_TOOLCHAIN_PREFIX i686-w64-mingw32) + +SET(CMAKE_C_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-gcc) +SET(CMAKE_CXX_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-g++) +SET(CMAKE_RC_COMPILER ${CMAKE_TOOLCHAIN_PREFIX}-windres) +SET(CMAKE_SYSTEM_PROCESSOR i686) + +# where is the target environment +SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32/sys-root/mingw/) + +# search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/build.minheadless.cmake/x86/common/configure_variant.sh b/build.minheadless.cmake/x86/common/configure_variant.sh new file mode 100644 index 0000000000..e2f71cf89e --- /dev/null +++ b/build.minheadless.cmake/x86/common/configure_variant.sh @@ -0,0 +1,24 @@ +#!/bin/sh +VARIANT_FOLDER="$1" +VARIANT_NAME="$2" +OS_NAME="`uname`" +GENERATOR_NAME="Unix Makefiles" +CMAKE_EXTRA_ARGS="" + +rm -rf "./$VARIANT_FOLDER" +mkdir "./$VARIANT_FOLDER" +cd "./$VARIANT_FOLDER" + +if [ "$OS_NAME" != "Darwin" ]; then + OS_NAME="`uname -o`" +fi + +if [ "$OS_NAME" = "Msys" ]; then + GENERATOR_NAME="MSYS Makefiles" +fi + +if [ "$OS_NAME" = "Cygwin" ]; then + CMAKE_EXTRA_ARGS="-DCMAKE_TOOLCHAIN_FILE=../../common/Toolchain-mingw32-cygwin.cmake" + export CC="i686-w64-mingw32-gcc" + export CXX="i686-w64-mingw32-g++" +fi \ No newline at end of file diff --git a/build.minheadless.cmake/x86/pharo.cog.spur/Makefile b/build.minheadless.cmake/x86/pharo.cog.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.cog.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x86/pharo.cog.spur/mvm b/build.minheadless.cmake/x86/pharo.cog.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.cog.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure b/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure_variant b/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure_variant new file mode 100755 index 0000000000..89d396f811 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.cog.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DBUILD_I386_VERSION=On -DPHARO_BRANDING=On -DSPUR_OBJECT_MODEL=On -DCOG_JIT=On diff --git a/build.minheadless.cmake/x86/pharo.stack.spur/Makefile b/build.minheadless.cmake/x86/pharo.stack.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.stack.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x86/pharo.stack.spur/mvm b/build.minheadless.cmake/x86/pharo.stack.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.stack.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure b/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure_variant b/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure_variant new file mode 100755 index 0000000000..1b94af3fb5 --- /dev/null +++ b/build.minheadless.cmake/x86/pharo.stack.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DBUILD_I386_VERSION=On -DPHARO_BRANDING=On -DSPUR_OBJECT_MODEL=On -DCOG_JIT=Off diff --git a/build.minheadless.cmake/x86/squeak.cog.spur/Makefile b/build.minheadless.cmake/x86/squeak.cog.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.cog.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x86/squeak.cog.spur/mvm b/build.minheadless.cmake/x86/squeak.cog.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.cog.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure b/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure_variant b/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure_variant new file mode 100755 index 0000000000..c942a8fffe --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.cog.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DBUILD_I386_VERSION=On -DPHARO_BRANDING=Off -DSPUR_OBJECT_MODEL=On -DCOG_JIT=On diff --git a/build.minheadless.cmake/x86/squeak.stack.spur/Makefile b/build.minheadless.cmake/x86/squeak.stack.spur/Makefile new file mode 100644 index 0000000000..b3ed147bb2 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.stack.spur/Makefile @@ -0,0 +1,18 @@ +all: + $(MAKE) -C debug all + $(MAKE) -C assert all + $(MAKE) -C relwithdebinfo all + $(MAKE) -C release all + +clean: + $(MAKE) -C debug clean + $(MAKE) -C assert clean + $(MAKE) -C relwithdebinfo clean + $(MAKE) -C release clean + +distclean: + rm -rf debug + rm -rf assert + rm -rf relwithdebinfo + rm -rf release + \ No newline at end of file diff --git a/build.minheadless.cmake/x86/squeak.stack.spur/mvm b/build.minheadless.cmake/x86/squeak.stack.spur/mvm new file mode 100755 index 0000000000..32331d0949 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.stack.spur/mvm @@ -0,0 +1,4 @@ +#!/bin/sh + +./mvm_configure +make \ No newline at end of file diff --git a/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure b/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure new file mode 100755 index 0000000000..3c7cb35833 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure @@ -0,0 +1,6 @@ +#!/bin/sh + +./mvm_configure_variant debug Debug +./mvm_configure_variant assert Assert +./mvm_configure_variant relwithdebinfo RelWithDebInfo +./mvm_configure_variant release Release diff --git a/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure_variant b/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure_variant new file mode 100755 index 0000000000..6b61c35d21 --- /dev/null +++ b/build.minheadless.cmake/x86/squeak.stack.spur/mvm_configure_variant @@ -0,0 +1,4 @@ +#!/bin/sh +. ../common/configure_variant.sh + +cmake ../../../.. -G "$GENERATOR_NAME" -DCMAKE_BUILD_TYPE="$VARIANT_NAME" $CMAKE_EXTRA_ARGS -DBUILD_I386_VERSION=On -DPHARO_BRANDING=Off -DSPUR_OBJECT_MODEL=On -DCOG_JIT=Off diff --git a/cmake/Mpeg3Plugin.cmake b/cmake/Mpeg3Plugin.cmake new file mode 100644 index 0000000000..ed7c371b70 --- /dev/null +++ b/cmake/Mpeg3Plugin.cmake @@ -0,0 +1,44 @@ +set(LIB_MPEG3_DIR "${CrossPlatformPluginFolder}/Mpeg3Plugin/libmpeg") +set(LIB_MPEG3_SOURCES + # Video + "${LIB_MPEG3_DIR}/video/getpicture.c" + "${LIB_MPEG3_DIR}/video/headers.c" + "${LIB_MPEG3_DIR}/video/idct.c" + "${LIB_MPEG3_DIR}/video/macroblocks.c" + "${LIB_MPEG3_DIR}/video/mmxtest.c" + "${LIB_MPEG3_DIR}/video/motion.c" + "${LIB_MPEG3_DIR}/video/mpeg3video.c" + "${LIB_MPEG3_DIR}/video/output.c" + "${LIB_MPEG3_DIR}/video/reconstruct.c" + "${LIB_MPEG3_DIR}/video/seek.c" + "${LIB_MPEG3_DIR}/video/slice.c" + "${LIB_MPEG3_DIR}/video//vlc.c" + + # Audio + "${LIB_MPEG3_DIR}/audio/dct.c" + "${LIB_MPEG3_DIR}/audio/header.c" + "${LIB_MPEG3_DIR}/audio/layer1.c" + "${LIB_MPEG3_DIR}/audio/layer2.c" + "${LIB_MPEG3_DIR}/audio/layer3.c" + "${LIB_MPEG3_DIR}/audio/mpeg3audio.c" + "${LIB_MPEG3_DIR}/audio/pcm.c" + "${LIB_MPEG3_DIR}/audio/synthesizers.c" + "${LIB_MPEG3_DIR}/audio/tables.c" + + # Common + "${LIB_MPEG3_DIR}/bitstream.c" + "${LIB_MPEG3_DIR}/changesForSqueak.c" + "${LIB_MPEG3_DIR}/libmpeg3.c" + "${LIB_MPEG3_DIR}/mpeg3atrack.c" + "${LIB_MPEG3_DIR}/mpeg3demux.c" + "${LIB_MPEG3_DIR}/mpeg3io.c" + "${LIB_MPEG3_DIR}/mpeg3title.c" + "${LIB_MPEG3_DIR}/mpeg3vtrack.c" +) + +include_directories( + "${CrossPlatformPluginFolder}/Mpeg3Plugin/libmpeg" + "${CrossPlatformPluginFolder}/Mpeg3Plugin/libmpeg/audio" + "${CrossPlatformPluginFolder}/Mpeg3Plugin/libmpeg/video" +) +add_vm_plugin_auto(Mpeg3Plugin INTERNAL "${LIB_MPEG3_SOURCES}") diff --git a/cmake/Plugins.cmake b/cmake/Plugins.cmake new file mode 100644 index 0000000000..5721e8ac6d --- /dev/null +++ b/cmake/Plugins.cmake @@ -0,0 +1,119 @@ +set(CrossPlatformPluginFolder platforms/Cross/plugins) +if (WIN32) + set(PlatformPluginFolder platforms/win32/plugins) +elseif(UNIX) + set(PlatformPluginFolder platforms/unix/plugins) +else() + set(PlatformPluginFolder) +endif() + +set(VM_INTERNAL_PLUGINS_INC_SOURCES "") + +macro(add_vm_plugin NAME TYPE) + set(VM_PLUGIN_${NAME}_SOURCES ${ARGN}) + set(VM_PLUGIN_${NAME}_TYPE ${TYPE}) + option(BUILD_PLUGIN_${NAME} "Build plugin ${NAME}" ON) + if(BUILD_PLUGIN_${NAME}) + if("${TYPE}" STREQUAL "INTERNAL") + set(VM_INTERNAL_PLUGINS_INC_SOURCES "${VM_INTERNAL_PLUGINS_INC_SOURCES}\nINTERNAL_PLUGIN(${NAME})") + set(VM_INTERNAL_PLUGIN_SOURCES ${VM_PLUGIN_${NAME}_SOURCES} ${VM_INTERNAL_PLUGIN_SOURCES}) + source_group("Internal Plugins\\${NAME}" FILES ${VM_PLUGIN_${NAME}_SOURCES}) + else() + if(NOT ONLY_CONFIG_H) + add_library(${NAME} SHARED ${ARGN}) + endif() + endif() + endif() +endmacro() + +macro(add_vm_plugin_sources NAME TYPE) + include_directories( + "${PluginsSourceFolderName}/${NAME}" + "${PlatformPluginFolder}/${NAME}" + "${CrossPlatformPluginFolder}/${NAME}" + ) + add_vm_plugin(${NAME} ${TYPE} ${ARGN}) +endmacro() + +macro(add_vm_plugin_auto NAME TYPE) + file(GLOB PLUGIN_SOURCES + "${PluginsSourceFolderName}/${NAME}/*.c" + "${PlatformPluginFolder}/${NAME}/*.c" + "${PlatformPluginFolder}/${NAME}/*.h" + "${CrossPlatformPluginFolder}/${NAME}/*.c" + "${CrossPlatformPluginFolder}/${NAME}/*.h" + ) + add_vm_plugin_sources(${NAME} ${TYPE} ${PLUGIN_SOURCES} ${ARGN}) +endmacro() + +macro(vm_plugin_link_libraries NAME) + if(VM_PLUGIN_${NAME}_TYPE STREQUAL "EXTERNAL") + if(NOT ONLY_CONFIG_H) + target_link_libraries(${NAME} ${ARGN}) + endif() + else() + set(VM_DEPENDENCIES_LIBRARIES ${ARGN} ${VM_DEPENDENCIES_LIBRARIES}) + endif() +endmacro() + +# The sources of the FFI plugin are special. +set(SqueakFFIPrims_Sources + "${CrossPlatformPluginFolder}/SqueakFFIPrims/sqFFI.h" + "${CrossPlatformPluginFolder}/SqueakFFIPrims/sqFFIPlugin.c" + "${CrossPlatformPluginFolder}/SqueakFFIPrims/sqFFITestFuncs.c" + "${CrossPlatformPluginFolder}/SqueakFFIPrims/sqManualSurface.c" + "${PluginsSourceFolderName}/SqueakFFIPrims/SqueakFFIPrims.c" +) + +add_vm_plugin_sources(SqueakFFIPrims INTERNAL ${SqueakFFIPrims_Sources}) + +# The sources of the IA32ABI plugin are special. +set(IA32ABI_Sources + "${CrossPlatformPluginFolder}/IA32ABI/AlienSUnitTestProcedures.c" + "${CrossPlatformPluginFolder}/IA32ABI/xabicc.c" + "${CrossPlatformPluginFolder}/IA32ABI/x64win64stub.c" + "${PluginsSourceFolderName}/IA32ABI/IA32ABI.c" +) + +add_vm_plugin_sources(IA32ABI INTERNAL ${IA32ABI_Sources}) + +# Basic internal plugins +add_vm_plugin_auto(FilePlugin INTERNAL) +add_vm_plugin_auto(LargeIntegers INTERNAL) +add_vm_plugin_auto(LocalePlugin INTERNAL) +add_vm_plugin_auto(MiscPrimitivePlugin INTERNAL) +add_vm_plugin_auto(SecurityPlugin INTERNAL) +add_vm_plugin_auto(SocketPlugin INTERNAL) +if(WIN32) + vm_plugin_link_libraries(SocketPlugin Ws2_32) +endif() + +add_vm_plugin_auto(B2DPlugin INTERNAL) +add_vm_plugin_sources(BitBltPlugin INTERNAL + "${PluginsSourceFolderName}/BitBltPlugin/BitBltPlugin.c" +) + +add_vm_plugin_auto(FloatArrayPlugin INTERNAL) +add_vm_plugin_auto(FloatMathPlugin INTERNAL) +add_vm_plugin_auto(Matrix2x3Plugin INTERNAL) + +# Basic external plugins +add_vm_plugin_auto(SurfacePlugin EXTERNAL) + +# Drop plugin +add_vm_plugin_sources(DropPlugin INTERNAL + "${PluginsSourceFolderName}/DropPlugin/DropPlugin.c" +) + +# Extra plugins +add_vm_plugin_auto(ZipPlugin INTERNAL) # Used by Monticello + +if(PHARO_VM) + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/PluginsPharo.cmake") +elseif(SQUEAK_VM) + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/PluginsSqueak.cmake") +endif() + +# Write the list of plugins. +file(WRITE ${CMAKE_BINARY_DIR}/sqInternalPlugins.inc +${VM_INTERNAL_PLUGINS_INC_SOURCES}) diff --git a/cmake/PluginsPharo.cmake b/cmake/PluginsPharo.cmake new file mode 100644 index 0000000000..312fda304b --- /dev/null +++ b/cmake/PluginsPharo.cmake @@ -0,0 +1,59 @@ +# Internal Pharo plugins +add_vm_plugin_auto(ADPCMCodecPlugin INTERNAL) +add_vm_plugin_auto(AsynchFilePlugin INTERNAL) +add_vm_plugin_auto(BMPReadWriterPlugin INTERNAL) +#add_vm_plugin_auto(CroquetPlugin INTERNAL) +add_vm_plugin_auto(DSAPrims INTERNAL) +add_vm_plugin_auto(FFTPlugin INTERNAL) +add_vm_plugin_auto(FileCopyPlugin INTERNAL) +add_vm_plugin_auto(JoystickTabletPlugin INTERNAL) +add_vm_plugin_auto(MIDIPlugin INTERNAL) +add_vm_plugin_auto(SerialPlugin INTERNAL) +add_vm_plugin_auto(SoundCodecPrims INTERNAL) +add_vm_plugin_auto(SoundGenerationPlugin INTERNAL) +#add_vm_plugin_auto(SoundPlugin INTERNAL) +add_vm_plugin_auto(StarSqueakPlugin INTERNAL) + +# External Pharo plugins +#add_vm_plugin_auto(B3DAcceleratorPlugin EXTERNAL) + +add_vm_plugin_auto(JPEGReaderPlugin EXTERNAL) +add_vm_plugin_auto(JPEGReadWriter2Plugin EXTERNAL) +add_vm_plugin_auto(RePlugin EXTERNAL) +add_vm_plugin_auto(InternetConfigPlugin EXTERNAL) +if (APPLE) + set(SqueakSSL_Sources + "platforms/iOS/plugins/SqueakSSL/sqMacSSL.c" + ) + add_vm_plugin_sources(SqueakSSL EXTERNAL ${SqueakSSL_Sources}) +else() + add_vm_plugin_auto(SqueakSSL EXTERNAL) +endif() +add_vm_plugin_auto(AioPlugin EXTERNAL) +add_vm_plugin_auto(EventsHandlerPlugin EXTERNAL) +if(HAVE_SDL2) + add_vm_plugin_auto(SDL2DisplayPlugin EXTERNAL) + vm_plugin_link_libraries(SDL2DisplayPlugin ${SDL2_LIBRARY}) +endif() + +# More complicated plugins +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/Mpeg3Plugin.cmake") + +# Free type plugin +find_package(Freetype) +if(FREETYPE_FOUND) + include_directories(${FREETYPE_INCLUDE_DIRS}) + add_vm_plugin_auto(FT2Plugin EXTERNAL) + vm_plugin_link_libraries(FT2Plugin ${FREETYPE_LIBRARIES}) +endif() + +# OSProcess +if(UNIX) + if(NOT APPLE) + add_vm_plugin_auto(VMProfileLinuxSupportPlugin INTERNAL) + endif() + add_vm_plugin_auto(UnixOSProcessPlugin INTERNAL) +endif() +if(WIN32) + add_vm_plugin_auto(Win32OSProcessPlugin INTERNAL) +endif() diff --git a/include/OpenSmalltalkVM.h b/include/OpenSmalltalkVM.h new file mode 100644 index 0000000000..3b6c638641 --- /dev/null +++ b/include/OpenSmalltalkVM.h @@ -0,0 +1,148 @@ +/* SqueakVirtualMachine.h -- platform-specific modifications to sq.h + * + * Copyright (C) 2016 by Ronie Salgado and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + * + */ + +/** + * Squeak virtual machine public embedding interface. + */ + +#ifndef OPEN_SMALLTALK_VM_H +#define OPEN_SMALLTALK_VM_H + +#include + +#ifdef _WIN32 +# ifdef BUILD_OSVM_STATIC +# define OSVM_VM_EXPORT +# define OSVM_VM_IMPORT +# else +# define OSVM_VM_EXPORT __declspec(dllexport) +# define OSVM_VM_IMPORT __declspec(dllimport) +# endif +#else +# define OSVM_VM_EXPORT +# define OSVM_VM_IMPORT +#endif + +#ifdef BUILD_VM_CORE +# define OSVM_VM_CORE_PUBLIC OSVM_VM_EXPORT +#else +# define OSVM_VM_CORE_PUBLIC OSVM_VM_IMPORT +#endif + +#define OSVM_VM_CORE_VERSION(major, minor, revision) ((major)*1000 + (minor)*10 + (revision)) +#define OSVM_VM_CORE_COMPILED_VERSION OSVM_VM_CORE_VERSION(0, 1, 0) + +typedef intptr_t osvmInt; +typedef uintptr_t osvmUInt; + +/**/ +typedef enum +{ + OSVM_SUCCESS = 0, + OSVM_ERROR, + OSVM_ERROR_INVALID_PARAMETER, + OSVM_ERROR_NOT_YET_IMPLEMENTED, + OSVM_ERROR_OUT_OF_BOUNDS, + OSVM_ERROR_OUT_OF_MEMORY, + OSVM_ERROR_UNSUPPORTED_OPERATION, + OSVM_ERROR_UNSUPPORTED_PARAMETER, + OSVM_ERROR_FAILED_TO_OPEN_FILE, + OSVM_ERROR_FAILED_TO_LOAD_IMAGE, +} OSVMError; + +/** + * Get the virtual machine embedding interface version + */ +OSVM_VM_CORE_PUBLIC int osvm_getInterfaceVersion(void); + +/** + * Simple all mighty main entry point for running a Squeak VM. + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_main(int argc, const char **argv); + +/** + * Global initialization of Squeak + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_initialize(void); + +/** + * Global shutting down of Squeak VM + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_shutdown(void); + +/** + * Initialize the Squeak VM + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_initializeVM(void); + +/** + * Shutdown the Squeak VM + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_shutdownVM(void); + +/** + * Parse command line arguments + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_parseCommandLineArguments(int argc, const char **argv); + +/** + * Parse the VM command line arguments + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_parseVMCommandLineArguments(int argc, const char **argv); + +/** + * Set a string VM parameter + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_setVMStringParameter(const char *name, const char *value); + +/** + * Set an integer VM parameter + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_setVMIntegerParameter(const char *name, const char *value); + +/** + * Pass the image command line arguments + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_passImageCommandLineArguments(int argc, const char **argv); + +/** + * Load the image + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_loadImage(const char *fileName); + +/** + * Load the default image or the one provided on the command line + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_loadDefaultImage(void); + +/** + * Run indefinetely the previously loaded image. + */ +OSVM_VM_CORE_PUBLIC OSVMError osvm_run(void); + +#endif /* OPEN_SMALLTALK_VM_H */ diff --git a/platforms/Cross/vm/sq.h b/platforms/Cross/vm/sq.h index 1b96eb004e..c078a97e29 100644 --- a/platforms/Cross/vm/sq.h +++ b/platforms/Cross/vm/sq.h @@ -56,6 +56,7 @@ */ #define EXPORT(returnType) returnType #define VM_EXPORT +#define VM_FUNCTION_EXPORT(returnType) returnType /* Image save/restore macros. */ @@ -69,6 +70,7 @@ #define sqImageFilePosition(f) ftell(f) #define sqImageFileRead(ptr, sz, count, f) fread(ptr, sz, count, f) #define sqImageFileSeek(f, pos) fseek(f, pos, SEEK_SET) +#define sqImageFileSeekEnd(f, pos) fseek(f, pos, SEEK_END) #define sqImageFileWrite(ptr, sz, count, f) fwrite(ptr, sz, count, f) #define sqImageFileStartLocation(fileRef, fileName, size) 0 @@ -212,9 +214,13 @@ sqInt sqGetFilenameFromString(char * aCharBuffer, char * aFilenameString, sqInt /* Interpreter entry points. */ /* Disable Intel compiler inlining of error which is used for breakpoints */ -#pragma auto_inline(off) -void error(char *s); -#pragma auto_inline(on) +#ifdef __INTEL_COMPILER +# pragma auto_inline(off) +#endif +extern void error(char *s); +#ifdef __INTEL_COMPILER +# pragma auto_inline(on) +#endif sqInt checkedByteAt(sqInt byteAddress); sqInt checkedByteAtput(sqInt byteAddress, sqInt byte); sqInt checkedLongAt(sqInt byteAddress); diff --git a/platforms/Cross/vm/sqAssert.h b/platforms/Cross/vm/sqAssert.h index 1d7908f201..4ffb4a7db8 100644 --- a/platforms/Cross/vm/sqAssert.h +++ b/platforms/Cross/vm/sqAssert.h @@ -19,6 +19,12 @@ extern void warningat(char *,int); void error(char *s); #pragma auto_inline(on) +extern void sqError(char *errorMessage); + +#ifndef error +#define error sqError +#endif + #undef assert # define __stringify(foo) #foo # define __stringifyNum(n) __stringify(n) @@ -32,6 +38,42 @@ void error(char *s); # define eassert(expr) 0 /* hack disabling of asserts. Better in makefile? */ # define PRODUCTION 1 #elif 1 +#if defined(_MSC_VER) +static inline int warningIf(int condition, char *message) +{ + if (condition) + { + warning(message); + return 0; + } + else + { + return 1; + } +} + +static inline int warningIfAt(int condition, char *message, int line) +{ + if (condition) + { + warningat(message, line); + return 0; + } + else + { + return 1; + } +} + +# define assert(expr) warningIf(!(expr), #expr " " __stringifyNum(__LINE__)) +# define asserta(expr) warningIf(!(expr), #expr " " __stringifyNum(__LINE__)) +# define assertf(msg) (warning(#msg " " __stringifyNum(__LINE__)),0) +# define assertl(expr,line) warningIfAt(!(expr), #expr, line) +# define assertal(expr,line) warningIfAt(!(expr), #expr, line) +# define assertfl(msg,line) (warningat(#msg,line),0) +extern char expensiveAsserts; +# define eassert(expr) warningIf(!expensiveAsserts && !(expr), #expr " " __stringifyNum(__LINE__)) +#else # define assert(expr) ((expr)||(warning(#expr " " __stringifyNum(__LINE__)),0)) # define asserta(expr) ((expr)||(warning(#expr " " __stringifyNum(__LINE__)),0)) # define assertf(msg) (warning(#msg " " __stringifyNum(__LINE__)),0) @@ -41,5 +83,6 @@ void error(char *s); extern char expensiveAsserts; # define eassert(expr) (!expensiveAsserts||(expr) \ ||(warning(#expr " " __stringifyNum(__LINE__)),0)) +#endif # define PRODUCTION 0 #endif diff --git a/platforms/Cross/vm/sqCircularQueue.h b/platforms/Cross/vm/sqCircularQueue.h new file mode 100644 index 0000000000..8fa5b1d2ff --- /dev/null +++ b/platforms/Cross/vm/sqCircularQueue.h @@ -0,0 +1,62 @@ +/* sqCircularQueue.c -- Generic circular queue data structure. + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef _SQ_CIRCULAR_QUEUE_H_ +#define _SQ_CIRCULAR_QUEUE_H_ + +/** + * Generic circular queue data structure + */ +typedef struct sqCircularQueueInfo +{ + int readIndex; + int writeIndex; +}sqCircularQueueInfo; + +#define sqQueueIsEmpty(queue, queueSize) ((queue).info.readIndex == (queue).info.writeIndex) +#define sqQueueIsFull(queue, queueSize) ( (((queue).info.writeIndex + 1) & (queueSize - 1)) == (queue).info.readIndex) + +#define sqQueueIncreaseWriteIndex(queue, queueSize) ((queue).info.writeIndex = (queue).info.writeIndex + 1 & (queueSize - 1)) +#define sqQueueIncreaseReadIndex(queue, queueSize) ((queue).info.readIndex = (queue).info.readIndex + 1 & (queueSize - 1)) + +#define sqQueuePush(queue, queueSize, value) { \ + (queue).elements[(queue).info.writeIndex] = value; \ + sqQueueIncreaseWriteIndex(queue, queueSize); \ + if(sqQueueIsEmpty(queue, queueSize)) {\ + sqQueueIncreaseReadIndex(queue, queueSize); \ + } \ +} + +#define sqQueuePopInto(queue, queueSize, result) { \ + if(!sqQueueIsEmpty(queue, queueSize)) {\ + *(result) = (queue).elements[(queue).info.readIndex]; \ + sqQueueIncreaseReadIndex(queue, queueSize); \ + } \ +} + +#endif /*_SQ_CIRCULAR_QUEUE_H_ */ diff --git a/platforms/Cross/vm/sqPath.c b/platforms/Cross/vm/sqPath.c new file mode 100644 index 0000000000..adac1324d4 --- /dev/null +++ b/platforms/Cross/vm/sqPath.c @@ -0,0 +1,135 @@ +/* sqPath.c -- Path manipulation functions + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#elif defined(__unix__) || defined(__MACH__) || defined(__APPLE__) +#include +#else +#include +#define getcwd(target, targetSize) strcpy(target, ".") +#endif + +#include "sqPath.h" +#include "sqTextEncoding.h" + +void sqGetCurrentWorkingDir(char *target, size_t targetSize) +{ +#ifdef _WIN32 + unsigned short *tempBuffer = (unsigned short*)calloc(MAX_PATH + 1, sizeof(unsigned short)); + GetCurrentDirectoryW(MAX_PATH + 1, tempBuffer); + sqUTF16ToUTF8Copy(target, targetSize, tempBuffer); + free(tempBuffer); +#else + getcwd(target, targetSize); +#endif +} + +int sqIsAbsolutePath(const char *path) +{ +#ifdef _WIN32 + return path && (path[0] != 0 && path[1] == ':'); +#else + /* Assume UNIX style path. */ + return path && (*path == '/'); +#endif +} + +void sqPathMakeAbsolute(char *target, size_t targetSize, const char *src) +{ + if (sqIsAbsolutePath(src)) + { + strcpy(target, src); + } + else + { + sqGetCurrentWorkingDir(target, targetSize); + /* TODO: Make a more secure copy than strcat*/ +#ifdef _WIN32 + strcat(target, "\\"); + if (src[0] == '.' && (src[1] == '/' || src[1] == '\\')) + strcat(target, src + 2); + else + strcat(target, src); +#else + strcat(target, "/"); + if(src[0] == '.' && src[1] == '/') + strcat(target, src + 2); + else + strcat(target, src); +#endif + } +} + +void sqPathExtractDirname(char *target, size_t targetSize, const char *src) +{ + size_t copySize; + const char *lastSeparator = strrchr(src, '/'); +#ifdef _WIN32 + const char *lastSeparator2 = strrchr(src, '\\'); + if (!lastSeparator || lastSeparator < lastSeparator2) + lastSeparator = lastSeparator2; +#endif + copySize = targetSize; + if (lastSeparator && lastSeparator - src + 1 < copySize) + copySize = lastSeparator - src + 1; + strncpy(target, src, copySize); +} + +void sqPathExtractBaseName(char *target, size_t targetSize, const char *src) +{ + const char *lastSeparator = strrchr(src, '/'); +#ifdef _WIN32 + const char *lastSeparator2 = strrchr(src, '\\'); + if (!lastSeparator || lastSeparator < lastSeparator2) + lastSeparator = lastSeparator2; +#endif + + if (lastSeparator) + strncpy(target, lastSeparator, targetSize); + else + strcpy(target, ""); +} + +#ifdef _WIN32 +# define SEPARATOR_STRING "\\" +# define SEPARATOR_CHAR '\\' +#else +# define SEPARATOR_STRING "/" +# define SEPARATOR_CHAR '/' +#endif +void sqPathJoin(char *target, size_t targetSize, const char *first, const char *second) +{ + strcpy(target, first); + if(first[strlen(first)-1] != SEPARATOR_CHAR) { + strcat(target, SEPARATOR_STRING); + } + strcat(target, second); + strcat(target, SEPARATOR_STRING); +} diff --git a/platforms/Cross/vm/sqPath.h b/platforms/Cross/vm/sqPath.h new file mode 100644 index 0000000000..5fd84f650e --- /dev/null +++ b/platforms/Cross/vm/sqPath.h @@ -0,0 +1,42 @@ +/* sqPath.h -- Path manipulation functions + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef SQ_PATH_H +#define SQ_PATH_H + +#include + +void sqGetCurrentWorkingDir(char *target, size_t targetSize); + +void sqPathMakeAbsolute(char *target, size_t targetSize, const char *src); +void sqPathExtractDirname(char *target, size_t targetSize, const char *src); +void sqPathExtractBaseName(char *target, size_t targetSize, const char *src); +void sqPathJoin(char *target, size_t targetSize, const char *first, const char *second); +int sqIsAbsolutePath(const char *path); + +#endif /* SQ_PATH_H */ diff --git a/platforms/Cross/vm/sqTextEncoding.c b/platforms/Cross/vm/sqTextEncoding.c new file mode 100644 index 0000000000..64e03d50c0 --- /dev/null +++ b/platforms/Cross/vm/sqTextEncoding.c @@ -0,0 +1,191 @@ +/* sqTextEncoding.c -- UTF8, UTF16 and UTF32 text encoding conversion functions + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#include "sq.h" +#include "sqTextEncoding.h" + +const char *sqUTF8ToUTF32Iterate(const char *string, int *dest) +{ + unsigned int first; + unsigned int sequenceSize; + unsigned int i; + unsigned int byte; + *dest = 0; + + first = (*string) & 0xFF; + if(first == 0) + return string; + + /* Single byte case */ + ++string; + if(first <= 127) + { + *dest = first; + return string; + } + + /* Count the size of the character */ + sequenceSize = 0; + while(first & 0x80) + { + first = (first << 1) & 0xFF; + ++sequenceSize; + } + + first >>= sequenceSize; + + /* Decode the full code point. */ + *dest = first; + --sequenceSize; + + for(i = 0; i < sequenceSize; ++i) + { + /* Fetch the next byte */ + byte = *string; + if(!byte) + return string; + ++string; + + /* Append the byte data */ + *dest = (*dest << 6) | (byte & 63); + } + + return string; +} + +extern const unsigned short *sqUTF16ToUTF32Iterate(const unsigned short *string, int *dest) +{ + unsigned int high; + unsigned int low; + *dest = 0; + if(!*string) + return string; + + while((high = *string) != 0) + { + ++string; + if(high < 0xD800 || 0xE000 <= high) + { + /* Single byte*/ + *dest = high; + break; + } + else if(high < 0xDC00) + { + /* Low surrogate*/ + low = *string; + if(!low) + break; + + /* Validate the low surrogate */ + ++string; + if(0xDC00 <= low && low < 0xE000) + { + *dest = (high - 0xD800) << 10 | (low - 0xDC00); + } + + /* Invalid character. Continue*/ + } + else + { + /* Invalid low surrogate without preceding high surrogate. Ignore it*/ + } + } + + return string; +} + +extern unsigned short *sqUTF8ToUTF16Copy(unsigned short *dest, size_t destSize, const char *src) +{ + unsigned int codePoint; + const char *pos; + unsigned short *originalDestination = dest; + + if(!src) + return 0; + + /* Iterate through the code points present in the UTF-8 string */ + pos = src; + while(*pos) + { + pos = sqUTF8ToUTF32Iterate(pos, &codePoint); + if(destSize <= 1 || !codePoint) + break; + + /* printf("CodePoint: %d\n", codePoint); */ + if(codePoint >= 0x10000) + { + /* Surrogate high - Surrogate low - Null terminator*/ + if(destSize < 3) + break; + + /* Surrogate high */ + *dest = (codePoint >> 10) + 0xD800; + ++dest; --destSize; + + /* Surrogate low */ + *dest = (codePoint & 1023) + 0xDC00; + ++dest; --destSize; + } + else + { + /* Just pass the code point. */ + *dest = codePoint; + ++dest; --destSize; + } + } + *dest = 0; + + return originalDestination; +} + +extern char *sqUTF16ToUTF8Copy(char *dest, size_t destSize, const unsigned short *src) +{ + char *originalDestination = dest; + + /* TODO: Implement this properly. */ + while (*src && destSize > 1) + { + *dest++ = *src++; + --destSize; + } + + *dest = 0; + return originalDestination; +} + +extern unsigned short *sqUTF8toUTF16New(const char *string) +{ + unsigned short *result; + size_t stringLength; + + stringLength = strlen(string); + result = (unsigned short*)calloc(stringLength + 1, sizeof(unsigned short)); + sqUTF8ToUTF16Copy(result, stringLength + 1, string); + return result; +} diff --git a/platforms/Cross/vm/sqTextEncoding.h b/platforms/Cross/vm/sqTextEncoding.h new file mode 100644 index 0000000000..8038c3031b --- /dev/null +++ b/platforms/Cross/vm/sqTextEncoding.h @@ -0,0 +1,39 @@ +/* sqTextEncoding.c -- UTF8, UTF16 and UTF32 text encoding conversion functions + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef SQ_TEXT_ENCODING_H +#define SQ_TEXT_ENCODING_H + +/* Text encoding conversions. */ +extern const char *sqUTF8ToUTF32Iterate(const char *string, int *dest); +extern const unsigned short *sqUTF16ToUTF32Iterate(const unsigned short *string, int *dest); +extern unsigned short *sqUTF8ToUTF16Copy(unsigned short *dest, size_t destSize, const char *src); +extern char *sqUTF16ToUTF8Copy(char *dest, size_t destSize, const unsigned short *src); +extern unsigned short *sqUTF8toUTF16New(const char *string); + +#endif /* SQ_TEXT_ENCODING_H */ diff --git a/platforms/iOS/plugins/FilePlugin/Makefile b/platforms/iOS/plugins/FilePlugin/Makefile new file mode 100644 index 0000000000..401083de6c --- /dev/null +++ b/platforms/iOS/plugins/FilePlugin/Makefile @@ -0,0 +1,3 @@ +INCDIRS:=../../platforms/minheadless/unix + +include ../common/Makefile.plugin diff --git a/platforms/iOS/plugins/FilePlugin/sqUnixFile.c b/platforms/iOS/plugins/FilePlugin/sqUnixFile.c new file mode 100644 index 0000000000..288a2053ca --- /dev/null +++ b/platforms/iOS/plugins/FilePlugin/sqUnixFile.c @@ -0,0 +1,361 @@ +/* sqUnixFile.c -- directory operations for Unix + * + * Copyright (C) 1996-2004 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* Author: Ian.Piumarta@INRIA.Fr + */ + +#include "sq.h" +#include "FilePlugin.h" +#include "sqUnixCharConv.h" + +#ifdef HAVE_DIRENT_H +# include +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include +# endif +# ifdef HAVE_SYS_DIR_H +# include +# endif +# ifdef HAVE_NDIR_H +# include +# endif +#endif + +#ifdef HAVE_UNISTD_H +# include +# include +#endif + +#include +#include +#include +#include +#include + +/*** + The interface to the directory primitive is path based. + That is, the client supplies a Squeak string describing + the path to the directory on every call. To avoid traversing + this path on every call, a cache is maintained of the last + path seen, along with the Mac volume and folder reference + numbers corresponding to that path. +***/ + +/*** Constants ***/ +#define ENTRY_FOUND 0 +#define NO_MORE_ENTRIES 1 +#define BAD_PATH 2 + +#define DELIMITER '/' + +/*** Variables ***/ +char lastPath[MAXPATHLEN+1]; +int lastPathValid = false; +int lastIndex= -1; +DIR *openDir= 0; + + +/*** Functions ***/ + +extern time_t convertToSqueakTime(time_t unixTime); + + +sqInt dir_Create(char *pathString, sqInt pathStringLength) +{ + /* Create a new directory with the given path. By default, this + directory is created relative to the cwd. */ + char name[MAXPATHLEN+1]; + int i; + if (pathStringLength >= MAXPATHLEN) + return false; + if (!sq2uxPath(pathString, pathStringLength, name, MAXPATHLEN, 1)) + return false; + return mkdir(name, 0777) == 0; /* rwxrwxrwx & ~umask */ +} + + +sqInt dir_Delete(char *pathString, sqInt pathStringLength) +{ + /* Delete the existing directory with the given path. */ + char name[MAXPATHLEN+1]; + int i; + if (pathStringLength >= MAXPATHLEN) + return false; + if (!sq2uxPath(pathString, pathStringLength, name, MAXPATHLEN, 1)) + return false; + if (lastPathValid && !strcmp(lastPath, name)) + { + closedir(openDir); + lastPathValid= false; + lastIndex= -1; + lastPath[0]= '\0'; + } + return rmdir(name) == 0; +} + + +sqInt dir_Delimitor(void) +{ + return DELIMITER; +} + + +static int maybeOpenDir(char *unixPath) +{ + /* if the last opendir was to the same directory, re-use the directory + pointer from last time. Otherwise close the previous directory, + open the new one, and save its name. Return true if the operation + was successful, false if not. */ + if (!lastPathValid || strcmp(lastPath, unixPath)) + { + /* invalidate the old, open the new */ + if (lastPathValid) + closedir(openDir); + lastPathValid= false; + strncpy(lastPath, unixPath, MAXPATHLEN); + if ((openDir= opendir(unixPath)) == 0) + return false; + lastPathValid= true; + lastIndex= 0; /* first entry is index 1 */ + } + return true; +} + +#if PharoVM +sqInt dir_Lookup(char *pathString, sqInt pathStringLength, sqInt index, +/* outputs: */ char *name, sqInt *nameLength, sqInt *creationDate, sqInt *modificationDate, + sqInt *isDirectory, squeakFileOffsetType *sizeIfFile, sqInt * posixPermissions, sqInt *isSymlink) +#else +sqInt dir_Lookup(char *pathString, sqInt pathStringLength, sqInt index, +/* outputs: */ char *name, sqInt *nameLength, sqInt *creationDate, sqInt *modificationDate, + sqInt *isDirectory, squeakFileOffsetType *sizeIfFile) +#endif +{ + /* Lookup the index-th entry of the directory with the given path, starting + at the root of the file system. Set the name, name length, creation date, + creation time, directory flag, and file size (if the entry is a file). + Return: 0 if a entry is found at the given index + 1 if the directory has fewer than index entries + 2 if the given path has bad syntax or does not reach a directory + */ + + int i; + int nameLen= 0; + struct dirent *dirEntry= 0; + char unixPath[MAXPATHLEN+1]; + struct stat statBuf; + + /* default return values */ + *name = 0; + *nameLength = 0; + *creationDate = 0; + *modificationDate = 0; + *isDirectory = false; + *sizeIfFile = 0; +#if PharoVM + *posixPermissions = 0; + *isSymlink = false; +#endif + + if ((pathStringLength == 0)) + strcpy(unixPath, "."); + else if (!sq2uxPath(pathString, pathStringLength, unixPath, MAXPATHLEN, 1)) + return BAD_PATH; + + /* get file or directory info */ + if (!maybeOpenDir(unixPath)) + return BAD_PATH; + + if (++lastIndex == index) + index= 1; /* fake that the dir is rewound and we want the first entry */ + else + { + rewinddir(openDir); /* really rewind it, and read to the index */ + lastIndex= index; + } + + for (i= 0; i < index; i++) + { + nextEntry: + do + { + errno= 0; + dirEntry= readdir(openDir); + } + while ((dirEntry == 0) && (errno == EINTR)); + + if (!dirEntry) + return NO_MORE_ENTRIES; + + nameLen= NAMLEN(dirEntry); + + /* ignore '.' and '..' (these are not *guaranteed* to be first) */ + if (nameLen < 3 && dirEntry->d_name[0] == '.') + if (nameLen == 1 || dirEntry->d_name[1] == '.') + goto nextEntry; + } + + *nameLength= ux2sqPath(dirEntry->d_name, nameLen, name, MAXPATHLEN, 0); + + { + char terminatedName[MAXPATHLEN+1]; + if(nameLen > MAXPATHLEN) + return BAD_PATH; + strncpy(terminatedName, dirEntry->d_name, nameLen); + terminatedName[nameLen]= '\0'; + if(strlen(unixPath) + 1 + nameLen > MAXPATHLEN) + return BAD_PATH; + strcat(unixPath, "/"); + strcat(unixPath, terminatedName); + if (stat(unixPath, &statBuf) && lstat(unixPath, &statBuf)) + { + /* We can't stat the entry, but failing here would invalidate + the whole directory --bertf */ + return ENTRY_FOUND; + } + } + + /* last change time */ + *creationDate= convertToSqueakTime(statBuf.st_ctime); + /* modification time */ + *modificationDate= convertToSqueakTime(statBuf.st_mtime); + + if (S_ISDIR(statBuf.st_mode)) + *isDirectory= true; + else + *sizeIfFile= statBuf.st_size; + +#if PharoVM + *isSymlink = S_ISLNK(statBuf.st_mode); + *posixPermissions = statBuf.st_mode & 0777; +#endif + + return ENTRY_FOUND; +} + + +#if PharoVM +sqInt dir_EntryLookup(char *pathString, sqInt pathStringLength, char* nameString, sqInt nameStringLength, +/* outputs: */ char *name, sqInt *nameLength, sqInt *creationDate, sqInt *modificationDate, + sqInt *isDirectory, squeakFileOffsetType *sizeIfFile, sqInt *posixPermissions, sqInt *isSymlink) +#else +sqInt dir_EntryLookup(char *pathString, sqInt pathStringLength, char* nameString, sqInt nameStringLength, +/* outputs: */ char *name, sqInt *nameLength, sqInt *creationDate, sqInt *modificationDate, + sqInt *isDirectory, squeakFileOffsetType *sizeIfFile) +#endif +{ + /* Lookup the given name in the given directory, + Set the name, name length, creation date, + creation time, directory flag, and file size (if the entry is a file). + Return: 0 if a entry is found at the given index + 1 if there is no such entry in the directory + 2 if the given path has bad syntax or does not reach a directory + */ + + char unixPath[MAXPATHLEN+1]; + struct stat statBuf; + + /* default return values */ + *name = 0; + *nameLength = 0; + *creationDate = 0; + *modificationDate = 0; + *isDirectory = false; + *sizeIfFile = 0; +#if PharoVM + *posixPermissions = 0; + *isSymlink = false; +#endif + + if ((pathStringLength == 0)) + strcpy(unixPath, "."); + else if (!sq2uxPath(pathString, pathStringLength, unixPath, MAXPATHLEN, 1)) + return BAD_PATH; + + char terminatedName[MAXPATHLEN+1]; + if(nameStringLength > MAXPATHLEN) + return BAD_PATH; + strncpy(terminatedName, nameString, nameStringLength); + terminatedName[nameStringLength]= '\0'; + if(strlen(unixPath) + 1 + nameStringLength > MAXPATHLEN) + return BAD_PATH; + strcat(unixPath, "/"); + strcat(unixPath, terminatedName); + if (stat(unixPath, &statBuf) && lstat(unixPath, &statBuf)) { + return NO_MORE_ENTRIES; + } + + /* To match the results of dir_Lookup, copy back the file name */ + *nameLength = ux2sqPath(nameString, nameStringLength, name, 256, 0); + + /* last change time */ + *creationDate= convertToSqueakTime(statBuf.st_ctime); + /* modification time */ + *modificationDate= convertToSqueakTime(statBuf.st_mtime); + + if (S_ISDIR(statBuf.st_mode)) + *isDirectory= true; + else + *sizeIfFile= statBuf.st_size; + +#if PharoVM + *isSymlink = S_ISLNK(statBuf.st_mode); + *posixPermissions = statBuf.st_mode & 0777; +#endif + + return ENTRY_FOUND; +} + +/* unix files are untyped, and the creator is correct by default */ + + +sqInt dir_SetMacFileTypeAndCreator(char *filename, sqInt filenameSize, char *fType, char *fCreator) +{ + return true; +} + +sqInt dir_GetMacFileTypeAndCreator(char *filename, sqInt filenameSize, char *fType, char *fCreator) +{ + return true; +} + + +/* + * The following is useful in a debugging context when the VM's output has been + * directed to a log file. It binds stdout to /dev/tty, arranging that output + * of debugging print routines such as printOop appear on stdout. + */ +void +sqStdoutToDevTTY() +{ + if (!freopen("/dev/tty","w",stdout)) + perror("sqStdoutToDevTTY freopen(\"/dev/tty\",\"w\",stdout):"); +} diff --git a/platforms/iOS/plugins/SecurityPlugin/sqMacSecurity.c b/platforms/iOS/plugins/SecurityPlugin/sqMacSecurity.c deleted file mode 100644 index ca2aca5101..0000000000 --- a/platforms/iOS/plugins/SecurityPlugin/sqMacSecurity.c +++ /dev/null @@ -1,272 +0,0 @@ -// John M McIntosh on 5/15/08. -// Copyright Corporate Smalltalk Consulting Ltd 2000-2008. All rights reserved. -/* - Copyright (c) 2008 Corporate Smalltalk Consulting Ltd. All rights reserved. - MIT License - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -The end-user documentation included with the redistribution, if any, must include the following acknowledgment: -"This product includes software developed by Corporate Smalltalk Consulting Ltd (http://www.smalltalkconsulting.com) -and its contributors", in the same place and form as other third-party acknowledgments. -Alternately, this acknowledgment may appear in the software itself, in the same form and location as other -such third-party acknowledgments. -*/ -// - -//JMM 2/13/01 create docs folder if non-existant -//JMM 4/4/01 look for documents/My Squeak folder versus just documents as secure location -//JMM 5/3/01 path was wrong for unsecure folder which uncovered a bug in lookupPath -//JMM 8/15/01 only allow call to ioInitSecurity Once, also return proper return code -//JMM 9/5/01 make it as a plugin -// 3.7.0bx Nov 24th, 2003 JMM gCurrentVMEncoding -//May 16th, 2008. IPhone - -#include "sq.h" -#include "SecurityPlugin.h" -#include "FilePlugin.h" -#include -typedef unsigned char Boolean; -#define DELIMITERInt '/' - -extern struct VirtualMachine * interpreterProxy; - -#define fromSqueak(string,length) string -void fixPath(char *path); -sqInt dir_CreateSecurity(char *pathString, sqInt pathStringLength); -sqInt _ioSetFileAccess(sqInt enable); - -static char secureUserDirectory[PATH_MAX]; -static char untrustedUserDirectory[PATH_MAX]; -static Boolean gInitialized = false; - -/***************************************************************************/ -/***************************************************************************/ -/***************************************************************************/ -/***************************************************************************/ -/* environment security */ -static int allowEnvironmentAccess = 1; /* full access to C environment */ - -sqInt ioDisableEnvironmentAccess(void) { return allowEnvironmentAccess = 0; } -sqInt ioHasEnvironmentAccess(void) { return allowEnvironmentAccess; } - -/***************************************************************************/ -/***************************************************************************/ -/***************************************************************************/ -/***************************************************************************/ -/* file security */ -static sqInt allowFileAccess = 1; /* full access to files */ - -static sqInt isAccessiblePathName(char *pathName) { - sqInt i; - /* Check if the path/file name is subdirectory of the image path */ - for(i=0; i0;i--) - if(path[i-1]==DELIMITERInt) { - path[i-1]=0x00; - return; - } -} - -sqInt dir_CreateSecurity(char *pathString, sqInt pathStringLength) { - /* Create a new directory with the given path. By default, this - directory is created in the current directory. Use - a full path name such as "MyDisk:Working:New Folder" to - create folders elsewhere. */ - - //JMM tests create file in Vm directory, other place, other volume - return (sqInt) dir_Create(pathString, pathStringLength); -} diff --git a/platforms/iOS/plugins/SecurityPlugin/sqUnixSecurity.c b/platforms/iOS/plugins/SecurityPlugin/sqUnixSecurity.c new file mode 100644 index 0000000000..d067142df6 --- /dev/null +++ b/platforms/iOS/plugins/SecurityPlugin/sqUnixSecurity.c @@ -0,0 +1,208 @@ +/* sqUnixSecurity.c -- directory operations for Unix + * + * Author: Bert Freudenberg (heavily based on Andreas Raab's sqWin32Security.c) + * + * Last edited: 2005-03-19 20:47:40 by piumarta on squeak.hpl.hp.com + * + * Note: According to Ian Piumarta, the Unix VM is inherently insecure since + * pluggable primitives can access all of libc! It would need + * some linker magic to hide these from dlsym(). + * + * A workaround would be to disallow lookups via dlsym() when + * fileaccess is disallowed - internal plugins should still work ... + */ + +#include "sq.h" +#include "SecurityPlugin.h" + +#include + +static char secureUserDirectory[MAXPATHLEN]; /* imagepath/secure/ */ +static char untrustedUserDirectory[MAXPATHLEN]; /* imagepath/untrusted/ */ +static int untrustedUserDirectoryLen; + +static char* fromSqueak(char* string, int len) +{ + static char buf[MAXPATHLEN]; + strncpy(buf, string, len); + buf[len]= '\0'; + return buf; +} + +/* environment security *******************************************************/ +static int allowEnvironmentAccess = 1; /* full access to C environment */ + +sqInt ioDisableEnvironmentAccess(void) { return allowEnvironmentAccess = 0; } +sqInt ioHasEnvironmentAccess(void) { return allowEnvironmentAccess; } + +/* file security ***********************************************************/ +static sqInt allowFileAccess= 1; /* full access to files */ + + +static int isAccessiblePathName(char *pathName) +{ + char realPathName[MAXPATHLEN]; + int realPathLen; + + realpath(pathName, realPathName); + realPathLen= strlen(realPathName); + + return (realPathLen >= untrustedUserDirectoryLen + && 0 == strncmp(realPathName, untrustedUserDirectory, untrustedUserDirectoryLen)); +} + + +static int isAccessibleFileName(char *fileName) +{ + char pathName[MAXPATHLEN]; + int pathLen= strrchr(fileName, '/') - fileName; + + strncpy(pathName, fileName, pathLen); + pathName[pathLen]= '\0'; + + return isAccessiblePathName(pathName); +} + + +/* directory access */ + + +sqInt ioCanCreatePathOfSize(char* pathString, sqInt pathStringLength) +{ + if (allowFileAccess) return 1; + return isAccessiblePathName(fromSqueak(pathString, pathStringLength)); +} + + +sqInt ioCanListPathOfSize(char* pathString, sqInt pathStringLength) +{ + if (allowFileAccess) return 1; + return isAccessiblePathName(fromSqueak(pathString, pathStringLength)); +} + + +sqInt ioCanDeletePathOfSize(char* pathString, sqInt pathStringLength) +{ + if (allowFileAccess) return 1; + return isAccessiblePathName(fromSqueak(pathString, pathStringLength)); +} + + +/* file access */ + + +sqInt ioCanOpenFileOfSizeWritable(char* pathString, sqInt pathStringLength, sqInt writeFlag) +{ + if (allowFileAccess) return 1; + return isAccessibleFileName(fromSqueak(pathString, pathStringLength)); +} + + +sqInt ioCanOpenAsyncFileOfSizeWritable(char* pathString, sqInt pathStringLength, sqInt writeFlag) +{ + return ioCanOpenFileOfSizeWritable(pathString, pathStringLength, writeFlag); +} + + +sqInt ioCanDeleteFileOfSize(char* pathString, sqInt pathStringLength) +{ + if (allowFileAccess) return 1; + return isAccessibleFileName(fromSqueak(pathString, pathStringLength)); +} + +sqInt ioCanRenameFileOfSize(char* pathString, sqInt pathStringLength) +{ + if (allowFileAccess) return 1; + return isAccessibleFileName(fromSqueak(pathString, pathStringLength)); +} + + +sqInt ioCanGetFileTypeOfSize(char* pathString, sqInt pathStringLength) +{ + return 1; /* we don't have file types */ +} + + +sqInt ioCanSetFileTypeOfSize(char* pathString, sqInt pathStringLength) +{ + return 1; /* we don't have file types */ +} + + +/* disabling/querying */ + + +sqInt ioDisableFileAccess(void) { return allowFileAccess = 0; } +sqInt ioHasFileAccess(void) { return allowFileAccess; } + + +/* image security **********************************************************/ +static sqInt allowImageWrite= 1; /* allow writing the image */ + +sqInt ioCanRenameImage(void) +{ + return allowImageWrite; /* only when we're allowed to save the image */ +} + +sqInt ioCanWriteImage(void) { return allowImageWrite; } +sqInt ioDisableImageWrite(void) { return allowImageWrite= 0; } + + +/* socket security - for now it's all or nothing ***************************/ +static sqInt allowSocketAccess= 1; /* allow access to sockets */ + +sqInt ioCanCreateSocketOfType(sqInt netType, sqInt socketType) +{ + return allowSocketAccess; +} +sqInt ioCanConnectToPort(sqInt addr, sqInt port) { return allowSocketAccess; } +sqInt ioCanListenOnPort(sqInt s, sqInt port) { return allowSocketAccess; } +sqInt ioDisableSocketAccess() { return allowSocketAccess = 0; } +sqInt ioHasSocketAccess() { return allowSocketAccess; } + + +/* SecurityPlugin primitive support ****************************************/ + +char *ioGetSecureUserDirectory(void) +{ + if (secureUserDirectory[0] == '\0') + return (char *)success(false); + return secureUserDirectory; +} + + +char *ioGetUntrustedUserDirectory(void) +{ + return untrustedUserDirectory; +} + + +/* note: following is called from VM directly, not from plugin */ +sqInt ioInitSecurity(void) +{ + int imagePathLen= strrchr(imageName, '/') - imageName; + char *squeakUserDirectory= 0; + + /* establish the secure user directory */ + strncpy(secureUserDirectory, imageName, imagePathLen); + strcpy(secureUserDirectory + imagePathLen, "/secure"); + + /* establish untrusted user directory */ + squeakUserDirectory= getenv("SQUEAK_USERDIR"); + if (0 == squeakUserDirectory) + { + strncpy(untrustedUserDirectory, imageName, imagePathLen); + strcpy(untrustedUserDirectory + imagePathLen, "/My Squeak"); + } + else + { + int lastChar= strlen(squeakUserDirectory); + /* path is not allowed to end with "/" */ + if ('/' == squeakUserDirectory[lastChar - 1]) + squeakUserDirectory[lastChar - 1]= '\0'; + strcpy(untrustedUserDirectory, squeakUserDirectory); + } + untrustedUserDirectoryLen= strlen(untrustedUserDirectory); + + return 1; +} diff --git a/platforms/minheadless/common/English.lproj/Newspeak-Localizable.strings b/platforms/minheadless/common/English.lproj/Newspeak-Localizable.strings new file mode 100644 index 0000000000..d9122b04b9 --- /dev/null +++ b/platforms/minheadless/common/English.lproj/Newspeak-Localizable.strings @@ -0,0 +1,3 @@ +/* Localized versions of Info.plist keys */ + +"SelectImagePanePrompt" = "Select a Newspeak image file to open"; diff --git a/platforms/minheadless/common/English.lproj/Pharo-Localizable.strings b/platforms/minheadless/common/English.lproj/Pharo-Localizable.strings new file mode 100644 index 0000000000..682559bae5 --- /dev/null +++ b/platforms/minheadless/common/English.lproj/Pharo-Localizable.strings @@ -0,0 +1,3 @@ +/* Localized versions of Info.plist keys */ + +"SelectImagePanePrompt" = "Open..."; diff --git a/platforms/minheadless/common/English.lproj/Squeak-Localizable.strings b/platforms/minheadless/common/English.lproj/Squeak-Localizable.strings new file mode 100644 index 0000000000..c8bf25e2db --- /dev/null +++ b/platforms/minheadless/common/English.lproj/Squeak-Localizable.strings @@ -0,0 +1,9 @@ +/* Localized versions of Info.plist keys + Localizable.strings + SqueakPureObjc + + Created by John M McIntosh on 09-11-24. + Copyright 2009 Corporate Smalltalk Consulting Ltd. All rights reserved. + */ + +"SelectImagePanePrompt" = "Select a Squeak image file to open"; diff --git a/platforms/minheadless/common/debug.h b/platforms/minheadless/common/debug.h new file mode 100644 index 0000000000..3a040144cb --- /dev/null +++ b/platforms/minheadless/common/debug.h @@ -0,0 +1,6 @@ +#ifndef SQ_DEBUG_H +#define SQ_DEBUG_H + +# define DPRINTF(ARGS) ((void)0) + +#endif diff --git a/platforms/minheadless/common/glibc.h b/platforms/minheadless/common/glibc.h new file mode 100644 index 0000000000..4a545c6032 --- /dev/null +++ b/platforms/minheadless/common/glibc.h @@ -0,0 +1,37 @@ +#include +#if defined(__GNUC__) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2,3) + /* squash __ctype_to{upper,lower}_loc and avoid including the header */ +# define _CTYPE_H 1 +# undef isalnum +# undef isalpha +# undef isascii +# undef isblank +# undef iscntrl +# undef isdigit +# undef isgraph +# undef islower +# undef isprint +# undef ispunct +# undef isspace +# undef isupper +# undef isxdigit + + extern int isalnum(int c); + extern int isalpha(int c); + extern int isascii(int c); + extern int isblank(int c); + extern int iscntrl(int c); + extern int isdigit(int c); + extern int isgraph(int c); + extern int islower(int c); + extern int isprint(int c); + extern int ispunct(int c); + extern int isspace(int c); + extern int isupper(int c); + extern int isxdigit(int c); + /* squash realpath@GLIBC_2.3 */ +# include +/* asm (".symver realpath, realpath@GLIBC_2.0"); */ +# endif +#endif diff --git a/platforms/minheadless/common/mac-alias.inc b/platforms/minheadless/common/mac-alias.inc new file mode 100644 index 0000000000..7db16f3152 --- /dev/null +++ b/platforms/minheadless/common/mac-alias.inc @@ -0,0 +1,42 @@ +/* Based on code contributed by Tom Rushworth. + * Mutilated beyond recognition by Ian Piumarta. + * + * last edited: 2006-04-24 15:38:40 by piumarta on emilia.local + */ + +#include /* see sqGetFilenameFromString() */ + +/* Answer nonzero if path describes an OS X alias file. */ + +static sqInt +isMacAlias(char *path) +{ + Boolean isAlias= false; + Boolean isFolder= false; + FSRef fileRef; /* No need to dispose of this. */ + FSRef *frp= &fileRef; + + return (noErr == FSPathMakeRef((UInt8 *)path, frp, &isFolder)) /* POSIX path -> OS X FSRef */ + && (noErr == FSIsAliasFile(frp, &isAlias, &isFolder)) /* test for alias */ + && isAlias; +} + + +/* Resolve aliases in the src path leaving the result in dst. + Answer nonzero if successful. + Note: dst and src may refer to the same buffer. */ + +static sqInt +resolveMacAlias(char *dst, char *src, sqInt max_length) +{ + Boolean wasAlias= false; + Boolean isFolder= false; + FSRef fileRef; /* No need to dispose of this. */ + FSRef *frp= &fileRef; + + return (noErr == FSPathMakeRef((UInt8 *)src, frp, &isFolder)) /* POSIX path -> OS X FSRef */ + && (noErr == FSResolveAliasFileWithMountFlags(frp, true, /* resolve */ + &isFolder, &wasAlias, + kResolveAliasFileNoUI)) + && (noErr == FSRefMakePath(frp, (UInt8 *)dst, PATH_MAX)); /* resolved FSRef -> POSIX path */ +} diff --git a/platforms/minheadless/common/sqConfig.h b/platforms/minheadless/common/sqConfig.h new file mode 100644 index 0000000000..b9a162f8c0 --- /dev/null +++ b/platforms/minheadless/common/sqConfig.h @@ -0,0 +1,21 @@ +#ifndef SQ_COMFIG_H_ +#define SQ_COMFIG_H_ + +#define VMBIGENDIAN 0 + +#if defined(__GNUC__) +/* Define the "don't generate functions with register parameters" attribute + * for x86 and similar. Do so for debugging; gdb typically can't call static + * functions that have been optimized to use register arguments. + */ +# if defined(_M_I386) || defined(_X86_) || defined(i386) || defined(i486) || defined(i586) || defined(i686) || defined(__i386__) || defined(__386__) || defined(X86) || defined(I386) +# define PlatformNoDbgRegParms __attribute__ ((regparm (0))) +# endif +# define NeverInline __attribute__ ((noinline)) +#endif + +#if defined( __clang__) +# define NeverInline __attribute__ ((noinline)) +#endif + +#endif /* SQ_COMFIG_H_ */ diff --git a/platforms/minheadless/common/sqEventCommon.c b/platforms/minheadless/common/sqEventCommon.c new file mode 100644 index 0000000000..6e4e1dd5c8 --- /dev/null +++ b/platforms/minheadless/common/sqEventCommon.c @@ -0,0 +1,50 @@ +/* sqEventCommon.h -- Common support functions used by legacy display API + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#include "sq.h" + +/*** event handling ***/ +static sqInt inputEventSemaIndex= 0; + +/* set asynchronous input event semaphore */ +sqInt +ioSetInputSemaphore(sqInt semaIndex) +{ + if (semaIndex == 0) + success(false); + else + inputEventSemaIndex= semaIndex; + return true; +} + +void +ioSignalInputEvent(void) +{ + if (inputEventSemaIndex > 0) + signalSemaphoreWithIndex(inputEventSemaIndex); +} diff --git a/platforms/minheadless/common/sqEventCommon.h b/platforms/minheadless/common/sqEventCommon.h new file mode 100644 index 0000000000..baa298eeff --- /dev/null +++ b/platforms/minheadless/common/sqEventCommon.h @@ -0,0 +1,64 @@ +/* sqEventCommon.h -- Common support functions used by legacy display API + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef SQ_EVENT_COMMON_H +#define SQ_EVENT_COMMON_H + +#include "sqCircularQueue.h" +#include "sq.h" + +#ifndef SQ_EVENT_QUEUE_SIZE +#define SQ_EVENT_QUEUE_SIZE 256 +#endif + +typedef union sqEventUnion +{ + sqIntptr_t type; + sqInputEvent input; + sqKeyboardEvent key; + sqMouseEvent mouse; + sqWindowEvent window; + sqDragDropFilesEvent dnd; + sqMenuEvent menu; + sqComplexEvent complex; +}sqEventUnion; + +typedef struct sqEventQueue +{ + sqCircularQueueInfo info; + sqEventUnion elements[SQ_EVENT_QUEUE_SIZE]; +} sqEventQueue; + +#define sqEventQueueIsEmpty(queue) sqQueueIsEmpty(queue, SQ_EVENT_QUEUE_SIZE) +#define sqEventQueueIsFull(queue) sqQueueIsFull(queue, SQ_EVENT_QUEUE_SIZE) +#define sqEventQueuePush(queue, value) sqQueuePush(queue, SQ_EVENT_QUEUE_SIZE, value) +#define sqEventQueuePopInto(queue, result) sqQueuePopInto(queue, SQ_EVENT_QUEUE_SIZE, result) + +void ioSignalInputEvent(void); + +#endif /* SQ_EVENT_COMMON_H */ diff --git a/platforms/minheadless/common/sqExternalPrimitives.c b/platforms/minheadless/common/sqExternalPrimitives.c new file mode 100644 index 0000000000..c9a7fd1481 --- /dev/null +++ b/platforms/minheadless/common/sqExternalPrimitives.c @@ -0,0 +1,252 @@ +/* sqExternalPrimitives.c -- Support functions for loading external primitives. + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include "sq.h" + +int sqVMOptionTraceModuleLoading = 0; + +static void *loadModuleHandle(const char *fileName); +static sqInt freeModuleHandle(void *module); +static void *getModuleSymbol(void *module, const char *symbol); + +/* Modules */ +extern char *squeakPlugins; +extern char squeakExtraPluginsPath[]; + +static const char *moduleNamePatterns[] = { + "%s%s", +#if defined(_WIN32) + "%s%s.dll", + "%slib%s.dll", +#elif defined(__APPLE__) + "%s%s", + "%s%s.dylib", + "%slib%s.dylib", +#else + "%s%s.so", + "%slib%s.so", +#endif + NULL +}; + +static const char *additionalModuleSearchPaths[] = { +#ifdef _WIN32 +#endif +#if defined(__linux__) || defined(unix) || defined(__APPLE__) + "/usr/local/lib/", + "/usr/lib/", + "/lib/", +# if defined(__linux__) +# if defined(__i386__) + "/usr/local/lib/i386-linux-gnu/", + "/usr/lib/i386-linux-gnu/", + "/lib/i386-linux-gnu/", +# elif defined(__x86_64__) + "/usr/local/lib/x86_64-linux-gnu/", + "/usr/lib/x86_64-linux-gnu/", + "/lib/x86_64-linux-gnu/", +# endif +# endif +#endif + NULL +}; + +static char moduleNameBuffer[FILENAME_MAX]; + +static void * +tryToLoadModuleInPath(const char *path, const char *moduleName) +{ + void *moduleHandle; + + const char **currentPattern = moduleNamePatterns; + for(; *currentPattern; ++currentPattern) + { + snprintf(moduleNameBuffer, FILENAME_MAX, *currentPattern, path, moduleName); + moduleNameBuffer[FILENAME_MAX - 1] = 0; + moduleHandle = loadModuleHandle(moduleNameBuffer); + if(moduleHandle) + return moduleHandle; + } + + return 0; +} + +void * +ioLoadModule(char *pluginName) +{ + void *moduleHandle; + + moduleHandle = tryToLoadModuleInPath(squeakPlugins, pluginName); + if(moduleHandle) + return moduleHandle; + + moduleHandle = tryToLoadModuleInPath(squeakExtraPluginsPath, pluginName); + if(moduleHandle) + return moduleHandle; + + moduleHandle = tryToLoadModuleInPath("", pluginName); + if(moduleHandle) + return moduleHandle; + + const char **currentPath = additionalModuleSearchPaths; + for(; *currentPath; ++currentPath) + { + moduleHandle = tryToLoadModuleInPath(*currentPath, pluginName); + if(moduleHandle) + return moduleHandle; + } + + if(sqVMOptionTraceModuleLoading) + fprintf(stderr, "Failed to load module: %s\n", pluginName); + return 0; +} + +sqInt +ioFreeModule(void *moduleHandle) +{ + return freeModuleHandle(moduleHandle); +} + +#if SPURVM +void * +ioFindExternalFunctionInAccessorDepthInto(char *lookupName, void *moduleHandle, + sqInt *accessorDepthPtr) +#else +void * +ioFindExternalFunctionIn(char *lookupName, void *moduleHandle) +#endif +{ + void *function; + + if (!*lookupName) /* avoid errors in dlsym from eitherPlugin: code. */ + return 0; + + function = getModuleSymbol(moduleHandle, lookupName); + +#if SPURVM + if (function && accessorDepthPtr) + { + char buf[256]; + signed char *accessorDepthVarPtr; + + strcpy(buf, lookupName); + snprintf(buf+strlen(buf), sizeof(buf) - strlen(buf), "AccessorDepth"); + accessorDepthVarPtr = getModuleSymbol(moduleHandle, buf); + /* The Slang machinery assumes accessor depth defaults to -1, which + * means "no accessor depth". It saves space not outputting -1 depths. + */ + *accessorDepthPtr = accessorDepthVarPtr + ? *accessorDepthVarPtr + : -1; + } +#endif /* SPURVM */ + return function; +} + +#if defined(_WIN32) + +static void * +loadModuleHandle(const char *fileName) +{ + WCHAR convertedPath[MAX_PATH + 1]; + sqUTF8ToUTF16Copy(convertedPath, MAX_PATH + 1, fileName); +#ifdef DEBUG + printf("try loading %s\n", fileName); +#endif + return LoadLibraryW(convertedPath); +} + +static sqInt +freeModuleHandle(void *module) +{ + return FreeLibrary((HMODULE)module) ? 1 : 0; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return (void*)GetProcAddress((HMODULE)module, symbol); +} + +#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) + +#include + +static void * +loadModuleHandle(const char *fileName) +{ + int flags = RTLD_NOW | RTLD_GLOBAL; +#ifdef RTLD_DEEPBIND + flags |= RTLD_DEEPBIND; /* Prefer local symbols in the shared object vs external symbols. */ +#endif + +#ifdef DEBUG + printf("try loading %s\n", fileName); +#endif + return dlopen(fileName, flags); +} + +static sqInt +freeModuleHandle(void *module) +{ + return dlclose(module) == 0 ? 0 : 1; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return dlsym(module, symbol); +} + +#else + +static void * +loadModuleHandle(const char *fileName) +{ + return 0; +} + +static sqInt +freeModuleHandle(void *module) +{ + return 1; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return 0; +} + +#endif diff --git a/platforms/minheadless/common/sqExternalPrimitives.c.orig b/platforms/minheadless/common/sqExternalPrimitives.c.orig new file mode 100644 index 0000000000..36f5494cd6 --- /dev/null +++ b/platforms/minheadless/common/sqExternalPrimitives.c.orig @@ -0,0 +1,241 @@ +/* sqExternalPrimitives.c -- Support functions for loading external primitives. + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +#include +#include "sq.h" + +int sqVMOptionTraceModuleLoading = 0; + +static void *loadModuleHandle(const char *fileName); +static sqInt freeModuleHandle(void *module); +static void *getModuleSymbol(void *module, const char *symbol); + +/* Modules */ +extern char *squeakPlugins; + +static const char *moduleNamePatterns[] = { + "%s%s", +#if defined(_WIN32) + "%s%s.dll", + "%slib%s.dll", +#elif defined(__APPLE__) + "%s%s", + "%s%s.dylib", + "%slib%s.dylib", +#else + "%s%s.so", + "%slib%s.so", +#endif + NULL +}; + +static const char *additionalModuleSearchPaths[] = { +#ifdef _WIN32 +#endif +#if defined(__linux__) || defined(unix) || defined(__APPLE__) + "/usr/local/lib/", + "/usr/lib/", + "/lib/", +# if defined(__linux__) +# if defined(__i386__) + "/usr/local/lib/i386-linux-gnu/", + "/usr/lib/i386-linux-gnu/", + "/lib/i386-linux-gnu/", +# elif defined(__x86_64__) + "/usr/local/lib/x86_64-linux-gnu/", + "/usr/lib/x86_64-linux-gnu/", + "/lib/x86_64-linux-gnu/", +# endif +# endif +#endif + NULL +}; + +static char moduleNameBuffer[FILENAME_MAX]; + +static void * +tryToLoadModuleInPath(const char *path, const char *moduleName) +{ + void *moduleHandle; + + const char **currentPattern = moduleNamePatterns; + for(; *currentPattern; ++currentPattern) + { + snprintf(moduleNameBuffer, FILENAME_MAX, *currentPattern, path, moduleName); + moduleNameBuffer[FILENAME_MAX - 1] = 0; + moduleHandle = loadModuleHandle(moduleNameBuffer); + if(moduleHandle) + return moduleHandle; + } + + return 0; +} + +void * +ioLoadModule(char *pluginName) +{ + void *moduleHandle; + + moduleHandle = tryToLoadModuleInPath(squeakPlugins, pluginName); + if(moduleHandle) + return moduleHandle; + + moduleHandle = tryToLoadModuleInPath("", pluginName); + if(moduleHandle) + return moduleHandle; + + const char **currentPath = additionalModuleSearchPaths; + for(; *currentPath; ++currentPath) + { + moduleHandle = tryToLoadModuleInPath(*currentPath, pluginName); + if(moduleHandle) + return moduleHandle; + } + + if(sqVMOptionTraceModuleLoading) + fprintf(stderr, "Failed to load module: %s\n", pluginName); + return 0; +} + +sqInt +ioFreeModule(void *moduleHandle) +{ + return freeModuleHandle(moduleHandle); +} + +#if SPURVM +void * +ioFindExternalFunctionInAccessorDepthInto(char *lookupName, void *moduleHandle, + sqInt *accessorDepthPtr) +#else +void * +ioFindExternalFunctionIn(char *lookupName, void *moduleHandle) +#endif +{ + void *function; + + if (!*lookupName) /* avoid errors in dlsym from eitherPlugin: code. */ + return 0; + + function = getModuleSymbol(moduleHandle, lookupName); + +#if SPURVM + if (function && accessorDepthPtr) + { + char buf[256]; + signed char *accessorDepthVarPtr; + + strcpy(buf, lookupName); + snprintf(buf+strlen(buf), sizeof(buf) - strlen(buf), "AccessorDepth"); + accessorDepthVarPtr = getModuleSymbol(moduleHandle, buf); + /* The Slang machinery assumes accessor depth defaults to -1, which + * means "no accessor depth". It saves space not outputting -1 depths. + */ + *accessorDepthPtr = accessorDepthVarPtr + ? *accessorDepthVarPtr + : -1; + } +#endif /* SPURVM */ + return function; +} + +#if defined(_WIN32) + +static void * +loadModuleHandle(const char *fileName) +{ + WCHAR convertedPath[MAX_PATH + 1]; + sqUTF8ToUTF16Copy(convertedPath, MAX_PATH + 1, fileName); + return LoadLibraryW(convertedPath); +} + +static sqInt +freeModuleHandle(void *module) +{ + return FreeLibrary((HMODULE)module) ? 1 : 0; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return (void*)GetProcAddress((HMODULE)module, symbol); +} + +#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) + +#include + +static void * +loadModuleHandle(const char *fileName) +{ + int flags = RTLD_NOW | RTLD_GLOBAL; +#ifdef RTLD_DEEPBIND + flags |= RTLD_DEEPBIND; /* Prefer local symbols in the shared object vs external symbols. */ +#endif + + return dlopen(fileName, flags); +} + +static sqInt +freeModuleHandle(void *module) +{ + return dlclose(module) == 0 ? 0 : 1; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return dlsym(module, symbol); +} + +#else + +static void * +loadModuleHandle(const char *fileName) +{ + return 0; +} + +static sqInt +freeModuleHandle(void *module) +{ + return 1; +} + +static void * +getModuleSymbol(void *module, const char *symbol) +{ + return 0; +} + +#endif diff --git a/platforms/minheadless/common/sqInternalPrimitives.c b/platforms/minheadless/common/sqInternalPrimitives.c new file mode 100644 index 0000000000..d281d352c5 --- /dev/null +++ b/platforms/minheadless/common/sqInternalPrimitives.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "sqWindow.h" + +static int pluginExportsCapacity = 0; +static int pluginExportsSize = 0; + +static void *emptyPluginExports[] = { + NULL +}; + +void **pluginExports = emptyPluginExports; +extern void *vm_exports[]; +extern void *os_exports[]; + +#define INTERNAL_PLUGIN(pluginName) \ + extern void *pluginName ## _exports[]; +#include "sqInternalPlugins.inc" + +#undef INTERNAL_PLUGIN + +static void +increaseCapacity() +{ + int newCapacity; + void **newPluginExports; + int i; + + /* Compute the new capacity. */ + newCapacity = pluginExportsCapacity*2; + if(newCapacity < 8) + newCapacity = 8; + + /* Allocate the plugin export list. */ + newPluginExports = (void**)calloc(newCapacity + 1, sizeof(void*)); + + /* Copy the old elements to the new list. */ + for(i = 0; i < pluginExportsSize; ++i) + newPluginExports[i] = pluginExports[i]; + + /* Free the old list. */ + if(pluginExports && pluginExports != emptyPluginExports) + free(pluginExports); + + /* Use the new capacity and the new list. */ + pluginExportsCapacity = newCapacity; + pluginExports = newPluginExports; +} + +void +ioAddInternalPluginPrimitives(void *primitiveList) +{ + if(pluginExportsSize >= pluginExportsCapacity) + increaseCapacity(); + + pluginExports[pluginExportsSize++] = primitiveList; +} + +void +ioInitializeInternalPluginPrimitives(void) +{ + ioAddInternalPluginPrimitives(vm_exports); + ioAddInternalPluginPrimitives(os_exports); + + #define INTERNAL_PLUGIN(pluginName) \ + ioAddInternalPluginPrimitives(pluginName ## _exports); + #include "sqInternalPlugins.inc" + + #undef INTERNAL_PLUGIN +} diff --git a/platforms/minheadless/common/sqMain.c b/platforms/minheadless/common/sqMain.c new file mode 100644 index 0000000000..0a64343113 --- /dev/null +++ b/platforms/minheadless/common/sqMain.c @@ -0,0 +1,34 @@ +/* sqMain.c -- main entry point for the standalone Squeak VM + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ +#include "OpenSmalltalkVM.h" + +int +main(int argc, const char **argv) +{ + return osvm_main(argc, argv); +} diff --git a/platforms/minheadless/common/sqNamedPrims.h b/platforms/minheadless/common/sqNamedPrims.h new file mode 100644 index 0000000000..602bf913ed --- /dev/null +++ b/platforms/minheadless/common/sqNamedPrims.h @@ -0,0 +1 @@ +extern sqExport **pluginExports; diff --git a/platforms/minheadless/common/sqPlatformSpecific.h b/platforms/minheadless/common/sqPlatformSpecific.h new file mode 100644 index 0000000000..83c9c412e5 --- /dev/null +++ b/platforms/minheadless/common/sqPlatformSpecific.h @@ -0,0 +1,43 @@ +/* sqPlatformSpecific.h -- platform-specific modifications to sq.h + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef _SQ_PLATFORM_SPECIFIC_H +#define _SQ_PLATFORM_SPECIFIC_H + +#include "sqMemoryAccess.h" +#include "sqPlatformSpecificCommon.h" + +#if defined(_WIN32) +#include "sqPlatformSpecific-Win32.h" +#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) +#include "sqPlatformSpecific-Unix.h" +#else +#include "sqPlatformSpecific-Generic.h" +#endif + +#endif /* _SQ_PLATFORM_SPECIFIC_H */ diff --git a/platforms/minheadless/common/sqPlatformSpecificCommon.h b/platforms/minheadless/common/sqPlatformSpecificCommon.h new file mode 100644 index 0000000000..7079223392 --- /dev/null +++ b/platforms/minheadless/common/sqPlatformSpecificCommon.h @@ -0,0 +1,71 @@ +/* sqPlatformSpecificCommon.h -- platform-specific modifications to sq.h + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef _SQ_PLATFORM_SPECIFIC_COMMON_H +#define _SQ_PLATFORM_SPECIFIC_COMMON_H + +#include "sqPath.h" +#include "sqTextEncoding.h" + +/** + * Printing and reporting functions. + * In some platforms, such as Windows, stdout, stderr and stdin are not always usable + */ +extern void sqMessagePrintf(const char *format, ...); +extern void sqWarnPrintf(const char *format, ...); +extern void sqErrorPrintf(const char *format, ...); +extern void sqFatalErrorPrintf(const char *format, ...); +extern void sqFatalErrorPrintfNoExit(const char *format, ...); +extern void sqError(char *errorMessage); + + + +/* +#define messagePrintf sqMessagePrintf +#define warnPrintf sqWarnPrintf +#define errorPrintf sqErrorPrintf +*/ + +#define messagePrintf printf +#define warnPrintf printf +#define errorPrintf printf + +#ifndef error +#define error sqError +#endif + +/* Function used by the Squeak security plugin. In a headless VM, do not create a message box. */ +extern int sqAskSecurityYesNoQuestion(const char *question); + +extern const char *sqGetCurrentImagePath(void); + +/* Stack trace dumping */ +typedef int (*sqFunctionThatCouldCrash)(void *userdata); +extern int sqExecuteFunctionWithCrashExceptionCatching(sqFunctionThatCouldCrash function, void *userdata); + +#endif /* _SQ_PLATFORM_SPECIFIC_COMMON_H */ diff --git a/platforms/minheadless/common/sqPrinting.c b/platforms/minheadless/common/sqPrinting.c new file mode 100644 index 0000000000..0157d43bb9 --- /dev/null +++ b/platforms/minheadless/common/sqPrinting.c @@ -0,0 +1,313 @@ +/* sqPrinting.c -- printing and logging functions + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ +#include +#include +#include "sq.h" +#include "sqConfig.h" + +#ifdef error +#undef error + +void +error(char *errorMessage) +{ + sqError(errorMessage); +} +#endif + +void +sqError(char *errorMessage) +{ + fprintf(stderr, "%s\n", errorMessage); + abort(); +} + + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include + +#define PRINTF_BUFFER_SIZE 4096 + +void +sqMessagePrintf(const char *format, ...) +{ + va_list args; + char *buffer; + + va_start (args, format); + if (!GetConsoleCP()) + { + buffer = (char*)calloc(PRINTF_BUFFER_SIZE, sizeof(char)); + vsnprintf(buffer, sizeof(buffer), format, args); + OutputDebugStringA(buffer); + free(buffer); + } + else + { + vfprintf(stdout, format, args); + } + va_end (args); +} + +void +sqWarnPrintf(const char *format, ...) +{ + va_list args; + char *buffer; + + va_start (args, format); + if (!GetConsoleCP()) + { + buffer = (char*)calloc(PRINTF_BUFFER_SIZE, sizeof(char)); + vsnprintf(buffer, sizeof(buffer), format, args); + OutputDebugStringA(buffer); + free(buffer); + } + else + { + vfprintf(stdout, format, args); + } + va_end (args); +} + +void +sqErrorPrintf(const char *format, ...) +{ + va_list args; + char *buffer; + + va_start (args, format); + if (!GetConsoleCP()) + { + buffer = (char*)calloc(PRINTF_BUFFER_SIZE, sizeof(char)); + vsnprintf(buffer, sizeof(buffer), format, args); + OutputDebugStringA(buffer); + free(buffer); + } + else + { + vfprintf(stderr, format, args); + } + va_end (args); +} + +void +sqFatalErrorPrintf(const char *format, ...) +{ + va_list args; + char *buffer; + + va_start (args, format); + if (!GetConsoleCP()) + { + /*TODO: Display a message box in this case.*/ + buffer = (char*)calloc(PRINTF_BUFFER_SIZE, sizeof(char)); + vsnprintf(buffer, sizeof(buffer), format, args); + OutputDebugStringA(buffer); + free(buffer); + } + else + { + vfprintf(stderr, format, args); + } + va_end (args); + abort(); +} + +void +sqFatalErrorPrintfNoExit(const char *format, ...) +{ + va_list args; + char *buffer; + + va_start (args, format); + if (!GetConsoleCP()) + { + /*TODO: Display a message box in this case.*/ + buffer = (char*)calloc(PRINTF_BUFFER_SIZE, sizeof(char)); + vsnprintf(buffer, sizeof(buffer), format, args); + OutputDebugStringA(buffer); + free(buffer); + } + else + { + vfprintf(stderr, format, args); + } + va_end (args); +} + +int +sqAskSecurityYesNoQuestion(const char *question) +{ + if (!GetConsoleCP()) + { + /* TODO: Support UTF-8. */ + return MessageBoxA(NULL, question, "Squeak Security Alert", MB_YESNO | MB_ICONSTOP) == IDYES; + } + else + { + return 0; + } +} + +#ifndef printLastError +void +printLastError(const TCHAR *prefix) +{ LPVOID lpMsgBuf; + DWORD lastError; + + lastError = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + wprintf(TEXT("%s (%ld) -- %s\n"), prefix, lastError, (unsigned short*)lpMsgBuf); + LocalFree( lpMsgBuf ); +} +#endif +#ifndef vprintLastError +void +vprintLastError(TCHAR *fmt, ...) +{ LPVOID lpMsgBuf; + DWORD lastError; + TCHAR *buf; + va_list args; + + buf = (TCHAR*) calloc(4096, sizeof(TCHAR)); + va_start(args, fmt); + wvsprintf(buf, fmt, args); + va_end(args); + + lastError = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + wprintf(TEXT("%s (%ld: %s)\n"), buf, lastError, (unsigned short*)lpMsgBuf); + LocalFree( lpMsgBuf ); + free(buf); +} +#endif + +#ifndef sqMessageBox +int +sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...) +{ + TCHAR *ptr, *buf; + va_list args; + DWORD result; + + ptr = sqUTF8toUTF16New(fmt); + buf = (TCHAR*)calloc(sizeof(TCHAR), 4096); + va_start(args, fmt); + wvsprintf(buf, ptr, args); + va_end(args); + + result = MessageBox(NULL, buf, titleString, dwFlags | MB_SETFOREGROUND); + free(ptr); + free(buf); + return result; +} +#endif + +#ifndef abortMessage +int +abortMessage(const TCHAR* fmt, ...) +{ + TCHAR *buf; + va_list args; + + buf = (TCHAR*)calloc(sizeof(TCHAR), 4096); + va_start(args, fmt); + wvsprintf(buf, fmt, args); + va_end(args); + + MessageBox(NULL, buf, TEXT(VM_NAME) TEXT("!"), MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); + free(buf); + exit(-1); +} +#endif + +#else +void +sqMessagePrintf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); +} + +void +sqWarnPrintf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); +} + +void +sqErrorPrintf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +void +sqFatalErrorPrintf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + abort(); +} + +void +sqFatalErrorPrintfNoExit(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +int +sqAskSecurityYesNoQuestion(const char *question) +{ + return 0; +} + +#endif diff --git a/platforms/minheadless/common/sqVirtualMachineInterface.c b/platforms/minheadless/common/sqVirtualMachineInterface.c new file mode 100644 index 0000000000..d4c1ec8b82 --- /dev/null +++ b/platforms/minheadless/common/sqVirtualMachineInterface.c @@ -0,0 +1,654 @@ +/* sqVirtualMachineInterce.c -- implementation of the standard VM embedding interface + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#ifndef _WIN32 +#include +#endif + +#include +#include + +#include "OpenSmalltalkVM.h" +#include "sq.h" +#include "sqSCCSVersion.h" + +#define DefaultHeapSize 20 /* megabytes BEYOND actual image size */ +#define DefaultMmapSize 1024 /* megabytes of virtual memory */ + +/** + * Some VM options + */ +extern int sqVMOptionTraceModuleLoading; + + +char imageName[FILENAME_MAX]; +static char imagePath[FILENAME_MAX]; + +static int headlessMode = 0; + +static char *emptyArgumentVector[] = { + NULL, +}; +static char *emptyEnvironmentVector[] = { + NULL, +}; + +int argCnt= 0; /* global copies for access from plugins */ +char **argVec= 0; +char **envVec= 0; +char *squeakPlugins; + +char *documentName = 0; +static char shortImageName[FILENAME_MAX]; +static char vmName[FILENAME_MAX]; +static char vmPath[FILENAME_MAX]; +char squeakExtraPluginsPath[FILENAME_MAX]; + +static int squeakArgumentCount; +static char **squeakArgumentVector; + +static int vmArgumentCount; +static char **vmArgumentVector; + +int sqVMOptionInstallExceptionHandlers = 1; +int sqVMOptionBlockOnError = 0; +int sqVMOptionBlockOnWarn = 0; + +extern void initGlobalStructure(void); // this is effectively null if a global register is not being used +extern void findExecutablePath(const char *localVmName, char *dest, size_t destSize); + +extern void ioInitWindowSystem(sqInt headlessMode); +extern void ioShutdownWindowSystem(void); +extern const char *ioWindowSystemName(void); + +extern void ioInitTime(void); +extern void ioInitThreads(void); +extern void aioInit(void); + +#ifdef _WIN32 +extern sqInt ioInitSecurity(void); +#endif + +extern void ioInitPlatformSpecific(void); +extern void ioInitializeInternalPluginPrimitives(void); + +static long extraMemory= 0; +int useMmap= DefaultMmapSize * 1024 * 1024; + +int +ioIsHeadless(void) +{ + return headlessMode; +} + +char* +getImageName(void) +{ + return imageName; +} + +const char* +sqGetCurrentImagePath() +{ + return imagePath; +} + +sqInt +imageNameGetLength(sqInt sqImageNameIndex, sqInt length) +{ + char *sqImageName = pointerForOop(sqImageNameIndex); + int count; + + count= strlen(imageName); + count= (length < count) ? length : count; + + /* copy the file name into the Squeak string */ + memcpy(sqImageName, imageName, count); + + return count; +} + +sqInt +imageNamePutLength(sqInt sqImageNameIndex, sqInt length) +{ + char *sqImageName= pointerForOop(sqImageNameIndex); + int count; + + count = (length >= sizeof(imageName)) ? sizeof(imageName) - 1 : length; + + /* copy the file name into a null-terminated C string */ + memcpy(imageName, sqImageName, count); + imageName[count] = 0; + + return count; +} + +sqInt +imageNameSize(void) +{ + return strlen(imageName); +} + +sqInt +vmPathSize(void) +{ + return strlen(vmPath); +} + +sqInt +vmPathGetLength(sqInt sqVMPathIndex, sqInt length) +{ + char *stVMPath= pointerForOop(sqVMPathIndex); + int count; + + count = strlen(vmPath); + count = (length < count) ? length : count; + + /* copy the file name into the Squeak string */ + memcpy(stVMPath, vmPath, count); + + return count; +} + +char* +ioGetLogDirectory(void) +{ + return ""; +} + +sqInt +ioSetLogDirectoryOfSize(void* lblIndex, sqInt sz) +{ + return 1; +} + +/*** Access to system attributes and command-line arguments ***/ + + +/* OS_TYPE may be set in configure.in and passed via the Makefile */ + +char * +GetAttributeString(sqInt id) +{ + if (id < 0) /* VM argument */ + { + if (-id < vmArgumentCount) + return vmArgumentVector[-id]; + success(false); + return ""; + } + + switch (id) + { + case 0: + return vmName[0] ? vmName : vmArgumentVector[0]; + case 1: + return imageName; + case 1001: + /* OS type: "unix", "win32", "mac", ... */ + return OS_TYPE; + case 1002: + /* OS name: e.g. "solaris2.5" on unix, "win95" on win32, ... */ + return VM_TARGET_OS; + case 1003: + /* processor architecture: e.g. "68k", "x86", "PowerPC", ... */ + return VM_TARGET_CPU; + case 1004: + /* Interpreter version string */ + return (char *)interpreterVersion; + case 1005: + /* window system name */ + return (char*)ioWindowSystemName(); + case 1006: + /* vm build string */ + return VM_BUILD_STRING; +#if STACKVM + case 1007: { /* interpreter build info */ + extern char *__interpBuildInfo; + return __interpBuildInfo; + } +# if COGVM + case 1008: { /* cogit build info */ + extern char *__cogitBuildInfo; + return __cogitBuildInfo; + } +# endif +#endif + + case 1009: /* source tree version info */ + return sourceVersionString(' '); + + default: + if ((id - 2) < squeakArgumentCount) + return squeakArgumentVector[id - 2]; + } + success(false); + return ""; +} + +sqInt +attributeSize(sqInt id) +{ + return strlen(GetAttributeString(id)); +} + +sqInt +getAttributeIntoLength(sqInt id, sqInt byteArrayIndex, sqInt length) +{ + if (length > 0) + strncpy(pointerForOop(byteArrayIndex), GetAttributeString(id), length); + return 0; +} + +/** + * FIXME: Check this malloc for memory leaks. + */ +char *getVersionInfo(int verbose) +{ +#if STACKVM + extern char *__interpBuildInfo; +# define INTERP_BUILD __interpBuildInfo +# if COGVM + extern char *__cogitBuildInfo; +# endif +#else +# define INTERP_BUILD interpreterVersion +#endif + extern char *revisionAsString(); + char *info= (char *)malloc(4096); + info[0]= '\0'; + +#if SPURVM +# if BytesPerOop == 8 +# define ObjectMemory " Spur 64-bit" +# else +# define ObjectMemory " Spur" +# endif +#else +# define ObjectMemory +#endif +#if defined(NDEBUG) +# define BuildVariant "Production" ObjectMemory +#elif DEBUGVM +# define BuildVariant "Debug" ObjectMemory +# else +# define BuildVariant "Assert" ObjectMemory +#endif + +#if ITIMER_HEARTBEAT +# define HBID " ITHB" +#else +# define HBID +#endif + + if (verbose) + sprintf(info+strlen(info), IMAGE_DIALECT_NAME " VM version: "); + sprintf(info+strlen(info), "%s-%s ", VM_VERSION, revisionAsString()); +#if defined(USE_XSHM) + sprintf(info+strlen(info), " XShm"); +#endif + sprintf(info+strlen(info), " [" BuildVariant HBID " VM]\n"); + if (verbose) + sprintf(info+strlen(info), "Built from: "); + sprintf(info+strlen(info), "%s\n", INTERP_BUILD); +#if COGVM + if (verbose) + sprintf(info+strlen(info), "With: "); + sprintf(info+strlen(info), "%s\n", GetAttributeString(1008)); /* __cogitBuildInfo */ +#endif + if (verbose) + sprintf(info+strlen(info), "Revision: "); + sprintf(info+strlen(info), "%s\n", sourceVersionString('\n')); + sprintf(info+strlen(info), "plugin path: %s [default: %s]\n", squeakPlugins, vmPath); + return info; +} + +void +getCrashDumpFilenameInto(char *buf) +{ + /* + strcpy(buf,vmLogDirA); + vmLogDirA[0] && strcat(buf, "/"); + */ + strcat(buf, "crash.dmp"); +} + +static void +outOfMemory(void) +{ + /* pushing stderr outputs the error report on stderr instead of stdout */ + /* pushOutputFile((char *)STDERR_FILENO); */ + error("out of memory\n"); +} + +static void +recordPathsForVMName(const char *localVmName) +{ + findExecutablePath(localVmName, vmPath, sizeof(vmPath)); +} + +static void +usage(void) +{ +} + +static int +parseVMArgument(char **argv) +{ +#define IS_VM_OPTION(name) (!strcmp(*argv, "-" name) || !strcmp(*argv, "--" name)) + if (IS_VM_OPTION("headless") || IS_VM_OPTION("no-interactive")) + { + headlessMode = 1; + return 1; + } + else if(IS_VM_OPTION("interactive") || IS_VM_OPTION("headfull")) + { + headlessMode = 0; + return 1; + } + else if(IS_VM_OPTION("trace-module-loads")) + { + sqVMOptionTraceModuleLoading = 1; + return 1; + } + else if(IS_VM_OPTION("full-trace")) + { + /* This should enable all of the tracing options that are available. */ + sqVMOptionTraceModuleLoading = 1; + return 1; + } + +#undef IS_VM_OPTION + + return 0; +} + +static int +parseArguments(int argc, char **argv) +{ +# define skipArg() (--argc, argv++) +# define saveArg() (vmArgumentVector[vmArgumentCount++]= *skipArg()) + + saveArg(); /* vm name */ + + while ((argc > 0) && (**argv == '-')) /* more options to parse */ + { + int n= 0; + if (!strcmp(*argv, "--")) /* escape from option processing */ + break; + + n = parseVMArgument(argv); + + if (n == 0) /* option not recognised */ + { + fprintf(stderr, "unknown option: %s\n", argv[0]); + usage(); + return OSVM_ERROR_UNSUPPORTED_PARAMETER; + } + + while (n--) + saveArg(); + } + + if (!argc) + return OSVM_SUCCESS; + if (!strcmp(*argv, "--")) + skipArg(); + else /* image name */ + { + if (!documentName) + strcpy(shortImageName, saveArg()); + if (!strstr(shortImageName, ".image")) + strcat(shortImageName, ".image"); + } + + /* save remaining arguments as Squeak arguments */ + while (argc > 0) + squeakArgumentVector[squeakArgumentCount++]= *skipArg(); + +# undef saveArg +# undef skipArg + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC int +osvm_getInterfaceVersion() +{ + return OSVM_VM_CORE_COMPILED_VERSION; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_loadImage(const char *fileName) +{ + size_t imageSize = 0; + sqImageFile imageFile = 0; + + /* Open the image file. */ + imageFile = sqImageFileOpen(fileName, "rb"); + if(!imageFile) + return OSVM_ERROR_FAILED_TO_OPEN_FILE; + + /* The security plugin requires an absolute path of the image.*/ + sqPathMakeAbsolute(imageName, sizeof(imageName), fileName); + sqPathExtractDirname(imagePath, sizeof(imagePath), imageName); + + /* Get the size of the image file*/ + sqImageFileSeekEnd(imageFile, 0); + imageSize = sqImageFilePosition(imageFile); + sqImageFileSeek(imageFile, 0); + + if (extraMemory) + useMmap= 0; + else + extraMemory = DefaultHeapSize * 1024 * 1024; +# ifdef DEBUG_IMAGE + printf("image size %ld + heap size %ld (useMmap = %d)\n", (long)sb.st_size, extraMemory, useMmap); +# endif + +#if SPURVM + readImageFromFileHeapSizeStartingAt(imageFile, 0, 0); +#else + extraMemory += (long)imageSize; + readImageFromFileHeapSizeStartingAt(imageFile, extraMemory, 0); +#endif + sqImageFileClose(imageFile); + + return OSVM_SUCCESS; +} + +static char tempImageNameAttempt[FILENAME_MAX]; +OSVM_VM_CORE_PUBLIC OSVMError +osvm_loadDefaultImage(void) +{ + OSVMError error; + + /* If the image name is empty, try to load the default image. */ + if(!shortImageName[0]) + strcpy(shortImageName, DEFAULT_IMAGE_NAME); + + /* Try to load the image as was passed. */ + sprintf(tempImageNameAttempt, "%s", shortImageName); + error = osvm_loadImage(tempImageNameAttempt); + if(!error) + return OSVM_SUCCESS; + + /* Make the image path relative to the VM*/ + sprintf(tempImageNameAttempt, "%s/%s", vmPath, shortImageName); + error = osvm_loadImage(tempImageNameAttempt); + if(!error) + return OSVM_SUCCESS; + + /* Failed. */ + return OSVM_ERROR_FAILED_TO_OPEN_FILE; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_initialize(void) +{ + /* check the interpreter's size assumptions for basic data types */ + if (sizeof(int) != 4) error("This C compiler's integers are not 32 bits."); + if (sizeof(double) != 8) error("This C compiler's floats are not 64 bits."); + if (sizeof(sqLong) != 8) error("This C compiler's long longs are not 64 bits."); + + argCnt = 0; + argVec = emptyArgumentVector; + envVec = emptyEnvironmentVector; + + initGlobalStructure(); + + /* Initialize the list of internal primitives. */ + ioInitializeInternalPluginPrimitives(); + + /* Perform platform specific initialization. */ + ioInitPlatformSpecific(); + + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_shutdown(void) +{ + /* Nothing required yet. */ + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_parseCommandLineArguments(int argc, const char **argv) +{ + /* Make parameters global for access from plugins */ + argCnt = argc; + argVec = (char**)argv; + envVec = NULL; + + /* Allocate arrays to store copies of pointers to command line + arguments. Used by getAttributeIntoLength(). */ + if ((vmArgumentVector = calloc(argc + 1, sizeof(char *))) == 0) + outOfMemory(); + + if ((squeakArgumentVector = calloc(argc + 1, sizeof(char *))) == 0) + outOfMemory(); + + recordPathsForVMName(argv[0]); /* full vm path */ + squeakPlugins = vmPath; /* default plugin location is VM directory */ + sqPathMakeAbsolute(vmName, sizeof(vmName), argv[0]); +#ifdef __APPLE__ + sqPathJoin(squeakExtraPluginsPath, sizeof(squeakExtraPluginsPath), squeakPlugins, "Plugins"); +#endif + return parseArguments(argc, (char**)argv); +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_parseVMCommandLineArguments(int argc, const char **argv) +{ + return OSVM_ERROR_NOT_YET_IMPLEMENTED; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_setVMStringParameter(const char *name, const char *value) +{ + return OSVM_ERROR_UNSUPPORTED_PARAMETER; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_setVMIntegerParameter(const char *name, const char *value) +{ + return OSVM_ERROR_UNSUPPORTED_PARAMETER; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_passImageCommandLineArguments(int argc, const char **argv) +{ + return OSVM_ERROR_NOT_YET_IMPLEMENTED; +} + +static int +osvm_doRunInterpreter(void *userdata) +{ + (void)userdata; + interpret(); + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_run(void) +{ + sqExecuteFunctionWithCrashExceptionCatching(&osvm_doRunInterpreter, NULL); + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_initializeVM(void) +{ + ioInitWindowSystem(headlessMode); + ioInitTime(); + ioInitThreads(); + ioVMThread = ioCurrentOSThread(); + aioInit(); + + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_shutdownVM(void) +{ + return OSVM_SUCCESS; +} + +OSVM_VM_CORE_PUBLIC OSVMError +osvm_main(int argc, const char **argv) +{ + OSVMError error; + + /* Global initialization */ + error = osvm_initialize(); + if(error) + return error; + + /* Parse the command line*/ + error = osvm_parseCommandLineArguments(argc, argv); + if(error) + return error; + + /* Initialize the VM */ + error = osvm_initializeVM(); + if(error) + return error; + + /* Load the command line image or the default one. */ + error = osvm_loadDefaultImage(); + if(error) + return error; + + /* Run Squeak */ + error = osvm_run(); + + /* Shutdown*/ + osvm_shutdown(); + + return error; +} diff --git a/platforms/minheadless/common/sqWindow-Dispatch.c b/platforms/minheadless/common/sqWindow-Dispatch.c new file mode 100644 index 0000000000..7708689e8a --- /dev/null +++ b/platforms/minheadless/common/sqWindow-Dispatch.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "sqWindow.h" + +extern sqWindowSystem sqNullWindowSystem; + +#ifdef SUPPORT_TRADITIONAL_DISPLAY +# ifdef HAVE_SDL2 +extern sqWindowSystem sqSDL2WindowSystem; +# endif +#endif + +sqWindowSystem *sqAllWindowSystems[] = { + &sqNullWindowSystem, +#ifdef SUPPORT_TRADITIONAL_DISPLAY +# ifdef HAVE_SDL2 + &sqSDL2WindowSystem, +# endif +#endif + + NULL +}; + +static sqWindowSystem *currentWindowSystem = 0; +extern void ioAddInternalPluginPrimitives(void *primitiveList); + +void (*ioProcessEventsHandler) (void) = 0; + +extern void setIoProcessEventsHandler(void * handler) { + ioProcessEventsHandler = (void(*)()) handler; +} + +void +ioSetWindowSystem(sqWindowSystem *windowSystem) +{ + if(!windowSystem) + return; + + currentWindowSystem = windowSystem; + ioAddInternalPluginPrimitives(windowSystem->primitives); +} + +void +ioInitWindowSystem(sqInt headlessMode) +{ + /* Try to use a non-null window system.*/ + if(!currentWindowSystem && !headlessMode) + ioSetWindowSystem(sqAllWindowSystems[1]); + + /* Make sure we are atleast using a null window system*/ + if(!currentWindowSystem) + ioSetWindowSystem(&sqNullWindowSystem); + currentWindowSystem->initialize(); +} + +void +ioShutdownWindowSystem(void) +{ + currentWindowSystem->shutdown(); + currentWindowSystem = 0; +} + +const char * +ioWindowSystemName(void) +{ + return currentWindowSystem->name; +} + +sqInt +ioSetCursorARGB(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY) +{ + return currentWindowSystem->setCursorARGB(cursorBitsIndex, extentX, extentY, offsetX, offsetY); +} + +sqInt +ioForceDisplayUpdate(void) +{ + return currentWindowSystem->forceDisplayUpdate(); +} + +sqInt +ioFormPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth, + double hScale, double vScale, sqInt landscapeFlag) +{ + return currentWindowSystem->formPrint(bitsAddr, width, height, depth, hScale, vScale, landscapeFlag); +} + +void +ioNoteDisplayChangedwidthheightdepth(void *b, int w, int h, int d) +{ + return currentWindowSystem->noteDisplayChangedWidthHeightDepth(b, w, h, d); +} + +sqInt +ioSetFullScreen(sqInt fullScreen) +{ + return currentWindowSystem->setFullScreen(fullScreen); +} + +sqInt +ioSetCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY) +{ + return currentWindowSystem->setCursor(cursorBitsIndex, offsetX, offsetY); +} + +sqInt +ioSetCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY) +{ + return currentWindowSystem->setCursorWithMask(cursorBitsIndex, cursorMaskIndex, offsetX, offsetY); +} + +sqInt +ioShowDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth, + sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB) +{ + return currentWindowSystem->showDisplay(dispBitsIndex, width, height, depth, + affectedL, affectedR, affectedT, affectedB); +} + +sqInt +ioHasDisplayDepth(sqInt depth) +{ + return currentWindowSystem->hasDisplayDepth(depth); +} + +sqInt +ioSetDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag) +{ + return currentWindowSystem->setDisplayMode(width, height, depth, fullscreenFlag); +} + +char* +ioGetWindowLabel(void) +{ + return currentWindowSystem->getWindowLabel(); +} + +sqInt +ioSetWindowLabelOfSize(void *lblIndex, sqInt sz) +{ + return currentWindowSystem->setWindowLabelOfSize(lblIndex, sz); +} + +sqInt +ioGetWindowWidth(void) +{ + return currentWindowSystem->getWindowWidth(); +} + +sqInt +ioGetWindowHeight(void) +{ + return currentWindowSystem->getWindowHeight(); +} + +sqInt +ioSetWindowWidthHeight(sqInt w, sqInt h) +{ + return currentWindowSystem->setWindowWidthHeight(w, h); +} + +sqInt +ioIsWindowObscured(void) +{ + return currentWindowSystem->isWindowObscured(); +} + +sqInt +ioGetNextEvent(sqInputEvent *evt) +{ + return currentWindowSystem->getNextEvent(evt); +} + +sqInt +ioGetButtonState(void) +{ + return currentWindowSystem->getButtonState(); +} + +sqInt +ioGetKeystroke(void) +{ + return currentWindowSystem->getKeystroke(); +} + +sqInt +ioMousePoint(void) +{ + return currentWindowSystem->mousePoint(); +} + +sqInt +ioPeekKeystroke(void) +{ + return currentWindowSystem->peekKeystroke(); +} + +sqInt +ioProcessEvents(void) +{ + if(ioProcessEventsHandler) + ioProcessEventsHandler(); + sqInt res = currentWindowSystem->processEvents(); + aioPoll(0); + return res; +} + +double +ioScreenScaleFactor(void) +{ + return currentWindowSystem->screenScaleFactor(); +} + +sqInt +ioScreenSize(void) +{ + return currentWindowSystem->screenSize(); +} + +sqInt +ioScreenDepth(void) +{ + return currentWindowSystem->screenDepth(); +} + +sqInt +clipboardSize(void) +{ + return currentWindowSystem->clipboardSize(); +} + +sqInt +clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + return currentWindowSystem->clipboardReadIntoAt(count, byteArrayIndex, startIndex); +} + +sqInt +clipboardWriteFromAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + return currentWindowSystem->clipboardWriteFromAt(count, byteArrayIndex, startIndex); +} + +sqInt +dropInit (void) +{ + return currentWindowSystem->dropInit(); +} + +sqInt +dropShutdown (void) +{ + return currentWindowSystem->dropShutdown(); +} + +char* +dropRequestFileName(sqInt dropIndex) +{ + return currentWindowSystem->dropRequestFileName(dropIndex); +} + +sqInt +dropRequestFileHandle(sqInt dropIndex) +{ + return currentWindowSystem->dropRequestFileHandle(dropIndex); +} + +sqInt +sqSecFileAccessCallback(void *callback) +{ + return 0; +} + +void +sqSetNumberOfDropFiles(sqInt numberOfFiles) +{ +} + +void +sqSetFileInformation(sqInt dropIndex, void *dropFile) +{ +} diff --git a/platforms/minheadless/common/sqWindow-Null.c b/platforms/minheadless/common/sqWindow-Null.c new file mode 100644 index 0000000000..f9dce6d3c8 --- /dev/null +++ b/platforms/minheadless/common/sqWindow-Null.c @@ -0,0 +1,271 @@ +#include +#include +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "sqWindow.h" +#include "config.h" + +static sqInt currentDisplayWidth; +static sqInt currentDisplayHeight; +static sqInt currentDisplayDepth; +static sqInt currentDisplayFullscreenFlag; + +static sqInt screenWidth = 1920; +static sqInt screenHeight = 1080; +static sqInt screenDepth = 32; + +static void +sqNull_initialize(void) +{ +} + +static void +sqNull_shutdown(void) +{ +} + +static sqInt +sqNull_setCursorARGB(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY) +{ + return false; +} + +static sqInt +sqNull_forceDisplayUpdate(void) +{ + return 0; +} + +static sqInt +sqNull_formPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth, + double hScale, double vScale, sqInt landscapeFlag) +{ + return 0; +} + +static void +sqNull_noteDisplayChangedWidthHeightDepth(void *b, int w, int h, int d) +{ +} + +static sqInt +sqNull_setFullScreen(sqInt fullScreen) +{ + return 0; +} + +static sqInt +sqNull_setCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY) +{ + return 0; +} + +static sqInt +sqNull_setCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY) +{ + return 0; +} + +static sqInt +sqNull_showDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth, + sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB) +{ + return 0; +} + +static sqInt +sqNull_hasDisplayDepth(sqInt depth) +{ + return true; +} + +static sqInt +sqNull_setDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag) +{ + currentDisplayWidth = width; + currentDisplayHeight = height; + currentDisplayDepth = depth; + currentDisplayFullscreenFlag = fullDisplayUpdate(); + + return 0; +} + +static char* +sqNull_getWindowLabel(void) +{ + return ""; +} + +static sqInt +sqNull_setWindowLabelOfSize(void *lblIndex, sqInt sz) +{ + return 0; +} + +static sqInt +sqNull_getWindowWidth(void) +{ + return currentDisplayWidth; +} + +static sqInt +sqNull_getWindowHeight(void) +{ + return currentDisplayHeight; +} + +static sqInt +sqNull_setWindowWidthHeight(sqInt w, sqInt h) +{ + currentDisplayWidth = w; + currentDisplayHeight = h; + return 0; +} + +static sqInt +sqNull_isWindowObscured(void) +{ + return false; +} + +/* Events */ +static sqInt +sqNull_getNextEvent(sqInputEvent *evt) +{ + evt->type = EventTypeNone; + return 0; +} + +static sqInt +sqNull_getButtonState(void) +{ + return 0; +} + +static sqInt +sqNull_getKeystroke(void) +{ + return 0; +} + +static sqInt +sqNull_mousePoint(void) +{ + return 0; +} + +static sqInt +sqNull_peekKeystroke(void) +{ + return 0; +} + +static sqInt +sqNull_processEvents(void) +{ + return 0; +} + +static double +sqNull_screenScaleFactor(void) +{ + return (double)screenWidth / (double)screenHeight; +} + +static sqInt +sqNull_screenSize(void) +{ + return screenWidth | (screenHeight << 16); +} + +static sqInt +sqNull_screenDepth(void) +{ + return screenDepth; +} + +/* Clipboard */ +static sqInt +sqNull_clipboardSize(void) +{ + return 0; +} + +static sqInt +sqNull_clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + return 0; +} + +static sqInt +sqNull_clipboardWriteFromAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + return 0; +} + +/* Drag/Drop*/ +extern sqInt nilObject(void); + +static sqInt +sqNull_dropInit (void) +{ + return 1; +} + +static sqInt +sqNull_dropShutdown (void) +{ + return 1; +} + +static char* +sqNull_dropRequestFileName(sqInt dropIndex) +{ + return 0; +} + +static sqInt +sqNull_dropRequestFileHandle(sqInt dropIndex) +{ + return nilObject(); +} + +sqWindowSystem sqNullWindowSystem = { + .name = "null", + + .initialize = sqNull_initialize, + .shutdown = sqNull_shutdown, + .setCursorARGB = sqNull_setCursorARGB, + .forceDisplayUpdate = sqNull_forceDisplayUpdate, + .formPrint = sqNull_formPrint, + .noteDisplayChangedWidthHeightDepth = sqNull_noteDisplayChangedWidthHeightDepth, + .setFullScreen = sqNull_setFullScreen, + .setCursor = sqNull_setCursor, + .setCursorWithMask = sqNull_setCursorWithMask, + .showDisplay = sqNull_showDisplay, + .hasDisplayDepth = sqNull_hasDisplayDepth, + .setDisplayMode = sqNull_setDisplayMode, + .getWindowLabel = sqNull_getWindowLabel, + .setWindowLabelOfSize = sqNull_setWindowLabelOfSize, + .getWindowWidth = sqNull_getWindowWidth, + .getWindowHeight = sqNull_getWindowHeight, + .setWindowWidthHeight = sqNull_setWindowWidthHeight, + .isWindowObscured = sqNull_isWindowObscured, + .getNextEvent = sqNull_getNextEvent, + .getButtonState = sqNull_getButtonState, + .getKeystroke = sqNull_getKeystroke, + .mousePoint = sqNull_mousePoint, + .peekKeystroke = sqNull_peekKeystroke, + .processEvents = sqNull_processEvents, + .screenScaleFactor = sqNull_screenScaleFactor, + .screenSize = sqNull_screenSize, + .screenDepth = sqNull_screenDepth, + .clipboardSize = sqNull_clipboardSize, + .clipboardReadIntoAt = sqNull_clipboardReadIntoAt, + .clipboardWriteFromAt = sqNull_clipboardWriteFromAt, + .dropInit = sqNull_dropInit, + .dropShutdown = sqNull_dropShutdown, + .dropRequestFileName = sqNull_dropRequestFileName, + .dropRequestFileHandle = sqNull_dropRequestFileHandle, +}; diff --git a/platforms/minheadless/common/sqWindow.h b/platforms/minheadless/common/sqWindow.h new file mode 100644 index 0000000000..af87af6039 --- /dev/null +++ b/platforms/minheadless/common/sqWindow.h @@ -0,0 +1,81 @@ +#ifndef SQUEAK_WINDOW_H +#define SQUEAK_WINDOW_H + +/** + * Window system interface. + */ +typedef struct +{ + const char *name; + void *primitives; + + void (*initialize)(void); + + void (*shutdown)(void); + + sqInt (*setCursorARGB)(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY); + + sqInt (*forceDisplayUpdate)(void); + + sqInt (*formPrint)(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth, + double hScale, double vScale, sqInt landscapeFlag); + + void (*noteDisplayChangedWidthHeightDepth)(void *b, int w, int h, int d); + + sqInt (*setFullScreen)(sqInt fullScreen); + + sqInt (*setCursor)(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY); + + sqInt (*setCursorWithMask)(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY); + + sqInt (*showDisplay)(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth, + sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB); + + sqInt (*hasDisplayDepth)(sqInt depth); + + sqInt (*setDisplayMode)(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag); + + char* (*getWindowLabel)(void); + + sqInt (*setWindowLabelOfSize)(void *lblIndex, sqInt sz); + + sqInt (*getWindowWidth)(void); + + sqInt (*getWindowHeight)(void); + + sqInt (*setWindowWidthHeight)(sqInt w, sqInt h); + + sqInt (*isWindowObscured)(void); + + sqInt (*getNextEvent)(sqInputEvent *evt); + + sqInt (*getButtonState)(void); + + sqInt (*getKeystroke)(void); + + sqInt (*mousePoint)(void); + + sqInt (*peekKeystroke)(void); + + sqInt (*processEvents)(void); + + double (*screenScaleFactor)(void); + + sqInt (*screenSize)(void); + + sqInt (*screenDepth)(void); + + sqInt (*clipboardSize)(void); + + sqInt (*clipboardReadIntoAt)(sqInt count, sqInt byteArrayIndex, sqInt startIndex); + + sqInt (*clipboardWriteFromAt)(sqInt count, sqInt byteArrayIndex, sqInt startIndex); + + sqInt (*dropInit) (void); + sqInt (*dropShutdown) (void); + + char* (*dropRequestFileName)(sqInt dropIndex); + sqInt (*dropRequestFileHandle)(sqInt dropIndex); +} sqWindowSystem; + +#endif /* SQUEAK_WINDOW_H */ diff --git a/platforms/minheadless/common/sqaio.h b/platforms/minheadless/common/sqaio.h new file mode 100644 index 0000000000..c2b5a74a60 --- /dev/null +++ b/platforms/minheadless/common/sqaio.h @@ -0,0 +1,125 @@ +/* sqaio.h -- asynchronous file i/o + * + * Copyright (C) 1996-2004 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* author: ian.piumarta@inria.fr + */ + +#ifndef __sqaio_h +#define __sqaio_h + + +#define AIO_X (1<<0) /* handle for exceptions */ +#define AIO_R (1<<1) /* handle for read */ +#define AIO_W (1<<2) /* handle for write */ +#define AIO_SEQ (1<<3) /* call handlers sequentially */ +#define AIO_EXT (1<<4) /* external fd -- don't close on aio shutdown */ + +#define AIO_RW (AIO_R | AIO_W) +#define AIO_RX (AIO_R | AIO_X) +#define AIO_WX (AIO_W | AIO_X) + +#define AIO_RWX (AIO_R | AIO_W | AIO_X) + +extern void aioInit(void); +extern void aioFini(void); + +/* Initialise `fd' for handling by AIO. `flags' can be 0 (aio takes + * over the descriptor entirely and the application should not assume + * anything about its subsequent behaviour) or AIO_EXT (aio will never + * set NBIO on `fd' or close it on behalf of the client). + */ +extern void aioEnable(int fd, void *clientData, int flags); + +/* Declare an interest in one or more events on `fd'. `mask' can be + * any combination in AIO_[R][W][X]. `handlerFn' will be called the + * next time any event in `mask' arrives on `fd' and will receive + * `fd', the original `clientData' (see aioEnable) and a `flag' + * containing ONE of AIO_{R,W,X} indicating which event occurred. In + * the event that the same handler is set for multiple events (either + * by setting more than one bit in `mask', or by calling aioHandle + * several times with different `mask's) and several events arrive + * simultaneously for the descriptor, then `handlerFn' is called + * multiple times -- once for each event in `mask'. The `handlerFn' + * will NOT be called again for the same event until the client calls + * aioHandle with an appropriate `mask' (the handled event is removed + * implicitly from the current mask before calling `handlerFn') . + * (Calls to aioHandle are cumulative: successive `mask's are ORed + * with the mask currently in effect for `fd'.) + */ +typedef void (*aioHandler)(int fd, void *clientData, int flag); +extern void aioHandle(int fd, aioHandler handlerFn, int mask); + +/* Suspend handling of the events in `mask' for `fd'. + */ +extern void aioSuspend(int fd, int mask); + +/* Disable further AIO handling of `fd'. The descriptor is reset to its + * default state (w.r.t. NBIO, etc.) but is NOT closed. + */ +extern void aioDisable(int fd); + +/* Sleep for at most `microSeconds'. Any event(s) arriving for + * handled fd(s) will terminate the sleep, with the appropriate + * handler(s) being called before returning. + */ +extern long aioPoll(long microSeconds); + +/* As above, but avoid sleeping in select() if microSeconds is small + * (less than a timeslice). Handlers are called, if neccessary, at + * the start and end of the sleep. + */ +extern long aioSleepForUsecs(long microSeconds); + +extern unsigned volatile long long ioUTCMicroseconds(void); +extern unsigned volatile long long ioUTCMicrosecondsNow(void); + +/* debugging stuff. */ +#ifdef AIO_DEBUG +# ifdef ACORN +# define FPRINTF(s) \ + do { \ + extern os_error privateErr; \ + extern void platReportError(os_error *e); \ + privateErr.errnum = (bits)0; \ + sprintf s; \ + platReportError((os_error *)&privateErr); \ + } while (0) +# else /* !ACORN */ + extern long aioLastTick, aioThisTick, ioMSecs(void); + extern const char *__shortFileName(const char *); +# define FPRINTF(X) do { \ + aioThisTick = ioMSecs(); \ + fprintf(stderr, "%8ld %4ld %s:%d ", aioThisTick, aioThisTick - aioLastTick,\ + __shortFileName(__FILE__),__LINE__); \ + aioLastTick = aioThisTick; \ + fprintf X; } while (0) +# endif /* ACORN */ +#else /* !DEBUG */ +# define FPRINTF(X) +#endif + +#endif /* __sqaio_h */ diff --git a/platforms/minheadless/common/version.c b/platforms/minheadless/common/version.c new file mode 100644 index 0000000000..35224777cf --- /dev/null +++ b/platforms/minheadless/common/version.c @@ -0,0 +1,25 @@ +/* Two versioning facilities in one file. If VERSION_PROGRAM is + * defined as non-zero then this will run the main in + * platforms/Cross/vm/sqSCCSVersion.h which will print various version + * info. Otherewise this defines vmBuildString with the current + * compiler. + */ +#if VERSION_PROGRAM +# include "sqSCCSVersion.h" +#else +# if !defined __VERSION__ +# define __VERSION__ "Unknown" +# endif + +# if !defined(TZ) +# define TZ "" +# define SPACER "" +# else +# define SPACER " " +# endif + +char vmBuildString[] = \ + "Mac OS X built on " \ + __DATE__" "__TIME__ SPACER TZ \ + " Compiler: " __VERSION__; +#endif /* VERSION_PROGRAM */ diff --git a/platforms/minheadless/config.h.in b/platforms/minheadless/config.h.in new file mode 100644 index 0000000000..df654455ab --- /dev/null +++ b/platforms/minheadless/config.h.in @@ -0,0 +1,192 @@ +/* config.h.in -- template for config.h -*- C -*- + * + * Copyright (C) 1996-2007 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + /* Author: Ian.Piumarta@squeakland.org + * + * Last edited: 2016-11-24 by Ronie Salgado - Adapted to CMake + */ + +#ifndef __sq_config_h +#define __sq_config_h + +/* explicit image width */ + +#cmakedefine HAVE_INTERP_H 1 + +/* package options */ + +#cmakedefine USE_X11 1 +#cmakedefine USE_X11_GLX 1 +#cmakedefine USE_QUARTZ 1 +#cmakedefine USE_QUARTZ_CGL 1 +#cmakedefine USE_RFB 1 + +/* libraries */ + +#cmakedefine HAVE_LIBX11 1 +#cmakedefine HAVE_LIBXEXT 1 +#cmakedefine HAVE_LIBDL 1 +#cmakedefine HAVE_DYLD 1 +#cmakedefine HAVE_LIBFFI 1 +#cmakedefine HAVE_ICONV 1 + +#cmakedefine USE_AUDIO_NONE 1 +#cmakedefine USE_AUDIO_SUN 1 +#cmakedefine USE_AUDIO_NAS 1 +#cmakedefine USE_AUDIO_OSS 1 +#cmakedefine USE_AUDIO_MACOSX 1 +#cmakedefine OSS_DEVICE 1 + +/* header files */ + +#cmakedefine HAVE_UNISTD_H 1 +#cmakedefine NEED_GETHOSTNAME_P 1 + +#cmakedefine HAVE_DIRENT_H 1 +#cmakedefine HAVE_SYS_NDIR_H 1 +#cmakedefine HAVE_SYS_DIR_H 1 +#cmakedefine HAVE_NDIR_H 1 +#cmakedefine HAVE_DLFCN_H 1 +#cmakedefine HAVE_ICONV_H 1 + +#cmakedefine HAVE_SYS_TIME_H 1 +#cmakedefine TIME_WITH_SYS_TIME 1 + +#cmakedefine HAVE_SYS_FILIO_H 1 + +#cmakedefine HAVE_SYS_AUDIOIO_H 1 +#cmakedefine HAVE_SUN_AUDIOIO_H 1 + +#cmakedefine HAVE_PTY_H 1 +#cmakedefine HAVE_UTIL_H 1 +#cmakedefine HAVE_LIBUTIL_H 1 +#cmakedefine HAVE_STROPTS_H 1 + +#cmakedefine HAVE_GL_GL_H 1 +#cmakedefine HAVE_OPENGL_GL_H 1 + +#cmakedefine NEED_SUNOS_H 1 + +/* system calls/library functions */ + +#cmakedefine AT_EXIT 1 + +#cmakedefine HAVE_TZSET 1 + +#cmakedefine HAVE_OPENPTY 1 +#cmakedefine HAVE_UNIX98_PTYS 1 + +#cmakedefine HAVE_SNPRINTF 1 +#cmakedefine HAVE___SNPRINTF 1 + +#cmakedefine HAVE_MMAP 1 + +#cmakedefine HAVE_DYLD 1 + +#cmakedefine HAVE_LANGINFO_CODESET 1 + +#cmakedefine HAVE_ALLOCA 1 +#cmakedefine HAVE_ALLOCA_H 1 + +#cmakedefine HAVE_KQUEUE 1 +#cmakedefine HAVE_SELECT 1 +#cmakedefine HAVE_EPOLL 1 +#cmakedefine HAVE_EPOLL_PWAIT 1 + +#cmakedefine HAVE_UNSETENV 1 + +#cmakedefine HAVE_NANOSLEEP 1 + +/* widths of primitive types */ + +#define SIZEOF_INT @SIZEOF_INT@ +#define SIZEOF_LONG @SIZEOF_LONG@ +#define SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@ +#define SIZEOF_VOID_P @SIZEOF_VOID_P@ + +/* structures */ + +#cmakedefine HAVE_TM_GMTOFF +#cmakedefine HAVE_TIMEZONE + +/* window systems */ +#cmakedefine SUPPORT_TRADITIONAL_DISPLAY +#cmakedefine HAVE_SDL2 + +/* typedefs */ + +#define squeakInt64 @SQUEAK_INT64_TYPEDEF@ + +/* architecture */ + +#define OS_TYPE "@OS_TYPE@" + +#define VM_HOST "@VM_TARGET@" +#define VM_HOST_CPU "@VM_TARGET_CPU@" +#define VM_HOST_OS "@VM_TARGET_OS@" +#define VM_TARGET "@VM_TARGET@" +#define VM_TARGET_CPU "@VM_TARGET_CPU@" +#define VM_TARGET_OS "@VM_TARGET_OS@" + +#if defined(_MSC_VER) +#define VM_BUILD_STRING VM_NAME " built for " VM_TARGET_OS " on "__DATE__ " "__TIME__" Compiler: Visual C" +#else +#define VM_BUILD_STRING VM_NAME " built for " VM_TARGET_OS " on "__DATE__ " "__TIME__" Compiler: "__VERSION__ +#endif + +#cmakedefine WORDS_BIGENDIAN +#undef DOUBLE_WORD_ALIGNMENT + +/* damage containment */ + +#cmakedefine DARWIN 1 + +#ifdef _WIN32 +# ifndef WIN32 +# define WIN32 1 +# endif +#endif + +#ifdef NEED_SUNOS_H +# include "sunos.h" +#endif + +/* other configured variables */ + +#cmakedefine VM_MODULE_PREFIX +#cmakedefine VM_DLSYM_PREFIX +#cmakedefine VM_X11DIR + +/* avoid dependencies on glibc2.3 */ + +#cmakedefine HAVE_FEATURES_H 1 + +#if defined(HAVE_FEATURES_H) +# include "glibc.h" +#endif + +#endif /* __sq_config_h */ diff --git a/platforms/minheadless/generic/sqPlatformSpecific-Generic.c b/platforms/minheadless/generic/sqPlatformSpecific-Generic.c new file mode 100644 index 0000000000..6530fa910d --- /dev/null +++ b/platforms/minheadless/generic/sqPlatformSpecific-Generic.c @@ -0,0 +1,270 @@ +#include +#include +#include "sq.h" +#include "sqMemoryAccess.h" +#include "config.h" + +void +ioInitPlatformSpecific(void) +{ +} + +void +ioInitTime(void) +{ +} + +void +ioInitThreads(void) +{ +} + +void +aioInit(void) +{ +} + +sqInt +amInVMThread(void) +{ + return false; +} + +int +isCFramePointerInUse(void) +{ + return true; +} + +int +osCogStackPageHeadroom(void) +{ + return 1024; +} + +long +ioMSecs(void) +{ + return 0; +} + +long +ioMicroMSecs(void) +{ + return 0; +} + +unsigned volatile long long +ioUTCMicrosecondsNow() +{ + return 0; +} + +unsigned volatile long long +ioUTCMicroseconds() +{ + return 0; +} + +unsigned volatile long long +ioLocalMicrosecondsNow() +{ + return 0; +} + +unsigned volatile long long +ioLocalMicroseconds() +{ + return 0; +} + +unsigned long long +ioUTCStartMicroseconds() +{ + return 0; +} + +sqInt +ioLocalSecondsOffset() +{ + return 0; +} + +void +ioUpdateVMTimezone() +{ +} + +# if ITIMER_HEARTBEAT /* Hack; allow heartbeat to avoid */ +int numAsyncTickees; /* prodHighPriorityThread unless necessary */ +# endif /* see platforms/unix/vm/sqUnixHeartbeat.c */ + +void +ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *runInNOutp, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip) +{ +} + +/* this function should return the value of the high performance + counter if there is such a thing on this platform (otherwise return 0) */ +sqLong +ioHighResClock(void) +{ + return 0; +} + +/* New filename converting function; used by the interpreterProxy function + ioFilenamefromStringofLengthresolveAliases. Most platforms can ignore the + resolveAlias boolean - it seems to only be of use by OSX but is crucial there. +*/ +sqInt +sqGetFilenameFromString(char * aCharBuffer, char * aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + memcpy(aCharBuffer, aFilenameString, filenameLength); + aCharBuffer[filenameLength] = 0; + return 0; +} + +sqInt +ioBeep(void) +{ + return 0; +} + +sqInt +ioExit(void) +{ + exit(0); +} + +sqInt +ioExitWithErrorCode(int errorCode) +{ + exit(errorCode); +} + +sqInt +crashInThisOrAnotherThread(sqInt flags) +{ + abort(); +} + +sqInt +ioRelinquishProcessorForMicroseconds(sqInt microSeconds) +{ + return 0; +} + +sqInt +ioSeconds(void) +{ + return 0; +} + +sqInt +ioSecondsNow(void) +{ + return time(NULL); +} + +void +ioInitHeartbeat(void) +{ +} + +int +ioHeartbeatMilliseconds(void) +{ + return 0; +} + +void +ioSetHeartbeatMilliseconds(int milliseconds) +{ +} + +unsigned long +ioHeartbeatFrequency(int frequency) +{ + return 0; +} + +void +ioProfileStatus(sqInt *running, void **exestartpc, void **exelimitpc, + void **vmhst, long *nvmhbin, void **eahst, long *neahbin) +{ +} + +void +ioControlProfile(int on, void **vhp, long *nvb, void **ehp, long *neb) +{ +} + +long +ioControlNewProfile(int on, unsigned long buffer_size) +{ + return 0; +} + +void +ioNewProfileStatus(sqInt *running, long *buffersize) +{ +} + +long +ioNewProfileSamplesInto(void *sampleBuffer) +{ + return 0; +} + +void +ioClearProfile(void) +{ +} + +sqInt +ioDisablePowerManager(sqInt disableIfNonZero) +{ + return true; +} + +static int +isAbsolutePath(const char *path) +{ +#ifdef _WIN32 + return *path == '\\' || (path[0] != 0 && path[1] == ':'); +#else + /* Assume UNIX style path. */ + return *path == '/'; +#endif +} + +void +findExecutablePath(const char *localVmName, char *dest, size_t destSize) +{ + const char *lastSeparator = strrchr(localVmName, '/'); +#ifdef _WIN32 + const char *lastSeparator2 = strrchr(localVmName, '\\'); + if(!lastSeparator || lastSeparator < lastSeparator2) + lastSeparator = lastSeparator2; +#endif + + if(!isAbsolutePath(localVmName)) + { + /* TODO: Get the current working directory*/ + strcpy(dest, "./"); + } + + if(lastSeparator) + strncat(dest, localVmName, lastSeparator - localVmName + 1); +} + +int +sqExecuteFunctionWithCrashExceptionCatching(sqFunctionThatCouldCrash function, void *userdata) +{ + return function(userdata); +} + +void *os_exports[][3]= +{ + { 0, 0, 0 } +}; diff --git a/platforms/minheadless/generic/sqPlatformSpecific-Generic.h b/platforms/minheadless/generic/sqPlatformSpecific-Generic.h new file mode 100644 index 0000000000..a9b98d3b65 --- /dev/null +++ b/platforms/minheadless/generic/sqPlatformSpecific-Generic.h @@ -0,0 +1 @@ +#error Unimplemented the Generic platform. diff --git a/platforms/minheadless/sdl2-window/sqWindow-SDL2.c b/platforms/minheadless/sdl2-window/sqWindow-SDL2.c new file mode 100644 index 0000000000..d234239aa5 --- /dev/null +++ b/platforms/minheadless/sdl2-window/sqWindow-SDL2.c @@ -0,0 +1,1186 @@ +/* sqWindows-SDL2.c -- Legacy display API backend using SDL2. + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Non-Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#include +#include +#include +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "sqEventCommon.h" +#include "sqWindow.h" +#include "config.h" + +extern int getSavedWindowSize(); + +typedef struct sqSDLEventQueue +{ + sqCircularQueueInfo info; + SDL_Event elements[SQ_EVENT_QUEUE_SIZE]; +} sqSDLEventQueue; + +#define sqSDLEventQueueIsEmpty(queue) sqQueueIsEmpty(queue, SQ_EVENT_QUEUE_SIZE) +#define sqSDLEventQueueIsFull(queue) sqQueueIsFull(queue, SQ_EVENT_QUEUE_SIZE) +#define sqSDLEventQueuePush(queue, value) sqQueuePush(queue, SQ_EVENT_QUEUE_SIZE, value) +#define sqSDLEventQueuePopInto(queue, result) sqQueuePopInto(queue, SQ_EVENT_QUEUE_SIZE, result) + +static sqInt sqSDL2_processEvents(void); + +static sqEventQueue eventQueue; +static sqSDLEventQueue sdlEventQueue; + +static SDL_Window *window; +static Uint32 windowID; +static SDL_Renderer *windowRenderer; +static SDL_Texture *windowTexture; +static SDL_Cursor *currentCursor; +static int windowTextureWidth; +static int windowTextureHeight; + +static int buttonState = 0; +static int modifiersState = 0; + +static int newSDLEvent = 0; +static int newDisplayEvent = 0; + +static int mousePositionX = 0; +static int mousePositionY = 0; + +static sqInt sdl2InputEventSemaIndex = 0; +static char droppedFileName[FILENAME_MAX]; + +static SDL_GLContext currentOpenGLContext = 0; +static SDL_Window *currentOpenGLWindow = 0; +static int openglStoreCount = 0; + +static void +storeOpenGLState(void) +{ + if(openglStoreCount == 0) + { + currentOpenGLContext = SDL_GL_GetCurrentContext(); + currentOpenGLWindow = SDL_GL_GetCurrentWindow(); + } + ++openglStoreCount; +} + +static void +restoreOpenGLState(void) +{ + --openglStoreCount; + if(openglStoreCount == 0) + { + SDL_GL_MakeCurrent(currentOpenGLWindow, currentOpenGLContext); + currentOpenGLContext = 0; + currentOpenGLWindow = 0; + } + + if(openglStoreCount < 0) + abort(); +} + +static sqInt +setSDL2InputSemaphoreIndex(sqInt semaIndex) +{ + if (semaIndex == 0) + success(false); + else + sdl2InputEventSemaIndex = semaIndex; + return true; +} + +static void +sdl2SignalInputEvent(void) +{ + if (sdl2InputEventSemaIndex > 0) + signalSemaphoreWithIndex(sdl2InputEventSemaIndex); +} + +static int +convertButton(int button) +{ +#ifdef __APPLE__ + // On OS X, swap the middle and right buttons. + switch(button) + { + case SDL_BUTTON_LEFT: return RedButtonBit; + case SDL_BUTTON_RIGHT: return BlueButtonBit; + case SDL_BUTTON_MIDDLE: return YellowButtonBit; + default: return 0; + } +#else + switch(button) + { + case SDL_BUTTON_LEFT: return RedButtonBit; + case SDL_BUTTON_MIDDLE: return BlueButtonBit; + case SDL_BUTTON_RIGHT: return YellowButtonBit; + default: return 0; + } +#endif + return 0; +} + +static int +convertModifiers(int state) +{ + int result = 0; + if(state & KMOD_SHIFT) + result |= ShiftKeyBit; + if(state & KMOD_CTRL) /* Alt-gr is received as RCtrl in some cases.*/ + result |= CtrlKeyBit; + if(state & KMOD_ALT) /* Right alt is used for grammar purposes. */ + result |= OptionKeyBit; + if(state & KMOD_GUI) + result |= CommandKeyBit; + return result; +} + +static int +convertSpecialKeySymToCharacter(int symbol) +{ + switch(symbol) + { + case SDLK_RETURN: return '\r'; + case SDLK_BACKSPACE: return 8; + case SDLK_TAB: return '\t'; + case SDLK_HOME: return 1; + case SDLK_LEFT: return 28; + case SDLK_UP: return 30; + case SDLK_RIGHT: return 29; + case SDLK_DOWN: return 31; + case SDLK_END: return 4; + case SDLK_INSERT: return 5; + case SDLK_PAGEUP: return 11; + case SDLK_PAGEDOWN: return 12; + case SDLK_DELETE: return 127; + default: + return 0; + } + +} + +static int +convertKeySymToCharacter(int symbol) +{ + if(symbol >= 0x400000) + return 0; + else + return symbol; +} + +static void +sqSDL2_initialize(void) +{ + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE); +} + +static void +sqSDL2_shutdown(void) +{ + SDL_Quit(); +} + +static void +createWindow(sqInt width, sqInt height, sqInt fullscreenFlag) +{ + int flags; + int actualWindowX, actualWindowY; + int actualWindowWidth, actualWindowHeight; + SDL_Rect displayBounds; + + if(window) + return; + + storeOpenGLState(); + flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI; + if(fullscreenFlag) + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + + modifiersState = convertModifiers(SDL_GetModState()); + window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags); + if(!window) + { + restoreOpenGLState(); + return; + } + + if(!fullscreenFlag) + { + SDL_GetWindowPosition(window, &actualWindowX, &actualWindowY); + SDL_GetWindowSize(window, &actualWindowWidth, &actualWindowHeight); +#if SDL_VERSION_ATLEAST(2, 5, 0) + SDL_GetDisplayUsableBounds(0, &displayBounds); +#else + SDL_GetDisplayBounds(0, &displayBounds); +#endif + if(actualWindowWidth + actualWindowX >= displayBounds.w || actualWindowHeight + actualWindowY >= displayBounds.h) + SDL_MaximizeWindow(window); + } + + windowID = SDL_GetWindowID(window); + windowRenderer = SDL_CreateRenderer(window, 0, 0); + restoreOpenGLState(); +} + +static int +ensureTextureOfSize(sqInt width, sqInt height) +{ + if(windowTexture && windowTextureWidth == width && windowTextureHeight == height) + return 0; + + storeOpenGLState(); + if(windowTexture) + SDL_DestroyTexture(windowTexture); + + windowTexture = SDL_CreateTexture(windowRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + windowTextureWidth = width; + windowTextureHeight = height; + restoreOpenGLState(); + return 1; +} + +static void +presentWindow() +{ + if(!window || !windowRenderer) + return; + + storeOpenGLState(); + SDL_SetRenderDrawColor(windowRenderer, 0, 0, 0, 0); + SDL_RenderClear(windowRenderer); + + if(windowTexture) + SDL_RenderCopy(windowRenderer, windowTexture, NULL, NULL); + + SDL_RenderPresent(windowRenderer); + restoreOpenGLState(); +} + +static void +recordSDLEvent(const SDL_Event *rawEvent) +{ + newSDLEvent = 1; + sqSDLEventQueuePush(sdlEventQueue, *rawEvent); +} + +static void +recordEvent(sqEventUnion *event) +{ + newDisplayEvent = 1; + sqEventQueuePush(eventQueue, *event); +} + +static void +recordLowImportanceEvent(sqEventUnion *event) +{ + if(sqEventQueueIsFull(eventQueue)) + return; + + recordEvent(event); +} + +static void +recordMouseWheel(int keyCode) +{ + int modifiers = modifiersState ^ CtrlKeyBit; + + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.pressCode = EventKeyDown; + event.key.charCode = keyCode; + event.key.utf32Code = keyCode; + event.key.modifiers = modifiers; + recordEvent(&event); + } + + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.pressCode = EventKeyChar; + event.key.charCode = keyCode; + event.key.utf32Code = keyCode; + event.key.modifiers = modifiers; + recordEvent(&event); + } + + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.pressCode = EventKeyUp; + event.key.charCode = keyCode; + event.key.utf32Code = keyCode; + event.key.modifiers = modifiers; + recordEvent(&event); + } +} + +static void +handleMouseWheel(const SDL_Event *rawEvent) +{ + if(rawEvent->wheel.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + if(rawEvent->wheel.x < 0) + { + recordMouseWheel(28); + } + else if(rawEvent->wheel.x > 0) + { + recordMouseWheel(29); + } + + if(rawEvent->wheel.y > 0) + { + recordMouseWheel(30); + } + else if(rawEvent->wheel.y < 0) + { + recordMouseWheel(31); + } +} + +static void +handleKeyDown(const SDL_Event *rawEvent) +{ + int character; + int isSpecial; + int hasRightAlt; + + hasRightAlt = (rawEvent->key.keysym.mod & KMOD_RALT) != 0; + modifiersState = convertModifiers(rawEvent->key.keysym.mod); + if(rawEvent->key.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + character = convertSpecialKeySymToCharacter(rawEvent->key.keysym.sym); + isSpecial = character != 0; + if(!character) + character = convertKeySymToCharacter(rawEvent->key.keysym.sym); + + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.timeStamp = rawEvent->key.timestamp; + event.key.pressCode = EventKeyDown; + event.key.charCode = rawEvent->key.keysym.sym; + event.key.utf32Code = character; + event.key.modifiers = modifiersState; + recordEvent(&event); + } + + /* We need to send a key stroke for some special circumstances. */ + if(!isSpecial && (!modifiersState || modifiersState == ShiftKeyBit || hasRightAlt)) + return; + + if(character && character != 27) + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.timeStamp = rawEvent->key.timestamp; + event.key.pressCode = EventKeyChar; + event.key.charCode = character; + event.key.utf32Code = character; + event.key.modifiers = modifiersState; + recordEvent(&event); + } +} + +static void +handleKeyUp(const SDL_Event *rawEvent) +{ + modifiersState = convertModifiers(rawEvent->key.keysym.mod); + if(rawEvent->key.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.timeStamp = rawEvent->key.timestamp; + event.key.pressCode = EventKeyUp; + event.key.charCode = convertKeySymToCharacter(rawEvent->key.keysym.sym); + event.key.utf32Code = convertKeySymToCharacter(rawEvent->key.keysym.sym); + event.key.modifiers = modifiersState; + recordEvent(&event); +} + +static void +handleTextInput(const SDL_Event *rawEvent) +{ + int utf32; + const char *position; + + if(rawEvent->text.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.key.type = EventTypeKeyboard; + event.key.timeStamp = rawEvent->text.timestamp; + event.key.pressCode = EventKeyChar; + event.key.modifiers = modifiersState; + + position = rawEvent->text.text; + while(*position) + { + position = sqUTF8ToUTF32Iterate(position, &utf32); + if(!utf32) + break; + + event.key.charCode = utf32; + event.key.utf32Code = utf32; + recordEvent(&event); + } +} + +static void +handleMouseButtonDown(const SDL_Event *rawEvent) +{ + if(rawEvent->button.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + buttonState |= convertButton(rawEvent->button.button); + + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.mouse.type = EventTypeMouse; + event.mouse.timeStamp = rawEvent->button.timestamp; + event.mouse.x = mousePositionX = rawEvent->button.x; + event.mouse.y = mousePositionY = rawEvent->button.y; + event.mouse.buttons = buttonState; + event.mouse.modifiers = modifiersState; + event.mouse.nrClicks = rawEvent->button.clicks; + recordEvent(&event); +} + +static void +handleMouseButtonUp(const SDL_Event *rawEvent) +{ + if(rawEvent->button.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + buttonState &= ~convertButton(rawEvent->button.button); + + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.mouse.type = EventTypeMouse; + event.mouse.timeStamp = rawEvent->button.timestamp; + event.mouse.x = mousePositionX = rawEvent->button.x; + event.mouse.y = mousePositionY = rawEvent->button.y; + event.mouse.buttons = buttonState; + event.mouse.modifiers = modifiersState; + event.mouse.nrClicks = rawEvent->button.clicks; + recordEvent(&event); +} + +static void +handleMouseMotion(const SDL_Event *rawEvent) +{ + if(rawEvent->motion.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.mouse.type = EventTypeMouse; + event.mouse.timeStamp = rawEvent->motion.timestamp; + event.mouse.x = mousePositionX = rawEvent->motion.x; + event.mouse.y = mousePositionY = rawEvent->motion.y; + event.mouse.buttons = buttonState; + event.mouse.modifiers = modifiersState; + recordLowImportanceEvent(&event); +} + +static void +handleWindowEvent(const SDL_Event *rawEvent) +{ + if(rawEvent->window.windowID != windowID) + { + recordSDLEvent(rawEvent); + return; + } + + switch(rawEvent->window.event) + { + case SDL_WINDOWEVENT_CLOSE: + { + sqEventUnion event; + memset(&event, 0, sizeof(event)); + event.window.type = EventTypeWindow; + event.window.timeStamp = rawEvent->window.timestamp; + event.window.action = WindowEventClose; + recordEvent(&event); + } + break; + case SDL_WINDOWEVENT_MOVED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + case SDL_WINDOWEVENT_RESIZED: + { + sqEventUnion event; + SDL_Rect rect; + SDL_GetWindowPosition(window, &rect.x, &rect.y); + SDL_GetRendererOutputSize(windowRenderer, &rect.w, &rect.h); + memset(&event, 0, sizeof(event)); + event.window.type = EventTypeWindow; + event.window.timeStamp = rawEvent->window.timestamp; + event.window.action = WindowEventMetricChange; + event.window.value1 = rect.x; + event.window.value2 = rect.y; + event.window.value3 = rect.x + rect.w; + event.window.value4 = rect.y + rect.h; + recordEvent(&event); + } + break; + } +} + +static void +handleDropFileEvent(const SDL_Event *rawEvent) +{ + sqEventUnion event; + strcpy(droppedFileName, rawEvent->drop.file); + SDL_free(rawEvent->drop.file); + + /* TODO: Support dropping files here or in the image.*/ + { + event.dnd.type = EventTypeDragDropFiles; + event.dnd.timeStamp = rawEvent->window.timestamp; + event.dnd.dragType = SQDragDrop; + event.dnd.numFiles = 1; + event.dnd.x = mousePositionX; + event.dnd.y = mousePositionY; + event.dnd.modifiers = modifiersState; + recordEvent(&event); + } +} + +static void +handleEvent(const SDL_Event *event) +{ + switch(event->type) + { + case SDL_KEYDOWN: + handleKeyDown(event); + break; + case SDL_KEYUP: + handleKeyUp(event); + break; + case SDL_TEXTINPUT: + handleTextInput(event); + break; + case SDL_MOUSEBUTTONDOWN: + handleMouseButtonDown(event); + break; + case SDL_MOUSEBUTTONUP: + handleMouseButtonUp(event); + break; + case SDL_MOUSEMOTION: + handleMouseMotion(event); + break; + case SDL_MOUSEWHEEL: + handleMouseWheel(event); + break; + case SDL_WINDOWEVENT: + handleWindowEvent(event); + break; + case SDL_DROPFILE: + handleDropFileEvent(event); + break; + default: + /* Record the unhandled SDL events for the image. */ + recordSDLEvent(event); + break; + } +} + +static void +handleEvents() +{ + SDL_Event event; + while(SDL_PollEvent(&event)) + handleEvent(&event); + + if(newDisplayEvent) + ioSignalInputEvent(); + if(newSDLEvent) + sdl2SignalInputEvent(); + + newDisplayEvent = 0; + newSDLEvent = 0; +} + +static sqInt +sqSDL2_setCursorARGB(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY) +{ + return false; +} + +static sqInt +sqSDL2_forceDisplayUpdate(void) +{ + presentWindow(); + return 0; +} + +static sqInt +sqSDL2_formPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth, + double hScale, double vScale, sqInt landscapeFlag) +{ + return 0; +} + +static void +sqSDL2_noteDisplayChangedWidthHeightDepth(void *b, int w, int h, int d) +{ +} + +static sqInt +sqSDL2_setFullScreen(sqInt fullScreen) +{ + return 0; +} + +static sqInt +sqSDL2_setCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY) +{ + return 0; +} + +static sqInt +sqSDL2_setCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY) +{ + SDL_Cursor *newCursor; + Uint8 convertedCursorBits[32]; + Uint8 convertedCursorMask[32]; + int i; + + unsigned int *cursorBits = (unsigned int*)pointerForOop(cursorBitsIndex); + unsigned int *cursorMask = (unsigned int*)pointerForOop(cursorMaskIndex); + + if (cursorMaskIndex == null) + cursorMask = cursorBits; + + /* Remove the extra padding */ + for(i = 0; i < 16; ++i) + { + convertedCursorBits[i*2 + 0]= (cursorBits[i] >> 24) & 0xFF; + convertedCursorBits[i*2 + 1]= (cursorBits[i] >> 16) & 0xFF; + convertedCursorMask[i*2 + 0]= (cursorMask[i] >> 24) & 0xFF; + convertedCursorMask[i*2 + 1]= (cursorMask[i] >> 16) & 0xFF; + } + + /* Create and set the new cursor. */ + newCursor = SDL_CreateCursor(convertedCursorBits, convertedCursorMask, 16, 16, -offsetX, -offsetY); + if(newCursor) + { + SDL_SetCursor(newCursor); + if(currentCursor) + SDL_FreeCursor(currentCursor); + currentCursor = newCursor; + } + + return 0; +} + +static void +blitRect32( + int surfaceWidth, int surfaceHeight, + uint8_t *sourcePixels, int sourcePitch, + uint8_t *destPixels, int destPitch, + int copyX, int copyY, int width, int height) +{ + int y; + + if(sourcePitch == destPitch && + surfaceWidth == width && surfaceHeight == height && copyX == 0 && copyY == 0) + { + memcpy(destPixels, sourcePixels, destPitch*height); + } + else if(sourcePitch == destPitch) + { + destPixels += copyY*destPitch; + sourcePixels += copyY*sourcePitch; + memcpy(destPixels, sourcePixels, destPitch*height); + } + else + { + int copyPitch = destPitch; + if(sourcePitch < copyPitch) + copyPitch = sourcePitch; + + destPixels += copyY*destPitch; + sourcePixels += copyY*sourcePitch; + + for(y = 0; y < height; ++y) + { + memcpy(destPixels, sourcePixels, copyPitch); + destPixels += destPitch; + sourcePixels += sourcePitch; + } + } +} + +static sqInt +sqSDL2_showDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth, + sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB) +{ + storeOpenGLState(); + if(!window) + createWindow(width, height, 0); + + SDL_Rect modifiedRect; + modifiedRect.x = affectedL; + modifiedRect.y = affectedT; + modifiedRect.w = affectedR - affectedL; + modifiedRect.h = affectedB - affectedT; + + /* Make sure the texture has the correct extent. */ + if(ensureTextureOfSize(width, height)) + { + /*If the texture was recreated, we have to upload the whole texture*/ + modifiedRect.x = 0; + modifiedRect.y = 0; + modifiedRect.w = width; + modifiedRect.h = height; + } + + if(!windowTexture) + { + restoreOpenGLState(); + return 0; + } + + uint8_t *pixels; + int pitch; + if(SDL_LockTexture(windowTexture, NULL, (void**)&pixels, &pitch)) + { + restoreOpenGLState(); + return 0; + } + + int sourcePitch = windowTextureWidth*4; + blitRect32(windowTextureWidth, windowTextureHeight, + (uint8_t*)pointerForOop(dispBitsIndex), sourcePitch, + pixels, pitch, + modifiedRect.x, modifiedRect.y, modifiedRect.w, modifiedRect.h + ); + + SDL_UnlockTexture(windowTexture); + presentWindow(); + restoreOpenGLState(); + return 0; +} + +static sqInt +sqSDL2_hasDisplayDepth(sqInt depth) +{ + return depth == 32; +} + +static sqInt +sqSDL2_setDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag) +{ + if(window) + { + storeOpenGLState(); + ioSetWindowWidthHeight(width, height); + ioSetFullScreen(fullscreenFlag); + restoreOpenGLState(); + return 0; + } + + storeOpenGLState(); + createWindow(width, height, fullscreenFlag); + restoreOpenGLState(); + return 0; +} + +static char* +sqSDL2_getWindowLabel(void) +{ + return (char*)SDL_GetWindowTitle(window); +} + +static sqInt +sqSDL2_setWindowLabelOfSize(void *lblIndex, sqInt size) +{ + char *buffer; + + buffer = (char*)malloc(size + 1); + memcpy(buffer, lblIndex, size); + buffer[size] = 0; + + SDL_SetWindowTitle(window, buffer); + + free(buffer); + return 0; +} + +static sqInt +sqSDL2_getWindowWidth(void) +{ + int width = 0; + int height = 0; + if(windowRenderer) + SDL_GetRendererOutputSize(windowRenderer, &width, &height); + return width; +} + +static sqInt +sqSDL2_getWindowHeight(void) +{ + int width = 0; + int height = 0; + if(windowRenderer) + SDL_GetRendererOutputSize(windowRenderer, &width, &height); + return height; +} + +static sqInt +sqSDL2_setWindowWidthHeight(sqInt w, sqInt h) +{ + if(window) + SDL_SetWindowSize(window, w, h); + return 0; +} + +static sqInt +sqSDL2_isWindowObscured(void) +{ + return false; +} + +/* Events */ +static sqInt +sqSDL2_getNextEvent(sqInputEvent *evt) +{ + if(sqEventQueueIsEmpty(eventQueue)) + { + evt->type = EventTypeNone; + } + else + { + sqEventQueuePopInto(eventQueue, (sqEventUnion*)evt); + } + + return 0; +} + +static sqInt +sqSDL2_getNextSDL2Event(void *buffer, size_t bufferSize) +{ + SDL_Event event; + size_t copySize; + + /* Retrieve the event from the queue. */ + if(sqSDLEventQueueIsEmpty(sdlEventQueue)) + return false; + + sqSDLEventQueuePopInto(sdlEventQueue, &event); + + /* Copy the event data into the buffer. */ + copySize = sizeof(SDL_Event); + if(bufferSize < copySize) + copySize = bufferSize; + memcpy(buffer, &event, copySize); + + return true; +} + +static sqInt +sqSDL2_getButtonState(void) +{ + ioProcessEvents(); + return buttonState | (modifiersState << 3); +} + +static sqInt +sqSDL2_getKeystroke(void) +{ + return 0; +} + +static sqInt +sqSDL2_mousePoint(void) +{ + ioProcessEvents(); + return (mousePositionX<<16) | mousePositionY; +} + +static sqInt +sqSDL2_peekKeystroke(void) +{ + return 0; +} + +static sqInt +sqSDL2_processEvents(void) +{ + handleEvents(); + return 0; +} + +static double +sqSDL2_screenScaleFactor(void) +{ + return 1.0; +} + +static sqInt +sqSDL2_screenSize(void) +{ + int width; + int height; + if(!windowRenderer) + return getSavedWindowSize(); + + SDL_GetRendererOutputSize(windowRenderer, &width, &height); + return height | (width << 16); +} + +static sqInt +sqSDL2_screenDepth(void) +{ + return 32; +} + +/* Clipboard */ +static sqInt +sqSDL2_clipboardSize(void) +{ + if(!SDL_HasClipboardText()) + return 0; + + return strlen(SDL_GetClipboardText()); +} + +static sqInt +sqSDL2_clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + sqInt clipSize; + char *clipboardText; + + clipboardText = SDL_GetClipboardText(); + if(!clipboardText) + clipboardText = ""; + + clipSize = count; + if(count < clipSize) + clipSize = count; + + memcpy(pointerForOop(byteArrayIndex + startIndex), (void *)clipboardText, clipSize); + return clipSize; +} + +static sqInt +sqSDL2_clipboardWriteFromAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex) +{ + char *buffer; + + buffer = (char*)malloc(count + 1); + memcpy(buffer, pointerForOop(byteArrayIndex + startIndex), count); + buffer[count] = 0; + + SDL_SetClipboardText(buffer); + + free(buffer); + return 0; +} + +/* Drag/Drop*/ +#include "FilePlugin.h" +extern SQFile * fileValueOf(sqInt objectPointer); +extern sqInt classByteArray(void); +extern sqInt instantiateClassindexableSize(sqInt classPointer, sqInt size); +extern sqInt nilObject(void); + +static usqIntptr_t +fileRecordSize(void) +{ + return sizeof(SQFile); +} + +static sqInt +sqSDL2_dropInit (void) +{ + return 1; +} + +static sqInt +sqSDL2_dropShutdown (void) +{ + return 1; +} + +static char* +sqSDL2_dropRequestFileName(sqInt dropIndex) +{ + if(dropIndex == 1) + return droppedFileName; + return NULL; +} + +static sqInt +sqSDL2_dropRequestFileHandle(sqInt dropIndex) +{ + if(droppedFileName[0] && dropIndex == 1) + { + // you cannot be serious? + sqInt handle = instantiateClassindexableSize(classByteArray(), fileRecordSize()); + sqFileOpen(fileValueOf(handle), droppedFileName, strlen(droppedFileName), 0); + return handle; + } + + return nilObject(); +} + +/* SDL2 primitives*/ +extern sqInt argumentCountOf(sqInt methodPointer); +extern sqInt failed(void); +extern sqInt pop(sqInt nItems); +extern sqInt popthenPush(sqInt nItems, sqInt oop); +extern sqInt primitiveMethod(void); +extern sqInt stackIntegerValue(sqInt offset); +extern sqInt stackValue(sqInt offset); +extern sqInt trueObject(void); +extern sqInt falseObject(void); +extern void *firstIndexableField(sqInt oop); + +static sqInt +primitiveIsVMDisplayUsingSDL2() +{ + popthenPush(1 + (argumentCountOf(primitiveMethod())), trueObject()); + return 0; +} + +static sqInt +primitivePollVMSDL2Event() +{ + sqInt bufferOop; + sqInt size; + sqInt result; + + bufferOop = stackValue(1); + size = stackIntegerValue(0); + + result = sqSDL2_getNextSDL2Event(firstIndexableField(bufferOop), size); + popthenPush(1 + (argumentCountOf(primitiveMethod())), result ? trueObject() : falseObject()); + + return 0; +} + +static sqInt +primitiveSetVMSDL2Input() +{ + sqInt sema; + + sema = stackIntegerValue(0); + setSDL2InputSemaphoreIndex(sema); + if (!(failed())) { + pop(argumentCountOf(primitiveMethod())); + } + + return 0; +} + +#define XFN(export) {"", #export, (void*)export}, +#define XFND(export,depth) {"", #export "\000" depth, (void*)export}, + +static void *sdl2_exports[][3]= +{ + XFND(primitiveIsVMDisplayUsingSDL2, "\001") + XFND(primitivePollVMSDL2Event, "\001") + XFND(primitiveSetVMSDL2Input, "\001") + { 0, 0, 0 } +}; + +sqWindowSystem sqSDL2WindowSystem = { + .name = "sdl2", + .primitives = sdl2_exports, + + .initialize = sqSDL2_initialize, + .shutdown = sqSDL2_shutdown, + .setCursorARGB = sqSDL2_setCursorARGB, + .forceDisplayUpdate = sqSDL2_forceDisplayUpdate, + .formPrint = sqSDL2_formPrint, + .noteDisplayChangedWidthHeightDepth = sqSDL2_noteDisplayChangedWidthHeightDepth, + .setFullScreen = sqSDL2_setFullScreen, + .setCursor = sqSDL2_setCursor, + .setCursorWithMask = sqSDL2_setCursorWithMask, + .showDisplay = sqSDL2_showDisplay, + .hasDisplayDepth = sqSDL2_hasDisplayDepth, + .setDisplayMode = sqSDL2_setDisplayMode, + .getWindowLabel = sqSDL2_getWindowLabel, + .setWindowLabelOfSize = sqSDL2_setWindowLabelOfSize, + .getWindowWidth = sqSDL2_getWindowWidth, + .getWindowHeight = sqSDL2_getWindowHeight, + .setWindowWidthHeight = sqSDL2_setWindowWidthHeight, + .isWindowObscured = sqSDL2_isWindowObscured, + .getNextEvent = sqSDL2_getNextEvent, + .getButtonState = sqSDL2_getButtonState, + .getKeystroke = sqSDL2_getKeystroke, + .mousePoint = sqSDL2_mousePoint, + .peekKeystroke = sqSDL2_peekKeystroke, + .processEvents = sqSDL2_processEvents, + .screenScaleFactor = sqSDL2_screenScaleFactor, + .screenSize = sqSDL2_screenSize, + .screenDepth = sqSDL2_screenDepth, + .clipboardSize = sqSDL2_clipboardSize, + .clipboardReadIntoAt = sqSDL2_clipboardReadIntoAt, + .clipboardWriteFromAt = sqSDL2_clipboardWriteFromAt, + .dropInit = sqSDL2_dropInit, + .dropShutdown = sqSDL2_dropShutdown, + .dropRequestFileName = sqSDL2_dropRequestFileName, + .dropRequestFileHandle = sqSDL2_dropRequestFileHandle, +}; diff --git a/platforms/minheadless/unix/BlueSistaSqueak.icns b/platforms/minheadless/unix/BlueSistaSqueak.icns new file mode 100644 index 0000000000..43f3ad0793 Binary files /dev/null and b/platforms/minheadless/unix/BlueSistaSqueak.icns differ diff --git a/platforms/minheadless/unix/GreenCogSqueak.icns b/platforms/minheadless/unix/GreenCogSqueak.icns new file mode 100644 index 0000000000..a1c801c0b6 Binary files /dev/null and b/platforms/minheadless/unix/GreenCogSqueak.icns differ diff --git a/platforms/minheadless/unix/NewspeakDocuments.icns b/platforms/minheadless/unix/NewspeakDocuments.icns new file mode 100644 index 0000000000..a4dd30fb6a Binary files /dev/null and b/platforms/minheadless/unix/NewspeakDocuments.icns differ diff --git a/platforms/minheadless/unix/NewspeakVirtualMachine.icns b/platforms/minheadless/unix/NewspeakVirtualMachine.icns new file mode 100644 index 0000000000..4691a24b58 Binary files /dev/null and b/platforms/minheadless/unix/NewspeakVirtualMachine.icns differ diff --git a/platforms/minheadless/unix/Pharo-Info.plist b/platforms/minheadless/unix/Pharo-Info.plist new file mode 100644 index 0000000000..65ced06d76 --- /dev/null +++ b/platforms/minheadless/unix/Pharo-Info.plist @@ -0,0 +1,538 @@ + + + + + CFBundleDevelopmentRegion + English + NSSupportsAutomaticGraphicsSwitching + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + image + + CFBundleTypeIconFile + Pharo.icns + CFBundleTypeName + Pharo Image File + CFBundleTypeOSTypes + + STim + + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + sources + + CFBundleTypeIconFile + SqueakSources.icns + CFBundleTypeName + Squeak Sources File + CFBundleTypeOSTypes + + STso + + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + changes + + CFBundleTypeIconFile + SqueakChanges.icns + CFBundleTypeName + Squeak Changes File + CFBundleTypeOSTypes + + STch + + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + sobj + + CFBundleTypeIconFile + SqueakScript.icns + CFBundleTypeName + Squeak Script File + CFBundleTypeOSTypes + + SOBJ + + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + pr + + CFBundleTypeIconFile + SqueakProject.icns + CFBundleTypeName + Squeak Project File + CFBundleTypeOSTypes + + STpr + + CFBundleTypeRole + Editor + + + CFBundleTypeName + JPEG + CFBundleTypeOSTypes + + JPEG + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + TEXT + CFBundleTypeOSTypes + + TEXT + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + ttro + CFBundleTypeOSTypes + + ttro + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + HTML + CFBundleTypeOSTypes + + HTML + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + RTF + CFBundleTypeOSTypes + + RTF + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + TIFF + CFBundleTypeOSTypes + + TIFF + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + PICT + CFBundleTypeOSTypes + + PICT + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + URL + CFBundleTypeOSTypes + + URL + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + ZIP + CFBundleTypeOSTypes + + ZIP + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + zip + CFBundleTypeOSTypes + + zip + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + BINA + CFBundleTypeOSTypes + + BINA + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + GIFf + CFBundleTypeOSTypes + + GIFf + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + PNGf + CFBundleTypeOSTypes + + PNGf + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MP3 + CFBundleTypeOSTypes + + MP3 + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MP3! + CFBundleTypeOSTypes + + MP3! + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MP3U + CFBundleTypeOSTypes + + MP3U + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MPEG + CFBundleTypeOSTypes + + MPEG + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + mp3! + CFBundleTypeOSTypes + + mp3! + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MPG2 + CFBundleTypeOSTypes + + MPG2 + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MPG3 + CFBundleTypeOSTypes + + MPG3 + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + MPG + CFBundleTypeOSTypes + + MPG + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + Mp3 + CFBundleTypeOSTypes + + mp3 + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + M3U + CFBundleTypeOSTypes + + M3U + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + SRCS + CFBundleTypeOSTypes + + SRCS + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + Chng + CFBundleTypeOSTypes + + Chng + + CFBundleTypeRole + Viewer + + + CFBundleTypeName + HPS5 + CFBundleTypeOSTypes + + HPS5 + + CFBundleTypeRole + Viewer + + + CFBundleExecutable + Pharo + CFBundleGetInfoString + CogVM VM 6.0-pre + CFBundleShortVersionString + $(VERSION_NUMBER) + CFBundleIconFile + Pharo.icns + CFBundleIdentifier + org.pharo.Pharo + CFBundleInfoDictionaryVersion + 4.0 + CFBundleName + Pharo + CFBundlePackageType + APPL + CFBundleSignature + FAST + CFBundleVersion + $(VERSION_NUMBER) + LSBackgroundOnly + + LSMinimumSystemVersion + 10.9.0 + NSMainNibFile + MainMenu + NSPrincipalClass + SqueakOSXApplication + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.data + + UTTypeDescription + Pharo Image File + UTTypeIdentifier + org.pharo.image + UTTypeTagSpecification + + com.apple.ostype + STim + public.filename-extension + + image + + public.mime-type + application/pharo-image + + + + UTTypeConformsTo + + public.utf8-plain-text + + UTTypeDescription + Pharo Sources File + UTTypeIdentifier + org.pharo.sources + UTTypeTagSpecification + + com.apple.ostype + STso + public.filename-extension + + sources + + public.mime-type + application/pharo-sources + + + + UTTypeConformsTo + + public.utf8-plain-text + + UTTypeDescription + Pharo Changes File + UTTypeIdentifier + org.pharo.changes + UTTypeTagSpecification + + com.apple.ostype + STch + public.filename-extension + + changes + + public.mime-type + application/pharo-changes + + + + UTTypeConformsTo + + public.data + + UTTypeDescription + Pharo Script File + UTTypeIdentifier + org.pharo.script + UTTypeTagSpecification + + com.apple.ostype + SOBJ + public.filename-extension + + sobj + + public.mime-type + application/squeak-script + + + + SqueakBrowserMouseCmdButton1 + 3 + SqueakBrowserMouseCmdButton2 + 3 + SqueakBrowserMouseCmdButton3 + 2 + SqueakBrowserMouseControlButton1 + 1 + SqueakBrowserMouseControlButton2 + 3 + SqueakBrowserMouseControlButton3 + 2 + SqueakBrowserMouseNoneButton1 + 1 + SqueakBrowserMouseNoneButton2 + 3 + SqueakBrowserMouseNoneButton3 + 2 + SqueakBrowserMouseOptionButton1 + 2 + SqueakBrowserMouseOptionButton2 + 3 + SqueakBrowserMouseOptionButton3 + 2 + SqueakDebug + 0 + SqueakExplicitWindowOpenNeeded + + SqueakImageName + Pharo4.0.image + SqueakMaxHeapSize + 536870912 + SqueakMouseCmdButton1 + 3 + SqueakMouseCmdButton2 + 3 + SqueakMouseCmdButton3 + 2 + SqueakMouseControlButton1 + 1 + SqueakMouseControlButton2 + 3 + SqueakMouseControlButton3 + 2 + SqueakMouseNoneButton1 + 1 + SqueakMouseNoneButton2 + 3 + SqueakMouseNoneButton3 + 2 + SqueakMouseOptionButton1 + 2 + SqueakMouseOptionButton2 + 3 + SqueakMouseOptionButton3 + 2 + SqueakPluginsBuiltInOrLocalOnly + + SqueakQuitOnQuitAppleEvent + + SqueakResourceDirectory + + SqueakTrustedDirectory + /foobar/tooBar/forSqueak/bogus/ + SqueakUnTrustedDirectory + ~/Library/Preferences/Pharo/Internet/My Pharo/ + SqueakUseFileMappedMMAP + + SqueakUIFadeForFullScreenInSeconds + 0.5 + + diff --git a/platforms/minheadless/unix/Pharo.icns b/platforms/minheadless/unix/Pharo.icns new file mode 100644 index 0000000000..9b69b36832 Binary files /dev/null and b/platforms/minheadless/unix/Pharo.icns differ diff --git a/platforms/minheadless/unix/PharoChanges.icns b/platforms/minheadless/unix/PharoChanges.icns new file mode 100644 index 0000000000..36c747b9bc Binary files /dev/null and b/platforms/minheadless/unix/PharoChanges.icns differ diff --git a/platforms/minheadless/unix/PharoImage.icns b/platforms/minheadless/unix/PharoImage.icns new file mode 100644 index 0000000000..5011477600 Binary files /dev/null and b/platforms/minheadless/unix/PharoImage.icns differ diff --git a/platforms/minheadless/unix/PharoSources.icns b/platforms/minheadless/unix/PharoSources.icns new file mode 100644 index 0000000000..8eef62457d Binary files /dev/null and b/platforms/minheadless/unix/PharoSources.icns differ diff --git a/platforms/minheadless/unix/Squeak.icns b/platforms/minheadless/unix/Squeak.icns new file mode 100755 index 0000000000..167b143744 Binary files /dev/null and b/platforms/minheadless/unix/Squeak.icns differ diff --git a/platforms/minheadless/unix/SqueakChanges.icns b/platforms/minheadless/unix/SqueakChanges.icns new file mode 100755 index 0000000000..6f5dc61cfb Binary files /dev/null and b/platforms/minheadless/unix/SqueakChanges.icns differ diff --git a/platforms/minheadless/unix/SqueakGeneric.icns b/platforms/minheadless/unix/SqueakGeneric.icns new file mode 100755 index 0000000000..a1b25e1371 Binary files /dev/null and b/platforms/minheadless/unix/SqueakGeneric.icns differ diff --git a/platforms/minheadless/unix/SqueakImage.icns b/platforms/minheadless/unix/SqueakImage.icns new file mode 100755 index 0000000000..b3eb84ae59 Binary files /dev/null and b/platforms/minheadless/unix/SqueakImage.icns differ diff --git a/platforms/minheadless/unix/SqueakPlugin.icns b/platforms/minheadless/unix/SqueakPlugin.icns new file mode 100755 index 0000000000..5f5e33f700 Binary files /dev/null and b/platforms/minheadless/unix/SqueakPlugin.icns differ diff --git a/platforms/minheadless/unix/SqueakProject.icns b/platforms/minheadless/unix/SqueakProject.icns new file mode 100755 index 0000000000..b5eae0f340 Binary files /dev/null and b/platforms/minheadless/unix/SqueakProject.icns differ diff --git a/platforms/minheadless/unix/SqueakScript.icns b/platforms/minheadless/unix/SqueakScript.icns new file mode 100755 index 0000000000..b5eae0f340 Binary files /dev/null and b/platforms/minheadless/unix/SqueakScript.icns differ diff --git a/platforms/minheadless/unix/SqueakSources.icns b/platforms/minheadless/unix/SqueakSources.icns new file mode 100755 index 0000000000..b6974c6c7f Binary files /dev/null and b/platforms/minheadless/unix/SqueakSources.icns differ diff --git a/platforms/minheadless/unix/aioUnix.c b/platforms/minheadless/unix/aioUnix.c new file mode 100644 index 0000000000..ce52fe7bc8 --- /dev/null +++ b/platforms/minheadless/unix/aioUnix.c @@ -0,0 +1,438 @@ +/* aio.c -- asynchronous file i/o + * + * Copyright (C) 1996-2006 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* Authors: Ian.Piumarta@squeakland.org, eliot.miranda@gmail.com + * + * Last edited: Tue Mar 29 13:06:00 PDT 2016 + */ + +#include "sqaio.h" + +#define HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H + +# include "config.h" + +# ifdef HAVE_UNISTD_H +# include +# include +# endif /* HAVE_UNISTD_H */ + +# ifdef NEED_GETHOSTNAME_P + extern int gethostname(); +# endif + +# include +# include +# include +# include +# include + +# ifdef HAVE_SYS_TIME_H +# include +# else +# include +# endif + +# if HAVE_KQUEUE +# include +# elif HAVE_EPOLL +# include +# elif HAVE_SELECT +# include +# endif + +# ifndef FIONBIO +# ifdef HAVE_SYS_FILIO_H +# include +# endif +# ifndef FIONBIO +# ifdef FIOSNBIO +# define FIONBIO FIOSNBIO +# else +# error: FIONBIO is not defined +# endif +# endif +# endif + +# if __sun__ + # include + # define signal(a, b) sigset(a, b) +# endif + +#else /* !HAVE_CONFIG_H -- assume lowest common demoninator */ + +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +#endif /* !HAVE_CONFIG_H */ + + +#if defined(AIO_DEBUG) +long aioLastTick = 0; +long aioThisTick = 0; + +#endif + +#define _DO_FLAG_TYPE() do { _DO(AIO_R, rd) _DO(AIO_W, wr) _DO(AIO_X, ex) } while (0) + +static aioHandler rdHandler[FD_SETSIZE]; +static aioHandler wrHandler[FD_SETSIZE]; +static aioHandler exHandler[FD_SETSIZE]; + +static void *clientData[FD_SETSIZE]; + +static int maxFd; +static fd_set fdMask; /* handled by aio */ +static fd_set rdMask; /* handle read */ +static fd_set wrMask; /* handle write */ +static fd_set exMask; /* handle exception */ +static fd_set xdMask; /* external descriptor */ + + +static void +undefinedHandler(int fd, void *clientData, int flags) +{ + fprintf(stderr, "undefined handler called (fd %d, flags %x)\n", fd, flags); +} + +#ifdef AIO_DEBUG +const char * +__shortFileName(const char *full__FILE__name) +{ + const char *p = strrchr(full__FILE__name, '/'); + + return p ? p + 1 : full__FILE__name; +} +static char * +handlerName(aioHandler h) +{ + if (h == undefinedHandler) + return "undefinedHandler"; +#ifdef DEBUG_SOCKETS + { + extern char *socketHandlerName(aioHandler); + + return socketHandlerName(h); + } +#endif + return "***unknown***"; +} + +#endif + +/* initialise asynchronous i/o */ + +void +aioInit(void) +{ + extern void forceInterruptCheck(int); /* not really, but hey */ + + FD_ZERO(&fdMask); + FD_ZERO(&rdMask); + FD_ZERO(&wrMask); + FD_ZERO(&exMask); + FD_ZERO(&xdMask); + maxFd = 0; + signal(SIGPIPE, SIG_IGN); + signal(SIGIO, forceInterruptCheck); +} + + +/* disable handlers and close all handled non-exteral descriptors */ + +void +aioFini(void) +{ + int fd; + + for (fd = 0; fd < maxFd; fd++) + if (FD_ISSET(fd, &fdMask) && !(FD_ISSET(fd, &xdMask))) { + aioDisable(fd); + close(fd); + FD_CLR(fd, &fdMask); + FD_CLR(fd, &rdMask); + FD_CLR(fd, &wrMask); + FD_CLR(fd, &exMask); + } + while (maxFd && !FD_ISSET(maxFd - 1, &fdMask)) + --maxFd; + signal(SIGPIPE, SIG_DFL); +} + + +/* + * answer whether i/o becomes possible within the given number of + * microSeconds + */ +#define max(x,y) (((x)>(y))?(x):(y)) + +long pollpip = 0; /* set in sqUnixMain.c by -pollpip arg */ + +#if COGMTVM +/* + * If on the MT VM and pollpip > 1 only pip if a threaded FFI call is in + * progress, which we infer from disownCount being non-zero. + */ +extern long disownCount; + +# define SHOULD_TICK() (pollpip == 1 || (pollpip > 1 && disownCount)) +#else +# define SHOULD_TICK() pollpip +#endif + +static char *ticks = "-\\|/"; +static char *ticker = ""; +static int tickCount = 0; + +#define TICKS_PER_CHAR 10 +#define DO_TICK(bool) \ +do if ((bool) && !(++tickCount % TICKS_PER_CHAR)) { \ + fprintf(stderr, "\r%c\r", *ticker); \ + if (!*ticker++) ticker= ticks; \ +} while (0) + +long +aioPoll(long microSeconds) +{ + int fd; + fd_set rd, wr, ex; + unsigned long long us; + + FPRINTF((stderr, "aioPoll(%ld)\n", microSeconds)); + DO_TICK(SHOULD_TICK()); + + /* + * get out early if there is no pending i/o and no need to relinquish + * cpu + */ + +#ifdef TARGET_OS_IS_IPHONE + if (maxFd == 0) + return 0; +#else + if ((maxFd == 0) && (microSeconds == 0)) + return 0; +#endif + + rd = rdMask; + wr = wrMask; + ex = exMask; + us = ioUTCMicroseconds(); + + for (;;) { + struct timeval tv; + int n; + unsigned long long now; + + tv.tv_sec = microSeconds / 1000000; + tv.tv_usec = microSeconds % 1000000; + n = select(maxFd, &rd, &wr, &ex, &tv); + if (n > 0) + break; + if (n == 0) + return 0; + if (errno && (EINTR != errno)) { + fprintf(stderr, "errno %d\n", errno); + perror("select"); + return 0; + } + now = ioUTCMicroseconds(); + microSeconds -= max(now - us, 1); + if (microSeconds <= 0) + return 0; + us = now; + } + + for (fd = 0; fd < maxFd; ++fd) { +#undef _DO +#define _DO(FLAG, TYPE) \ + if (FD_ISSET(fd, &TYPE)) { \ + aioHandler handler= TYPE##Handler[fd]; \ + FD_CLR(fd, &TYPE##Mask); \ + TYPE##Handler[fd]= undefinedHandler; \ + handler(fd, clientData[fd], FLAG); \ + } + _DO_FLAG_TYPE(); + } + return 1; +} + + +/* + * sleep for microSeconds or until i/o becomes possible, avoiding sleeping in + * select() if timeout too small + */ + +long +aioSleepForUsecs(long microSeconds) +{ +#if defined(HAVE_NANOSLEEP) + if (microSeconds < (1000000 / 60)) { /* < 1 timeslice? */ + if (!aioPoll(0)) { + struct timespec rqtp = {0, microSeconds * 1000}; + struct timespec rmtp; + + nanosleep(&rqtp, &rmtp); + microSeconds = 0; /* poll but don't block */ + } + } +#endif + return aioPoll(microSeconds); +} + + +/* enable asynchronous notification for a descriptor */ + +void +aioEnable(int fd, void *data, int flags) +{ + FPRINTF((stderr, "aioEnable(%d)\n", fd)); + if (fd < 0) { + FPRINTF((stderr, "aioEnable(%d): IGNORED\n", fd)); + return; + } + if (FD_ISSET(fd, &fdMask)) { + fprintf(stderr, "aioEnable: descriptor %d already enabled\n", fd); + return; + } + clientData[fd] = data; + rdHandler[fd] = wrHandler[fd] = exHandler[fd] = undefinedHandler; + FD_SET(fd, &fdMask); + FD_CLR(fd, &rdMask); + FD_CLR(fd, &wrMask); + FD_CLR(fd, &exMask); + if (fd >= maxFd) + maxFd = fd + 1; + if (flags & AIO_EXT) { + FD_SET(fd, &xdMask); + /* we should not set NBIO ourselves on external descriptors! */ + } + else { + /* + * enable non-blocking asynchronous i/o and delivery of SIGIO + * to the active process + */ + int arg; + + FD_CLR(fd, &xdMask); + +#if defined(O_ASYNC) + if (fcntl(fd, F_SETOWN, getpid()) < 0) + perror("fcntl(F_SETOWN, getpid())"); + if ((arg = fcntl(fd, F_GETFL, 0)) < 0) + perror("fcntl(F_GETFL)"); + if (fcntl(fd, F_SETFL, arg | O_NONBLOCK | O_ASYNC) < 0) + perror("fcntl(F_SETFL, O_ASYNC)"); + +#elif defined(FASYNC) + if (fcntl(fd, F_SETOWN, getpid()) < 0) + perror("fcntl(F_SETOWN, getpid())"); + if ((arg = fcntl(fd, F_GETFL, 0)) < 0) + perror("fcntl(F_GETFL)"); + if (fcntl(fd, F_SETFL, arg | O_NONBLOCK | FASYNC) < 0) + perror("fcntl(F_SETFL, FASYNC)"); + +#elif defined(FIOASYNC) + arg = getpid(); + if (ioctl(fd, SIOCSPGRP, &arg) < 0) + perror("ioctl(SIOCSPGRP, getpid())"); + arg = 1; + if (ioctl(fd, FIOASYNC, &arg) < 0) + perror("ioctl(FIOASYNC, 1)"); +#endif + } +} + + +/* install/change the handler for a descriptor */ + +void +aioHandle(int fd, aioHandler handlerFn, int mask) +{ + FPRINTF((stderr, "aioHandle(%d, %s, %d)\n", fd, handlerName(handlerFn), mask)); + if (fd < 0) { + FPRINTF((stderr, "aioHandle(%d): IGNORED\n", fd)); + return; + } +#undef _DO +#define _DO(FLAG, TYPE) \ + if (mask & FLAG) { \ + FD_SET(fd, &TYPE##Mask); \ + TYPE##Handler[fd]= handlerFn; \ + } + _DO_FLAG_TYPE(); +} + + +/* temporarily suspend asynchronous notification for a descriptor */ + +void +aioSuspend(int fd, int mask) +{ + if (fd < 0) { + FPRINTF((stderr, "aioSuspend(%d): IGNORED\n", fd)); + return; + } + FPRINTF((stderr, "aioSuspend(%d)\n", fd)); +#undef _DO +#define _DO(FLAG, TYPE) \ + if (mask & FLAG) { \ + FD_CLR(fd, &TYPE##Mask); \ + TYPE##Handler[fd]= undefinedHandler; \ + } + _DO_FLAG_TYPE(); +} + + +/* definitively disable asynchronous notification for a descriptor */ + +void +aioDisable(int fd) +{ + if (fd < 0) { + FPRINTF((stderr, "aioDisable(%d): IGNORED\n", fd)); + return; + } + FPRINTF((stderr, "aioDisable(%d)\n", fd)); + aioSuspend(fd, AIO_RWX); + FD_CLR(fd, &xdMask); + FD_CLR(fd, &fdMask); + rdHandler[fd] = wrHandler[fd] = exHandler[fd] = 0; + clientData[fd] = 0; + /* keep maxFd accurate (drops to zero if no more sockets) */ + while (maxFd && !FD_ISSET(maxFd - 1, &fdMask)) + --maxFd; +} diff --git a/platforms/minheadless/unix/sqPlatformSpecific-Unix.c b/platforms/minheadless/unix/sqPlatformSpecific-Unix.c new file mode 100644 index 0000000000..3e19907b7b --- /dev/null +++ b/platforms/minheadless/unix/sqPlatformSpecific-Unix.c @@ -0,0 +1,698 @@ +/* sqPlatformSpecific-Win32.c -- Platform specific interface implementation for Unix + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ +/** + * Note: The code present in this file is a result of refactoring the code present + * in the old Squeak Win32 ports. For purpose of copyright, each one of the functions + * present in this file may have an actual author that is different to the author + * of this file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(NOEXECINFO) +# include +# define BACKTRACE_DEPTH 64 +#endif +#if __OpenBSD__ +# include +#endif +#if __FreeBSD__ +# include +#endif +#if __sun__ +# include +# include +#endif + +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "config.h" +#include "debug.h" + +#ifdef __APPLE__ +#include "mac-alias.inc" +#endif + +#if defined(__GNUC__) && ( defined(i386) || defined(__i386) || defined(__i386__) \ + || defined(i486) || defined(__i486) || defined (__i486__) \ + || defined(intel) || defined(x86) || defined(i86pc) ) +static void fldcw(unsigned int cw) +{ + __asm__("fldcw %0" :: "m"(cw)); +} +#else +# define fldcw(cw) +#endif + +#if defined(__GNUC__) && ( defined(ppc) || defined(__ppc) || defined(__ppc__) \ + || defined(POWERPC) || defined(__POWERPC) || defined (__POWERPC__) ) +void mtfsfi(unsigned long long fpscr) +{ + __asm__("lfd f0, %0" :: "m"(fpscr)); + __asm__("mtfsf 0xff, f0"); +} +#else +# define mtfsfi(fpscr) +#endif + +#if !defined(min) +# define min(x,y) (((x)>(y))?(y):(x)) +#endif + +extern int sqVMOptionInstallExceptionHandlers; +extern int sqVMOptionBlockOnError; +extern int sqVMOptionBlockOnWarn; +extern char **argVec; + +static int inFault = 0; +static char crashdump[FILENAME_MAX+1]; + +extern char *GetAttributeString(sqInt id); +extern const char *getVersionInfo(int verbose); +extern void getCrashDumpFilenameInto(char *buf); + +extern void printAllStacks(void); +extern void printCallStack(void); +extern void dumpPrimTraceLog(void); +extern void pushOutputFile(char *); +extern void popOutputFile(void); +extern void ifValidWriteBackStackPointersSaveTo(void*,void*,char**,char**); + +static void +block() +{ + struct timespec while_away_the_hours; + char pwd[MAXPATHLEN+1]; + + printf("blocking e.g. to allow attaching debugger\n"); + printf("pid: %d pwd: %s vm:%s\n", + (int)getpid(), getcwd(pwd,MAXPATHLEN+1), argVec[0]); + while (1) + { + while_away_the_hours.tv_sec = 3600; + nanosleep(&while_away_the_hours, 0); + } +} + + +void ioInitPlatformSpecific(void) +{ + fldcw(0x12bf); /* signed infinity, round to nearest, REAL8, disable intrs, disable signals */ + mtfsfi(0); /* disable signals, IEEE mode, round to nearest */ +} + +time_t convertToSqueakTime(time_t unixTime) +{ +#ifdef HAVE_TM_GMTOFF + unixTime+= localtime(&unixTime)->tm_gmtoff; +#else +# ifdef HAVE_TIMEZONE + unixTime+= ((daylight) * 60*60) - timezone; +# else +# error: cannot determine timezone correction +# endif +#endif + /* Squeak epoch is Jan 1, 1901. Unix epoch is Jan 1, 1970: 17 leap years + and 52 non-leap years later than Squeak. */ + return unixTime + ((52*365UL + 17*366UL) * 24*60*60UL); +} + +#if COGVM +/* + * Support code for Cog. + * a) Answer whether the C frame pointer is in use, for capture of the C stack + * pointers. + * b) answer the amount of stack room to ensure in a Cog stack page, including + * the size of the redzone, if any. + */ + +/* + * Cog has already captured CStackPointer before calling this routine. Record + * the original value, capture the pointers again and determine if CFramePointer + * lies between the two stack pointers and hence is likely in use. This is + * necessary since optimizing C compilers for x86 may use %ebp as a general- + * purpose register, in which case it must not be captured. + */ +int +isCFramePointerInUse() +{ + extern unsigned long CStackPointer, CFramePointer; + extern void (*ceCaptureCStackPointers)(void); + unsigned long currentCSP = CStackPointer; + + currentCSP = CStackPointer; + ceCaptureCStackPointers(); + assert(CStackPointer < currentCSP); + return CFramePointer >= CStackPointer && CFramePointer <= currentCSP; +} + +/* Answer an approximation of the size of the redzone (if any). Do so by + * sending a signal to the process and computing the difference between the + * stack pointer in the signal handler and that in the caller. Assumes stacks + * descend. + */ + +static char * volatile p = 0; + +static void +sighandler(int sig, siginfo_t *info, void *uap) { p = (char *)&sig; } + +static int +getRedzoneSize() +{ + struct sigaction handler_action, old; + handler_action.sa_sigaction = sighandler; + handler_action.sa_flags = SA_NODEFER | SA_SIGINFO; + sigemptyset(&handler_action.sa_mask); + (void)sigaction(SIGPROF, &handler_action, &old); + + do kill(getpid(),SIGPROF); while (!p); + (void)sigaction(SIGPROF, &old, 0); + return (char *)min(&old,&handler_action) - sizeof(struct sigaction) - p; +} + +sqInt reportStackHeadroom; +static int stackPageHeadroom; + +/* Answer the redzone size plus space for any signal handlers to run in. + * N.B. Space for signal handers may include space for the dynamic linker to + * run in since signal handlers may reference other functions, and linking may + * be lazy. The reportheadroom switch can be used to check empirically that + * there is sufficient headroom. + */ +int +osCogStackPageHeadroom() +{ + if (!stackPageHeadroom) + stackPageHeadroom = getRedzoneSize() + 1024; + return stackPageHeadroom; +} +#endif /* COGVM */ + +/* New filename converting function; used by the interpreterProxy function + ioFilenamefromStringofLengthresolveAliases. Most platforms can ignore the + resolveAlias boolean - it seems to only be of use by OSX but is crucial there. +*/ +sqInt +sqGetFilenameFromString(char * aCharBuffer, char * aFilenameString, sqInt filenameLength, sqInt resolveAlias) +{ + int numLinks= 0; + struct stat st; + + memcpy(aCharBuffer, aFilenameString, filenameLength); + aCharBuffer[filenameLength]= 0; + + if (resolveAlias) + { + for (;;) /* aCharBuffer might refer to link or alias */ + { + if (!lstat(aCharBuffer, &st) && S_ISLNK(st.st_mode)) /* symlink */ + { + char linkbuf[PATH_MAX+1]; + if (++numLinks > MAXSYMLINKS) + return -1; /* too many levels of indirection */ + + filenameLength= readlink(aCharBuffer, linkbuf, PATH_MAX); + if ((filenameLength < 0) || (filenameLength >= PATH_MAX)) + return -1; /* link unavailable or path too long */ + + linkbuf[filenameLength]= 0; + + if (filenameLength > 0 && *linkbuf == '/') /* absolute */ + strcpy(aCharBuffer, linkbuf); + else { + char *lastSeparator = strrchr(aCharBuffer,'/'); + char *append = lastSeparator ? lastSeparator + 1 : aCharBuffer; + if (append - aCharBuffer + strlen(linkbuf) > PATH_MAX) + return -1; /* path too long */ + strcpy(append,linkbuf); + } + continue; + } + +# if defined(DARWIN) + if (isMacAlias(aCharBuffer)) + { + if ((++numLinks > MAXSYMLINKS) || !resolveMacAlias(aCharBuffer, aCharBuffer, PATH_MAX)) + return -1; /* too many levels or bad alias */ + continue; + } +# endif + + break; /* target is no longer a symlink or alias */ + } + } + + return 0; +} + +sqInt +ioBeep(void) +{ + return 0; +} + +sqInt +ioExit(void) +{ + exit(0); +} + +sqInt +ioExitWithErrorCode(int errorCode) +{ + exit(errorCode); +} + +sqInt +ioRelinquishProcessorForMicroseconds(sqInt microSeconds) +{ + aioSleepForUsecs(microSeconds); + return 0; +} + +void +ioProfileStatus(sqInt *running, void **exestartpc, void **exelimitpc, + void **vmhst, long *nvmhbin, void **eahst, long *neahbin) +{ +} + +void +ioControlProfile(int on, void **vhp, long *nvb, void **ehp, long *neb) +{ +} + +long +ioControlNewProfile(int on, unsigned long buffer_size) +{ + return 0; +} + +void +ioNewProfileStatus(sqInt *running, long *buffersize) +{ +} + +long +ioNewProfileSamplesInto(void *sampleBuffer) +{ + return 0; +} + +void +ioClearProfile(void) +{ +} + +sqInt +ioDisablePowerManager(sqInt disableIfNonZero) +{ + return true; +} + +/* Executable path. */ +void +findExecutablePath(const char *localVmName, char *dest, size_t destSize) +{ +#if defined(__linux__) + static char name[MAXPATHLEN+1]; + int len; +#endif + +#if defined(__linux__) + if ((len = readlink("/proc/self/exe", name, sizeof(name))) > 0) + { + struct stat st; + name[len]= '\0'; + if (!stat(name, &st)) + localVmName= name; + } +#endif + + /* get canonical path to vm */ + if (realpath(localVmName, dest) == 0) + sqPathMakeAbsolute(dest, destSize, localVmName); + + /* truncate vmPath to dirname */ + { + int i= 0; + for (i = strlen(dest); i >= 0; i--) + { + if ('/' == dest[i]) + { + dest[i+1]= '\0'; + break; + } + } + } +} + + +/* Print an error message, possibly a stack trace, do /not/ exit. + * Allows e.g. writing to a log file and stderr. + */ +static void *printRegisterState(ucontext_t *uap); + +static void +reportStackState(const char *msg, char *date, int printAll, ucontext_t *uap) +{ +#if !defined(NOEXECINFO) + void *addrs[BACKTRACE_DEPTH]; + void *pc; + int depth; +#endif + /* flag prevents recursive error when trying to print a broken stack */ + static sqInt printingStack = false; + +#if COGVM + /* Testing stackLimit tells us whether the VM is initialized. */ + extern usqInt stackLimitAddress(void); +#endif + + printf("\n%s%s%s\n\n", msg, date ? " " : "", date ? date : ""); + printf("%s\n%s\n\n", GetAttributeString(0), getVersionInfo(1)); + +#if COGVM + /* Do not attempt to report the stack until the VM is initialized!! */ + if (!*(char **)stackLimitAddress()) + return; +#endif + +#if !defined(NOEXECINFO) + printf("C stack backtrace & registers:\n"); + if (uap) { + addrs[0] = printRegisterState(uap); + depth = 1 + backtrace(addrs + 1, BACKTRACE_DEPTH); + } + else + depth = backtrace(addrs, BACKTRACE_DEPTH); + putchar('*'); /* indicate where pc is */ + fflush(stdout); /* backtrace_symbols_fd uses unbuffered i/o */ + backtrace_symbols_fd(addrs, depth + 1, fileno(stdout)); +#endif + + if (ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) { + if (!printingStack) { +#if COGVM + /* If we're in generated machine code then the only way the stack + * dump machinery has of giving us an accurate report is if we set + * stackPointer & framePointer to the native stack & frame pointers. + */ +# if __APPLE__ && __MACH__ && __i386__ +# if __GNUC__ && !__INTEL_COMPILER /* icc pretends to be gcc */ + void *fp = (void *)(uap ? uap->uc_mcontext->__ss.__ebp: 0); + void *sp = (void *)(uap ? uap->uc_mcontext->__ss.__esp: 0); +# else + void *fp = (void *)(uap ? uap->uc_mcontext->ss.ebp: 0); + void *sp = (void *)(uap ? uap->uc_mcontext->ss.esp: 0); +# endif +# elif __APPLE__ && __MACH__ && __x86_64__ + void *fp = (void *)(uap ? uap->uc_mcontext->__ss.__rbp: 0); + void *sp = (void *)(uap ? uap->uc_mcontext->__ss.__rsp: 0); +# elif __linux__ && __i386__ + void *fp = (void *)(uap ? uap->uc_mcontext.gregs[REG_EBP]: 0); + void *sp = (void *)(uap ? uap->uc_mcontext.gregs[REG_ESP]: 0); +# elif __linux__ && __x86_64__ + void *fp = (void *)(uap ? uap->uc_mcontext.gregs[REG_RBP]: 0); + void *sp = (void *)(uap ? uap->uc_mcontext.gregs[REG_RSP]: 0); +# elif __FreeBSD__ && __i386__ + void *fp = (void *)(uap ? uap->uc_mcontext.mc_ebp: 0); + void *sp = (void *)(uap ? uap->uc_mcontext.mc_esp: 0); +# elif __OpenBSD__ + void *fp = (void *)(uap ? uap->sc_rbp: 0); + void *sp = (void *)(uap ? uap->sc_rsp: 0); +# elif __sun__ && __i386__ + void *fp = (void *)(uap ? uap->uc_mcontext.gregs[REG_FP]: 0); + void *sp = (void *)(uap ? uap->uc_mcontext.gregs[REG_SP]: 0); +# elif defined(__arm__) || defined(__arm32__) || defined(ARM32) + void *fp = (void *)(uap ? uap->uc_mcontext.arm_fp: 0); + void *sp = (void *)(uap ? uap->uc_mcontext.arm_sp: 0); +# else +# error need to implement extracting pc from a ucontext_t on this system +# endif + char *savedSP, *savedFP; + + ifValidWriteBackStackPointersSaveTo(fp,sp,&savedFP,&savedSP); +#endif /* COGVM */ + + printingStack = true; + if (printAll) { + printf("\n\nAll Smalltalk process stacks (active first):\n"); + printAllStacks(); + } + else { + printf("\n\nSmalltalk stack dump:\n"); + printCallStack(); + } + printingStack = false; +#if COGVM + /* Now restore framePointer and stackPointer via same function */ + ifValidWriteBackStackPointersSaveTo(savedFP,savedSP,0,0); +#endif + } + } + else + printf("\nCan't dump Smalltalk stack(s). Not in VM thread\n"); +#if STACKVM + printf("\nMost recent primitives\n"); + dumpPrimTraceLog(); +# if COGVM + printf("\n"); + reportMinimumUnusedHeadroom(); +# endif +#endif + printf("\n\t(%s)\n", msg); + fflush(stdout); +} + +/* Attempt to dump the registers to stdout. Only do so if we know how. */ +static void * +printRegisterState(ucontext_t *uap) +{ +#if __linux__ && __i386__ + greg_t *regs = uap->uc_mcontext.gregs; + printf( "\teax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n" + "\tedi 0x%08x esi 0x%08x ebp 0x%08x esp 0x%08x\n" + "\teip 0x%08x\n", + regs[REG_EAX], regs[REG_EBX], regs[REG_ECX], regs[REG_EDX], + regs[REG_EDI], regs[REG_EDI], regs[REG_EBP], regs[REG_ESP], + regs[REG_EIP]); + return (void *)regs[REG_EIP]; +#elif __APPLE__ && __DARWIN_UNIX03 && __i386__ + _STRUCT_X86_THREAD_STATE32 *regs = &uap->uc_mcontext->__ss; + printf( "\teax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n" + "\tedi 0x%08x esi 0x%08x ebp 0x%08x esp 0x%08x\n" + "\teip 0x%08x\n", + regs->__eax, regs->__ebx, regs->__ecx, regs->__edx, + regs->__edi, regs->__edi, regs->__ebp, regs->__esp, + regs->__eip); + return (void *)(regs->__eip); +#elif __APPLE__ && __i386__ + _STRUCT_X86_THREAD_STATE32 *regs = &uap->uc_mcontext->ss; + printf( "\teax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n" + "\tedi 0x%08x esi 0x%08x ebp 0x%08x esp 0x%08x\n" + "\teip 0x%08x\n", + regs->eax, regs->ebx, regs->ecx, regs->edx, + regs->edi, regs->edi, regs->ebp, regs->esp, + regs->eip); + return (void *)(regs->eip); +#elif __APPLE__ && __x86_64__ + _STRUCT_X86_THREAD_STATE64 *regs = &uap->uc_mcontext->__ss; + printf( "\trax 0x%016llx rbx 0x%016llx rcx 0x%016llx rdx 0x%016llx\n" + "\trdi 0x%016llx rsi 0x%016llx rbp 0x%016llx rsp 0x%016llx\n" + "\tr8 0x%016llx r9 0x%016llx r10 0x%016llx r11 0x%016llx\n" + "\tr12 0x%016llx r13 0x%016llx r14 0x%016llx r15 0x%016llx\n" + "\trip 0x%016llx\n", + regs->__rax, regs->__rbx, regs->__rcx, regs->__rdx, + regs->__rdi, regs->__rdi, regs->__rbp, regs->__rsp, + regs->__r8 , regs->__r9 , regs->__r10, regs->__r11, + regs->__r12, regs->__r13, regs->__r14, regs->__r15, + regs->__rip); + return (void *)(regs->__rip); +# elif __APPLE__ && (defined(__arm__) || defined(__arm32__)) + _STRUCT_ARM_THREAD_STATE *regs = &uap->uc_mcontext->ss; + printf( "\t r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + "\t r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n" + "\t r8 0x%08x r9 0x%08x r10 0x%08x fp 0x%08x\n" + "\t ip 0x%08x sp 0x%08x lr 0x%08x pc 0x%08x\n" + "\tcpsr 0x%08x\n", + regs->r[0],regs->r[1],regs->r[2],regs->r[3], + regs->r[4],regs->r[5],regs->r[6],regs->r[7], + regs->r[8],regs->r[9],regs->r[10],regs->r[11], + regs->r[12], regs->sp, regs->lr, regs->pc, regs->cpsr); + return (void *)(regs->pc); +#elif __FreeBSD__ && __i386__ + struct mcontext *regs = &uap->uc_mcontext; + printf( "\teax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n" + "\tedi 0x%08x esi 0x%08x ebp 0x%08x esp 0x%08x\n" + "\teip 0x%08x\n", + regs->mc_eax, regs->mc_ebx, regs->mc_ecx, regs->mc_edx, + regs->mc_edi, regs->mc_edi, regs->mc_ebp, regs->mc_esp, + regs->mc_eip); + return regs->mc_eip; +#elif __linux__ && __x86_64__ + greg_t *regs = uap->uc_mcontext.gregs; + printf( "\trax 0x%08x rbx 0x%08x rcx 0x%08x rdx 0x%08x\n" + "\trdi 0x%08x rsi 0x%08x rbp 0x%08x rsp 0x%08x\n" + "\tr8 0x%08x r9 0x%08x r10 0x%08x r11 0x%08x\n" + "\tr12 0x%08x r13 0x%08x r14 0x%08x r15 0x%08x\n" + "\trip 0x%08x\n", + regs[REG_RAX], regs[REG_RBX], regs[REG_RCX], regs[REG_RDX], + regs[REG_RDI], regs[REG_RDI], regs[REG_RBP], regs[REG_RSP], + regs[REG_R8 ], regs[REG_R9 ], regs[REG_R10], regs[REG_R11], + regs[REG_R12], regs[REG_R13], regs[REG_R14], regs[REG_R15], + regs[REG_RIP]); + return regs[REG_RIP]; +# elif __linux__ && (defined(__arm__) || defined(__arm32__) || defined(ARM32)) + struct sigcontext *regs = &uap->uc_mcontext; + printf( "\t r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + "\t r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n" + "\t r8 0x%08x r9 0x%08x r10 0x%08x fp 0x%08x\n" + "\t ip 0x%08x sp 0x%08x lr 0x%08x pc 0x%08x\n", + regs->arm_r0,regs->arm_r1,regs->arm_r2,regs->arm_r3, + regs->arm_r4,regs->arm_r5,regs->arm_r6,regs->arm_r7, + regs->arm_r8,regs->arm_r9,regs->arm_r10,regs->arm_fp, + regs->arm_ip, regs->arm_sp, regs->arm_lr, regs->arm_pc); +#else + printf("don't know how to derive register state from a ucontext_t on this platform\n"); + return 0; +#endif +} + +static void +sigusr1(int sig, siginfo_t *info, void *uap) +{ + int saved_errno = errno; + time_t now = time(NULL); + char ctimebuf[32]; + unsigned long pc; + + if (!ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) { + pthread_kill(getVMOSThread(),sig); + errno = saved_errno; + return; + } + + getCrashDumpFilenameInto(crashdump); + ctime_r(&now,ctimebuf); + pushOutputFile(crashdump); + reportStackState("SIGUSR1", ctimebuf, 1, uap); + popOutputFile(); + reportStackState("SIGUSR1", ctimebuf, 1, uap); + + errno = saved_errno; +} + +static void +sigsegv(int sig, siginfo_t *info, void *uap) +{ + time_t now = time(NULL); + char ctimebuf[32]; + const char *fault; + switch(sig) + { + case SIGSEGV: + fault = "Segmentation fault"; + break; + case SIGBUS: + fault = "Bus error"; + break; + case SIGILL: + fault = "Illegal instruction"; + break; + default: + fault = "Unknown signal"; + break; + } + + if (!inFault) { + getCrashDumpFilenameInto(crashdump); + ctime_r(&now,ctimebuf); + pushOutputFile(crashdump); + reportStackState(fault, ctimebuf, 0, uap); + popOutputFile(); + reportStackState(fault, ctimebuf, 0, uap); + } + if (sqVMOptionBlockOnError) block(); + abort(); +} + +#if defined(IMAGE_DUMP) +static void +sighup(int ignore) { dumpImageFile= 1; } + +static void +sigquit(int ignore) { emergencyDump(1); } +#endif + +int sqExecuteFunctionWithCrashExceptionCatching(sqFunctionThatCouldCrash function, void *userdata) +{ + if (sqVMOptionInstallExceptionHandlers) + { + struct sigaction sigusr1_handler_action, sigsegv_handler_action; + + sigsegv_handler_action.sa_sigaction = sigsegv; + sigsegv_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO; + sigemptyset(&sigsegv_handler_action.sa_mask); + (void)sigaction(SIGSEGV, &sigsegv_handler_action, 0); + (void)sigaction(SIGBUS, &sigsegv_handler_action, 0); + (void)sigaction(SIGILL, &sigsegv_handler_action, 0); + + sigusr1_handler_action.sa_sigaction = sigusr1; + sigusr1_handler_action.sa_flags = SA_NODEFER | SA_SIGINFO; + sigemptyset(&sigusr1_handler_action.sa_mask); + (void)sigaction(SIGUSR1, &sigusr1_handler_action, 0); + } + +#if defined(IMAGE_DUMP) + signal(SIGHUP, sighup); + signal(SIGQUIT, sigquit); +#endif + + return function(userdata); +} + +/* OS Exports */ +void *os_exports[][3]= +{ + { 0, 0, 0 } +}; diff --git a/platforms/minheadless/unix/sqPlatformSpecific-Unix.h b/platforms/minheadless/unix/sqPlatformSpecific-Unix.h new file mode 100644 index 0000000000..0856dffc09 --- /dev/null +++ b/platforms/minheadless/unix/sqPlatformSpecific-Unix.h @@ -0,0 +1,128 @@ +/* sqPlatformSpecific.h -- platform-specific modifications to sq.h + * + * Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: ian.piumarta@squeakland.org + * + */ + +#undef sqAllocateMemory +#undef sqGrowMemoryBy +#undef sqShrinkMemoryBy +#undef sqMemoryExtraBytesLeft + +#include "sqMemoryAccess.h" + +extern usqInt sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize); +#define allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, fileStream, headerSize) \ +sqAllocateMemory(minimumMemory, heapSize) +extern sqInt sqGrowMemoryBy(sqInt oldLimit, sqInt delta); +extern sqInt sqShrinkMemoryBy(sqInt oldLimit, sqInt delta); +extern sqInt sqMemoryExtraBytesLeft(sqInt includingSwap); +#if COGVM +extern void sqMakeMemoryExecutableFromTo(unsigned long, unsigned long); +extern void sqMakeMemoryNotExecutableFromTo(unsigned long, unsigned long); + +extern int isCFramePointerInUse(void); +extern int osCogStackPageHeadroom(void); +extern void reportMinimumUnusedHeadroom(void); +#endif + +/* Thread support for thread-safe signalSemaphoreWithIndex and/or the COGMTVM */ +#if STACKVM || NewspeakVM +# define sqLowLevelYield() sched_yield() +/* linux's sched.h defines clone that conflicts with the interpreter's */ +# define clone NameSpacePollutant +# include +# undef clone +# define sqOSThread pthread_t +/* these are used both in the STACKVM & the COGMTVM */ +# define ioOSThreadsEqual(a,b) pthread_equal(a,b) +# define ioCurrentOSThread() pthread_self() +# if COGMTVM +/* Please read the comment for CogThreadManager in the VMMaker package for + * documentation of this API. + */ +typedef struct { + pthread_cond_t cond; + pthread_mutex_t mutex; + int count; + } sqOSSemaphore; +# define ioDestroyOSSemaphore(ptr) 0 +# if !ForCOGMTVMImplementation /* this is a read-only export */ +extern const pthread_key_t tltiIndex; +# endif +# define ioGetThreadLocalThreadIndex() ((long)pthread_getspecific(tltiIndex)) +# define ioSetThreadLocalThreadIndex(v) (pthread_setspecific(tltiIndex,(void*)(v))) +# define ioOSThreadIsAlive(thread) (pthread_kill(thread,0) == 0) +# define ioTransferTimeslice() sched_yield() +# define ioMilliSleep(ms) usleep((ms) * 1000) +# endif /* COGMTVM */ +#endif /* STACKVM || NewspeakVM */ + +#include + +typedef off_t squeakFileOffsetType; + +#undef sqFilenameFromString +#undef sqFilenameFromStringOpen +#define sqFilenameFromStringOpen sqFilenameFromString + +extern void sqFilenameFromString(char *uxName, sqInt stNameIndex, int sqNameLength); + +#undef dispatchFunctionPointer +#undef dispatchFunctionPointerOnin +/* we'd like to untypedef fptr too, but such is life */ + +#include /* for declaration of ftruncate */ + +#undef sqFTruncate +/* sqFTruncate should return 0 on success, ftruncate does also */ +#define sqFTruncate(f,o) ftruncate(fileno(f), o) +#define ftell ftello +#define fseek fseeko + +#if defined(__GNUC__) +# if !defined(VM_LABEL) +# define VM_LABEL(foo) asm("\n.globl L" #foo "\nL" #foo ":") +# endif +#else +# if HAVE_ALLOCA_H +# include +# else +# ifdef _AIX +# pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ + char *alloca(); +# endif +# endif +# endif +#endif + +#if !defined(VM_LABEL) || COGVM +# undef VM_LABEL +# define VM_LABEL(foo) 0 +#endif diff --git a/platforms/minheadless/unix/sqUnixCharConv.c b/platforms/minheadless/unix/sqUnixCharConv.c new file mode 100644 index 0000000000..73dbe11418 --- /dev/null +++ b/platforms/minheadless/unix/sqUnixCharConv.c @@ -0,0 +1,474 @@ +/* sqUnixCharConv.c -- conversion between character encodings + * + * Author: Ian.Piumarta@squeakland.org + * + * Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Last edited: 2009-08-15 12:59:49 by piumarta on emilia-2.local + */ + +#if !defined(__MACH__) +# include "sqMemoryAccess.h" +#endif +#include "sqUnixCharConv.h" + +#include +#include +#include +#include +#include + +static inline int min(int x, int y) { return (x < y) ? x : y; } + +static int +convertCopy(char *from, int fromLen, char *to, int toLen, int term) +{ + int len= min(toLen - term, fromLen); + strncpy(to, from, len); + if (term) to[len]= '\0'; + return len; +} + + +#if defined(__MACH__) + +// we have to do something special on MacOSX (surprise surprise) because: +// - MacOSX is not Unix98 compliant and lacks builtin iconv functions +// - the free libiconv cannot handle the canonical decomposition used in HFS+ + +# include +# include "sqMemoryAccess.h" + +typedef struct +{ + char *alias; + void *encoding; +} alias; + +static alias encodings[]= +{ + { "MACROMAN", (void *)kCFStringEncodingMacRoman }, + { "MAC", (void *)kCFStringEncodingMacRoman }, + { "MACINTOSH", (void *)kCFStringEncodingMacRoman }, + { "CSMACINTOSH", (void *)kCFStringEncodingMacRoman }, + { "UTF8", (void *)kCFStringEncodingUTF8 }, + { "UTF-8", (void *)kCFStringEncodingUTF8 }, + { "ISOLATIN9", (void *)kCFStringEncodingISOLatin9 }, + { "LATIN9", (void *)kCFStringEncodingISOLatin9 }, + { "ISO-8859-15", (void *)kCFStringEncodingISOLatin9 }, + { "ISOLATIN1", (void *)kCFStringEncodingISOLatin1 }, + { "LATIN1", (void *)kCFStringEncodingISOLatin1 }, + { "ISO-8859-1", (void *)kCFStringEncodingISOLatin1 }, + // there are many tens of these and I cannot be bothered. + { 0, 0 } +}; + +// defaults + +void *localeEncoding= 0; +void *sqTextEncoding= ((void *)kCFStringEncodingMacRoman); // xxxFIXME -> kCFStringEncodingISOLatin9 +void *uxTextEncoding= ((void *)kCFStringEncodingISOLatin9); +void *uxPathEncoding= ((void *)kCFStringEncodingUTF8); +void *uxUTF8Encoding= ((void *)kCFStringEncodingUTF8); +void *uxXWinEncoding= ((void *)kCFStringEncodingISOLatin1); + +void +setLocaleEncoding(char *locale) { } + +void +freeEncoding(void *encoding) { } + +void +setEncoding(void **encoding, char *rawName) +{ + char *name= strdup(rawName); + int len= strlen(name); + int i; + int utf8= 0; + alias *ap= encodings; + for (i= 0; i < len; ++i) + name[i]= toupper(name[i]); + while (ap->alias) + if (!strcmp(name, ap->alias)) + { + *encoding= ap->encoding; + goto done; + } + else + ++ap; + fprintf(stderr, "setEncoding: could not set encoding '%s'\n", name); + done: + free(name); +} + +void +setNEncoding(void **encoding, char *rawName, int n) +{ + setEncoding(encoding, rawName); +} + +int +convertChars(char *from, int fromLen, void *fromCode, char *to, int toLen, void *toCode, int norm, int term) +{ + CFStringRef cfs= CFStringCreateWithBytes(NULL, (unsigned char *)from, fromLen, (CFStringEncoding)fromCode, 0); + CFMutableStringRef str= CFStringCreateMutableCopy(NULL, 0, cfs); + CFRelease(cfs); + if (norm) // HFS+ imposes Unicode2.1 decomposed UTF-8 encoding on all path elements + CFStringNormalize(str, kCFStringNormalizationFormD); // canonical decomposition + { + CFRange rng= CFRangeMake(0, CFStringGetLength(str)); + CFIndex len= 0; + CFIndex num= CFStringGetBytes(str, rng, (CFStringEncoding)toCode, '?', 0, (UInt8 *)to, toLen - term, &len); + CFRelease(str); + if (!num) + return convertCopy(from, fromLen, to, toLen, term); + if (term) + to[len]= '\0'; + return len; + } +} + + +#elif defined(HAVE_ICONV_H) + +# include + +typedef char ichar_t; + +static char macEncoding[]= "MACINTOSH"; +static char utf8Encoding[]= "UTF-8"; +static char iso1Encoding[]= "ISO-8859-1"; +static char iso15Encoding[]= "ISO-8859-15"; + +static char *preDefinedEncodings[]= + { + macEncoding, + utf8Encoding, + iso1Encoding, + iso15Encoding + }; + +void *localeEncoding= 0; +void *sqTextEncoding= (void *)macEncoding; +void *uxTextEncoding= (void *)iso15Encoding; +void *uxPathEncoding= (void *)utf8Encoding; +void *uxUTF8Encoding= (void *)utf8Encoding; +void *uxXWinEncoding= (void *)iso1Encoding; + +void +freeEncoding(void *encoding) +{ + int i; + for (i= 0; i < sizeof(preDefinedEncodings) / sizeof(char *); ++i) + if (encoding == preDefinedEncodings[i]) + return; + free(encoding); +} + +typedef struct +{ + char *name; + char *encoding; +} alias; + +void +setNEncoding(void **encoding, char *rawName, int n) +{ + char *name= malloc((size_t)((n + 1) * sizeof(char))); + int i; + + static alias aliases[]= + { + {"UTF8", utf8Encoding}, + {"MACROMAN", macEncoding}, + {"MAC-ROMAN", macEncoding}, + }; + + for (i= 0; i < n; ++i) + name[i]= toupper(rawName[i]); + name[n]= '\0'; + if ((*encoding) && (*encoding != localeEncoding)) + freeEncoding(*encoding); + if (localeEncoding && !strcmp(name, localeEncoding)) + { + *encoding= localeEncoding; + free(name); + return; + } + for(i= 0; i < sizeof(preDefinedEncodings) / sizeof(char *); ++i) + if (!strcmp(name, preDefinedEncodings[i])) + { + *encoding= preDefinedEncodings[i]; + free(name); + return; + } + for (i= 0; i < sizeof(aliases) / sizeof(alias); ++i) + if(!strcmp(name, aliases[i].name)) + { + *encoding= aliases[i].encoding; + free(name); + return; + } + *encoding= name; +} + +void +setLocaleEncoding(char *locale) +{ + while (*locale) + if (*locale++ == '.') + { + int len= 0; + while (locale[len] && (locale[len] != '@')) + ++len; + setNEncoding(&localeEncoding, locale, len); + sqTextEncoding= uxTextEncoding= uxPathEncoding= uxXWinEncoding= localeEncoding; + return; + } +} + +void +setEncoding(void **encoding, char *rawName) +{ + setNEncoding(encoding, rawName, strlen(rawName)); +} + +static void +iconvFail(char *toCode, char *fromCode) +{ + static int warned= 0; + if (!warned++) + { + char buf[256]; + snprintf(buf, sizeof(buf), "iconv_open(%s, %s)", toCode, fromCode); + perror(buf); + } +} + +int +convertChars(char *from, int fromLen, void *fromCode, char *to, int toLen, void *toCode, int norm, int term) +{ + ichar_t *inbuf= from; + size_t inbytes= fromLen; + char *outbuf= to; + size_t outbytes= toLen - term; + static iconv_t cd= (iconv_t)-1; + static void *pfc= 0; + static void *ptc= 0; + + if ((pfc != fromCode) || (ptc != toCode)) + { + if (cd != (iconv_t)-1) iconv_close(cd); + pfc= ptc= (void *)-1; + cd= iconv_open((const char *)toCode, (const char *)fromCode); + if ((iconv_t)-1 != cd) + { + pfc= fromCode; + ptc= toCode; + } + } + + if ((iconv_t)-1 != cd) + { + while (inbytes > 0) + { + int n= iconv(cd, &inbuf, &inbytes, &outbuf, &outbytes); + if ((size_t)-1 == n) + { + switch (errno) + { + case EINVAL: /* broken multibyte at end of input */ + case EILSEQ: /* broken multibyte in input */ + { + unsigned char c= (unsigned char)*inbuf; + unsigned char mask= 0x80; + size_t skip= 0; + + if (0xfe == c || 0xff == c) /* invalid */ + skip= 1; + else + while ((skip < inbytes) && (mask & c)) + { + skip++; + mask >>= 1; + } + inbuf += skip; + inbytes -= skip; + if (outbytes > 0) + { + *outbuf++ = '?'; + outbytes--; + } + } + break; + + case E2BIG: /* out of room in output buffer */ + inbytes= 0; + /* fall through */ + + default: + iconvFail(toCode, fromCode); + break; + } + } + } + if (term) *outbuf= '\0'; + return outbuf - to; + } + else + iconvFail(toCode, fromCode); + + return convertCopy(from, fromLen, to, toLen, term); +} + +#else /* !__MACH__ && !HAVE_LIBICONV */ + +void *localeEncoding= 0; +void *sqTextEncoding= 0; +void *uxTextEncoding= 0; +void *uxPathEncoding= 0; +void *uxUTF8Encoding= 0; +void *uxXWinEncoding= 0; + +void +setLocaleEncoding(char *locale) { } + +void +freeEncoding(void *encoding) { } + +void +setEncoding(void **encoding, char *name) { } + +int +convertChars(char *from, int fromLen, void *fromCode, char *to, int toLen, void *toCode, int norm, int term) +{ + return convertCopy(from, fromLen, to, toLen, term); +} + +#endif + + +static inline void +sq2uxLines(char *string, int n) +{ + while (n--) + { + if ('\015' == *string) *string= '\012'; + ++string; + } +} + +static inline void +ux2sqLines(char *string, int n) +{ + while (n--) + { + if ('\012' == *string) *string= '\015'; + ++string; + } +} + + +#define Convert(sq,ux, type, F, T, N, L) \ + int sq##2##ux##type(char *from, int fromLen, char *to, int toLen, int term) \ + { \ + int n= convertChars(from, fromLen, F, to, toLen, T, N, term); \ + if (L) sq##2##ux##Lines(to, n); \ + return n; \ + } + +Convert(sq,ux, Text, sqTextEncoding, uxTextEncoding, 0, 1); +Convert(ux,sq, Text, uxTextEncoding, sqTextEncoding, 0, 1); +#if defined(__MACH__) +Convert(sq,ux, Path, sqTextEncoding, uxPathEncoding, 1, 0); // normalised paths for HFS+ +#else +Convert(sq,ux, Path, sqTextEncoding, uxPathEncoding, 0, 0); // composed paths for others +#endif +Convert(ux,sq, Path, uxPathEncoding, sqTextEncoding, 0, 0); +Convert(sq,ux, UTF8, sqTextEncoding, uxUTF8Encoding, 0, 1); +Convert(ux,sq, UTF8, uxUTF8Encoding, sqTextEncoding, 0, 1); +Convert(ux,sq, XWin, uxXWinEncoding, sqTextEncoding, 0, 1); + +#undef Convert + + + +void +sqFilenameFromString(char *uxName, sqInt sqNameIndex, int sqNameLength) +{ + /*xxx BUG: lots of code generate from the image assumes 1000 chars max path len */ + sq2uxPath(pointerForOop(sqNameIndex), sqNameLength, uxName, 1000, 1); +} + + + +#if defined(CONV_TEST) + + +#if defined(HAVE_LANGINFO_CODESET) +# include +#endif + +int main() +{ +#if defined(HAVE_LANGINFO_CODESET) + if (0 == strcmp(nl_langinfo(CODESET), "UTF-8")) + printf("UTF-8 codeset selected\n"); +#else + { + char *s; + if ((( (s = getenv("LC_ALL")) && *s) + || ((s = getenv("LC_CTYPE")) && *s) + || ((s = getenv("LANG")) && *s)) + && strstr(s, "UTF-8")) + printf("UTF-8 locale selected\n"); + } +#endif + + { + char *in, out[256]; + int n; + in= "tésté"; // UTF-8 composed Unicode + n= convertChars(in, strlen(in), uxPathEncoding, out, sizeof(out), uxTextEncoding, 0, 1); + printf("%d: %s -> %s\n", n, in, out); + in= "tésté"; // UTF-8 decomposed Unicode (libiconv fails on this one, MacOSX passes) + n= convertChars(in, strlen(in), uxPathEncoding, out, sizeof(out), uxTextEncoding, 0, 1); + printf("%d: %s -> %s\n", n, in, out); + in= "t�st�"; // ISO-8859-15 + n= convertChars(in, strlen(in), uxTextEncoding, out, sizeof(out), uxPathEncoding, 0, 1); + printf("%d: %s -> %s\n", n, in, out); // default composition -- should yield "tésté" + n= convertChars(in, strlen(in), uxTextEncoding, out, sizeof(out), uxPathEncoding, 1, 1); + printf("%d: %s -> %s\n", n, in, out); // canonical decomposition -- should yield "tésté" + } + return 0; +} + +/* + cc -Wall -DCONV_TEST -g -o main sqUnixCharConv.c -framework CoreFoundation # MacOSX + cc -Wall -DCONV_TEST -g -o main sqUnixCharConv.c # glibc >= 2.2 + cc -Wall -DCONV_TEST -g -o main sqUnixCharConv.c -liconv # others +*/ + +#endif /* defined(CONV_TEST) */ diff --git a/platforms/minheadless/unix/sqUnixCharConv.h b/platforms/minheadless/unix/sqUnixCharConv.h new file mode 100644 index 0000000000..81824d8a5c --- /dev/null +++ b/platforms/minheadless/unix/sqUnixCharConv.h @@ -0,0 +1,60 @@ +/* sqUnixCharConv.h -- conversion between character encodings + * + * Author: Ian.Piumarta@squeakland.org + * + * Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Last edited: 2008-03-19 14:43:16 by piumarta on emilia.local + */ + +#ifndef __sqUnixCharConv_h +#define __sqUnixCharConv_h + +extern void *sqTextEncoding; +extern void *uxTextEncoding; +extern void *uxPathEncoding; +extern void *uxUTF8Encoding; +extern void *uxXWinEncoding; +extern void *localeEncoding; + +extern void setEncoding(void **encoding, char *name); + +extern int convertChars(char *from, int fromLen, void *fromCode, + char *to, int toLen, void *toCode, + int norm, int term); + +extern int sq2uxText(char *from, int fromLen, char *to, int toLen, int term); +extern int ux2sqText(char *from, int fromLen, char *to, int toLen, int term); +extern int sq2uxPath(char *from, int fromLen, char *to, int toLen, int term); +extern int ux2sqPath(char *from, int fromLen, char *to, int toLen, int term); +extern int sq2uxUTF8(char *from, int fromLen, char *to, int toLen, int term); +extern int ux2sqUTF8(char *from, int fromLen, char *to, int toLen, int term); +extern int ux2sqXWin(char *from, int fromLen, char *to, int toLen, int term); + +extern void freeEncoding(void *encoding); +extern void setNEncoding(void **encoding, char *rawName, int n); +extern void setLocaleEncoding(char *locale); + +#endif /* __sqUnixCharConv_h */ diff --git a/platforms/minheadless/unix/sqUnixHeartbeat.c b/platforms/minheadless/unix/sqUnixHeartbeat.c new file mode 100644 index 0000000000..b65d288326 --- /dev/null +++ b/platforms/minheadless/unix/sqUnixHeartbeat.c @@ -0,0 +1,410 @@ +/**************************************************************************** +* PROJECT: Unix (pthread) heartbeat logic for Stack/Cog VM +* FILE: sqUnixHeartbeat.c +* CONTENT: +* +* AUTHOR: Eliot Miranda +* ADDRESS: +* EMAIL: eliot.miranda@gmail.com +* RCSID: $Id$ +* +* NOTES: +* Feb 1st, 2012, EEM refactored into three separate files. +* July 31st, 2008, EEM added heart-beat thread. +* Aug 20th, 2009, EEM added 64-bit microsecond clock support code +* +*****************************************************************************/ + +#if ITIMER_HEARTBEAT +# if VM_TICKER +# include "sqUnixITimerTickerHeartbeat.c" +# else +# include "sqUnixITimerHeartbeat.c" +# endif +#else /* ITIMER_HEARTBEAT */ + +#include "sq.h" +#include "sqAssert.h" +#include "sqMemoryFence.h" +#include "sqSCCSVersion.h" +#include +#include +#include /* for fprintf */ +#include +#include +#include "sqaio.h" + +#define SecondsFrom1901To1970 2177452800LL +#define MicrosecondsFrom1901To1970 2177452800000000LL + +#define MicrosecondsPerSecond 1000000LL +#define MillisecondsPerSecond 1000LL + +#define MicrosecondsPerMillisecond 1000LL + +static unsigned volatile long long utcMicrosecondClock; +static unsigned volatile long long localMicrosecondClock; +static unsigned volatile long millisecondClock; /* for the ioMSecs clock. */ +static unsigned long long utcStartMicroseconds; /* for the ioMSecs clock. */ +static long long vmGMTOffset = 0; +static unsigned long long frequencyMeasureStart = 0; +static unsigned long heartbeats; + +#define microToMilliseconds(usecs) ((((usecs) - utcStartMicroseconds) \ + / MicrosecondsPerMillisecond) \ + & MillisecondClockMask) + +#define LOG_CLOCK 1 + +#if LOG_CLOCK +# define LOGSIZE 1024 +static unsigned long long useclog[LOGSIZE]; +static unsigned long mseclog[LOGSIZE]; +static int logClock = 0; +static unsigned int ulogidx = (unsigned int)-1; +static unsigned int mlogidx = (unsigned int)-1; +# define logusecs(usecs) do { sqLowLevelMFence(); \ + if (logClock) useclog[++ulogidx % LOGSIZE] = (usecs); \ + } while (0) +# define logmsecs(msecs) do { sqLowLevelMFence(); \ + if (logClock) mseclog[++mlogidx % LOGSIZE] = (msecs); \ + } while (0) +void +ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *runInNOutp, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip) +{ + logClock = *runInNOutp; + sqLowLevelMFence(); + *runInNOutp = LOGSIZE; + *usecsp = useclog; + *uip = ulogidx % LOGSIZE; + *msecsp = mseclog; + *mip = mlogidx % LOGSIZE; +} +#else /* LOG_CLOCK */ +# define logusecs(usecs) 0 +# define logmsecs(msecs) 0 +void +ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *np, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip) +{ + *np = *uip = *mip = 0; + *usecsp = *msecsp = 0; +} +#endif /* LOG_CLOCK */ + +/* Compute the current VM time basis, the number of microseconds from 1901. */ + +static unsigned long long +currentUTCMicroseconds() +{ + struct timeval utcNow; + + gettimeofday(&utcNow,0); + return ((utcNow.tv_sec * MicrosecondsPerSecond) + utcNow.tv_usec) + + MicrosecondsFrom1901To1970; +} + +/* + * Update the utc and local microsecond clocks, and the millisecond clock. + * Since this is invoked from interupt code, and since the clocks are 64-bit values + * that are read concurrently by the VM, care must be taken to access these values + * atomically on 32-bit systems. If they are not accessed atomically there is a + * possibility of fetching the two halves of the clock from different ticks which + * would cause a jump in the clock of 2^32 microseconds (1 hr, 11 mins, 34 secs). + * + * Since an interrupt could occur between any two instructions the clock must be + * read atomically as well as written atomically. If possible this can be + * implemented without locks using atomic 64-bit reads and writes. + */ + +#include "sqAtomicOps.h" + +static void +updateMicrosecondClock() +{ + unsigned long long newUtcMicrosecondClock; + unsigned long long newLocalMicrosecondClock; + + newUtcMicrosecondClock = currentUTCMicroseconds(); + + /* The native clock may go backwards, e.g. due to NTP adjustments, although + * why it can't avoid small backward steps itself, I don't know. Simply + * ignore backward steps and wait until the clock catches up again. Of + * course this will cause problems if the clock is manually adjusted. To + * which the doctor says, "don't do that". + */ + if (!asserta(newUtcMicrosecondClock >= utcMicrosecondClock)) { + logusecs(0); /* if logging log a backward step as 0 */ + return; + } + newLocalMicrosecondClock = newUtcMicrosecondClock + vmGMTOffset; + + set64(utcMicrosecondClock,newUtcMicrosecondClock); + set64(localMicrosecondClock,newLocalMicrosecondClock); + millisecondClock = microToMilliseconds(newUtcMicrosecondClock); + + logusecs(newUtcMicrosecondClock); + logmsecs(millisecondClock); +} + +void +ioUpdateVMTimezone() +{ + updateMicrosecondClock(); +#ifdef HAVE_TM_GMTOFF + time_t utctt; + utctt = (get64(utcMicrosecondClock) - MicrosecondsFrom1901To1970) + / MicrosecondsPerSecond; + vmGMTOffset = localtime(&utctt)->tm_gmtoff * MicrosecondsPerSecond; +#else +# ifdef HAVE_TIMEZONE + extern time_t timezone, altzone; + extern int daylight; + vmGMTOffset = -1 * (daylight ? altzone : timezone) * MicrosecondsPerSecond; +# else +# error: cannot determine timezone correction +# endif +#endif +} + +sqLong +ioHighResClock(void) +{ + /* return the value of the high performance counter */ + sqLong value = 0; +#if defined(__GNUC__) && (defined(i386) || defined(__i386) || defined(__i386__) \ + || defined(x86_64) || defined(__x86_64) || defined (__x86_64__)) + __asm__ __volatile__ ("rdtsc" : "=A"(value)); +#elif defined(__arm__) && (defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_7A__)) + /* tpr - do nothing for now; needs input from eliot to decide further */ +#else +# error "no high res clock defined" +#endif + return value; +} + +unsigned volatile long long +ioUTCMicroseconds() { return get64(utcMicrosecondClock); } + +unsigned volatile long long +ioLocalMicroseconds() { return get64(localMicrosecondClock); } + +sqInt +ioLocalSecondsOffset() { return vmGMTOffset / MicrosecondsPerSecond; } + +/* This is an expensive interface for use by Smalltalk or vm profiling code that + * wants the time now rather than as of the last heartbeat. + */ +unsigned volatile long long +ioUTCMicrosecondsNow() { return currentUTCMicroseconds(); } + +unsigned long long +ioUTCStartMicroseconds() { return utcStartMicroseconds; } + +unsigned volatile long long +ioLocalMicrosecondsNow() { return currentUTCMicroseconds() + vmGMTOffset; }; + +/* ioMSecs answers the millisecondClock as of the last tick. */ +long +ioMSecs() { return millisecondClock; } + +/* ioMicroMSecs answers the millisecondClock right now */ +long ioMicroMSecs(void) { return microToMilliseconds(currentUTCMicroseconds());} + +/* returns the local wall clock time */ +sqInt +ioSeconds(void) { return get64(localMicrosecondClock) / MicrosecondsPerSecond; } + +sqInt +ioSecondsNow(void) { return ioLocalMicrosecondsNow() / MicrosecondsPerSecond; } + +sqInt +ioUTCSeconds(void) { return get64(utcMicrosecondClock) / MicrosecondsPerSecond; } + +sqInt +ioUTCSecondsNow(void) { return currentUTCMicroseconds() / MicrosecondsPerSecond; } + +/* + * On Mac OS X use the following. + * On Unix use dpy->ioRelinquishProcessorForMicroseconds + */ +#if macintoshSqueak +sqInt +ioRelinquishProcessorForMicroseconds(sqInt microSeconds) +{ + long realTimeToWait; + extern usqLong getNextWakeupUsecs(); + usqLong nextWakeupUsecs = getNextWakeupUsecs(); + usqLong utcNow = get64(utcMicrosecondClock); + + if (nextWakeupUsecs <= utcNow) { + /* if nextWakeupUsecs is non-zero the next wakeup time has already + * passed and we should not wait. + */ + if (nextWakeupUsecs != 0) + return 0; + realTimeToWait = microSeconds; + } + else { + realTimeToWait = nextWakeupUsecs - utcNow; + if (realTimeToWait > microSeconds) + realTimeToWait = microSeconds; + } + + aioSleepForUsecs(realTimeToWait); + + return 0; +} +#endif /* !macintoshSqueak */ + +void +ioInitTime(void) +{ + ioUpdateVMTimezone(); /* does updateMicrosecondClock as a side-effect */ + updateMicrosecondClock(); /* this can now compute localUTCMicroseconds */ + utcStartMicroseconds = utcMicrosecondClock; +} + +static void +heartbeat() +{ + int saved_errno = errno; + + updateMicrosecondClock(); + if (get64(frequencyMeasureStart) == 0) { + set64(frequencyMeasureStart,utcMicrosecondClock); + heartbeats = 0; + } + else + heartbeats += 1; + checkHighPriorityTickees(utcMicrosecondClock); + forceInterruptCheckFromHeartbeat(); + + errno = saved_errno; +} + +typedef enum { dead, condemned, nascent, quiescent, active } machine_state; + +#define UNDEFINED 0xBADF00D + +static int stateMachinePolicy = UNDEFINED; +static struct sched_param stateMachinePriority; + +static volatile machine_state beatState = nascent; + +#if !defined(DEFAULT_BEAT_MS) +# define DEFAULT_BEAT_MS 2 +#endif +static int beatMilliseconds = DEFAULT_BEAT_MS; +static struct timespec beatperiod = { 0, DEFAULT_BEAT_MS * 1000 * 1000 }; + +static void * +beatStateMachine(void *careLess) +{ + int er; + if ((er = pthread_setschedparam(pthread_self(), + stateMachinePolicy, + &stateMachinePriority))) { + /* Linux pthreads as of 2009 does not support setting the priority of + * threads other than with real-time scheduling policies. But such + * policies are only available to processes with superuser privileges. + * Linux kernels >= 2.6.13 support different thread priorities, but + * require a suitable /etc/security/limits.d/VMNAME.conf. + */ + extern char *revisionAsString(); + errno = er; + perror("pthread_setschedparam failed"); + fprintf(stderr, + "Read e.g. https://github.com/OpenSmalltalk/opensmalltalk-vm/releases/tag/r3732#linux\n"); + exit(errno); + } + beatState = active; + while (beatState != condemned) { +# define MINSLEEPNS 2000 /* don't bother sleeping for short times */ + struct timespec naptime = beatperiod; + + while (nanosleep(&naptime, &naptime) == -1 + && naptime.tv_sec >= 0 /* oversleeps can return tv_sec < 0 */ + && (naptime.tv_sec > 0 || naptime.tv_nsec > MINSLEEPNS)) /*repeat*/ + if (errno != EINTR) { + perror("nanosleep"); + exit(1); + } + heartbeat(); + } + beatState = dead; + return 0; +} + +void +ioInitHeartbeat() +{ + int er; + struct timespec halfAMo; + pthread_t careLess; + + /* First time through choose a policy and priority for the heartbeat thread, + * and install ioInitHeartbeat via pthread_atfork to be run again in a forked + * child, restarting the heartbeat in a forked child. + */ + if (stateMachinePolicy == UNDEFINED) { + if ((er = pthread_getschedparam(pthread_self(), + &stateMachinePolicy, + &stateMachinePriority))) { + errno = er; + perror("pthread_getschedparam failed"); + exit(errno); + } + assert(stateMachinePolicy != UNDEFINED); + ++stateMachinePriority.sched_priority; + /* If the priority isn't appropriate for the policy (typically + * SCHED_OTHER) then change policy. + */ + if (sched_get_priority_max(stateMachinePolicy) < stateMachinePriority.sched_priority) + stateMachinePolicy = SCHED_FIFO; + pthread_atfork(0, /*prepare*/ 0, /*parent*/ ioInitHeartbeat /*child*/); + } + else /* subsequently (in the child) init beatState before creating thread */ + beatState = nascent; + + halfAMo.tv_sec = 0; + halfAMo.tv_nsec = 1000 * 100; + if ((er= pthread_create(&careLess, + (const pthread_attr_t *)0, + beatStateMachine, + 0))) { + errno = er; + perror("beat thread creation failed"); + exit(errno); + } + while (beatState == nascent) + nanosleep(&halfAMo, 0); +} + +void +ioSetHeartbeatMilliseconds(int ms) +{ + beatMilliseconds = ms; + beatperiod.tv_sec = beatMilliseconds / 1000; + beatperiod.tv_nsec = (beatMilliseconds % 1000) * 1000 * 1000; +} + +int +ioHeartbeatMilliseconds() { return beatMilliseconds; } + + +/* Answer the average heartbeats per second since the stats were last reset. + */ +unsigned long +ioHeartbeatFrequency(int resetStats) +{ + unsigned long duration = (ioUTCMicroseconds() - get64(frequencyMeasureStart)) + / MicrosecondsPerSecond; + unsigned long frequency = duration ? heartbeats / duration : 0; + + if (resetStats) { + unsigned long long zero = 0; + set64(frequencyMeasureStart,zero); + } + return frequency; +} +#endif /* ITIMER_HEARTBEAT */ diff --git a/platforms/minheadless/unix/sqUnixMemory.c b/platforms/minheadless/unix/sqUnixMemory.c new file mode 100644 index 0000000000..15647ca7bf --- /dev/null +++ b/platforms/minheadless/unix/sqUnixMemory.c @@ -0,0 +1,369 @@ +/* sqUnixMemory.c -- dynamic memory management + * + * Author: Ian.Piumarta@squeakland.org + * + * Copyright (C) 1996-2005 by Ian Piumarta and other authors/contributors + * listed elsewhere in this file. + * All rights reserved. + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "sq.h" +#include "sqMemoryAccess.h" +#include "config.h" +#include "debug.h" + +#if !SPURVM /* Spur uses sqUnixSpurMemory.c */ +void *uxAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize); + +/* Note: + * + * The code allows memory to be overallocated; i.e., the initial + * block is reserved via mmap() and then the unused portion + * munmap()ped from the top end. This is INHERENTLY DANGEROUS since + * malloc() may randomly map new memory in the block we "reserved" + * and subsequently unmap()ped. Enabling this causes crashes in + * Croquet, which makes heavy use of the FFI and thus calls malloc() + * all over the place. + * + * For this reason, overallocateMemory is DISABLED by default. + * + * The upshot of all this is that Squeak will claim (and hold on to) + * ALL of the available virtual memory (or at least 75% of it) when + * it starts up. If you can't live with that, use the -memory + * option to allocate a fixed size heap. + */ + +char *uxGrowMemoryBy(char *oldLimit, sqInt delta); +char *uxShrinkMemoryBy(char *oldLimit, sqInt delta); +sqInt uxMemoryExtraBytesLeft(sqInt includingSwap); + +static int pageSize = 0; +static unsigned int pageMask = 0; +int mmapErrno = 0; + +#if defined(HAVE_MMAP) + +#include + +#if !defined(MAP_ANON) +# if defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +# else +# define MAP_ANON 0 +# endif +#endif + +#define MAP_PROT (PROT_READ | PROT_WRITE) +#define MAP_FLAGS (MAP_ANON | MAP_PRIVATE) + +extern int useMmap; +/* Since Cog needs to make memory executable via mprotect, and since mprotect + * only works on mmapped memory we must always use mmap in Cog. + */ +#if COGVM +# define ALWAYS_USE_MMAP 1 +#endif + +#if SQ_IMAGE32 && SQ_HOST64 +char *sqMemoryBase= (char *)-1; +#endif + +/*xxx THESE SHOULD BE COMMAND-LINE/ENVIRONMENT OPTIONS */ +int overallocateMemory = 0; /* see notes above */ + +static int devZero = -1; +static char *heap = 0; +static int heapSize = 0; +static int heapLimit = 0; + +#define valign(x) ((x) & pageMask) + +static int min(int x, int y) { return (x < y) ? x : y; } +static int max(int x, int y) { return (x > y) ? x : y; } + + +/* answer the address of (minHeapSize <= N <= desiredHeapSize) bytes of memory. */ + +#if SPURVM +void * +uxAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ + if (heap) { + fprintf(stderr, "uxAllocateMemory: already called\n"); + exit(1); + } + pageSize= getpagesize(); + pageMask= ~(pageSize - 1); +#else /* SPURVM */ +void *uxAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ +# if !ALWAYS_USE_MMAP + if (!useMmap) + return malloc(desiredHeapSize); +# endif + + if (heap) { + fprintf(stderr, "uxAllocateMemory: already called\n"); + exit(1); + } + pageSize= getpagesize(); + pageMask= ~(pageSize - 1); + + DPRINTF(("uxAllocateMemory: pageSize 0x%x (%d), mask 0x%x\n", pageSize, pageSize, pageMask)); + +# if (!MAP_ANON) + if ((devZero= open("/dev/zero", O_RDWR)) < 0) { + perror("uxAllocateMemory: /dev/zero"); + return 0; + } +# endif + + DPRINTF(("uxAllocateMemory: /dev/zero descriptor %d\n", devZero)); + DPRINTF(("uxAllocateMemory: min heap %d, desired %d\n", minHeapSize, desiredHeapSize)); + + heapLimit= valign(max(desiredHeapSize, useMmap)); + + while ((!heap) && (heapLimit >= minHeapSize)) { + DPRINTF(("uxAllocateMemory: mapping 0x%08x bytes (%d Mbytes)\n", heapLimit, heapLimit >> 20)); + if (MAP_FAILED == (heap= mmap(0, heapLimit, MAP_PROT, MAP_FLAGS, devZero, 0))) { + heap= 0; + heapLimit= valign(heapLimit / 4 * 3); + } + } + + if (!heap) { + fprintf(stderr, "uxAllocateMemory: failed to allocate at least %lld bytes)\n", (long long)minHeapSize); + useMmap= 0; + return malloc(desiredHeapSize); + } + + heapSize= heapLimit; + + if (overallocateMemory) + uxShrinkMemoryBy(heap + heapLimit, heapLimit - desiredHeapSize); + + return heap; +} +#endif /* SPURVM */ + + +static int log_mem_delta = 0; +#define MDPRINTF(foo) if (log_mem_delta) DPRINTF(foo); else 0 + +/* grow the heap by delta bytes. answer the new end of memory. */ + +char *uxGrowMemoryBy(char *oldLimit, sqInt delta) +{ + if (useMmap) + { + int newSize= min(valign(oldLimit - heap + delta), heapLimit); + int newDelta= newSize - heapSize; + MDPRINTF(("uxGrowMemory: %p By: %d(%d) (%d -> %d)\n", oldLimit, newDelta, delta, heapSize, newSize)); + assert(0 == (newDelta & ~pageMask)); + assert(0 == (newSize & ~pageMask)); + assert(newDelta >= 0); + if (newDelta) + { + MDPRINTF(("was: %p %p %p = 0x%x (%d) bytes\n", heap, heap + heapSize, heap + heapLimit, heapSize, heapSize)); + if (overallocateMemory) + { + char *base= heap + heapSize; + MDPRINTF(("remap: %p + 0x%x (%d)\n", base, newDelta, newDelta)); + if (MAP_FAILED == mmap(base, newDelta, MAP_PROT, MAP_FLAGS | MAP_FIXED, devZero, heapSize)) + { + perror("mmap"); + return oldLimit; + } + } + heapSize += newDelta; + MDPRINTF(("now: %p %p %p = 0x%x (%d) bytes\n", heap, heap + heapSize, heap + heapLimit, heapSize, heapSize)); + assert(0 == (heapSize & ~pageMask)); + } + return heap + heapSize; + } + return oldLimit; +} + + +/* shrink the heap by delta bytes. answer the new end of memory. */ + +char *uxShrinkMemoryBy(char *oldLimit, sqInt delta) +{ + if (useMmap) + { + int newSize= max(0, valign((char *)oldLimit - heap - delta)); + int newDelta= heapSize - newSize; + MDPRINTF(("uxGrowMemory: %p By: %d(%d) (%d -> %d)\n", oldLimit, newDelta, delta, heapSize, newSize)); + assert(0 == (newDelta & ~pageMask)); + assert(0 == (newSize & ~pageMask)); + assert(newDelta >= 0); + if (newDelta) + { + MDPRINTF(("was: %p %p %p = 0x%x (%d) bytes\n", heap, heap + heapSize, heap + heapLimit, heapSize, heapSize)); + if (overallocateMemory) + { + char *base= heap + heapSize - newDelta; + MDPRINTF(("unmap: %p + 0x%x (%d)\n", base, newDelta, newDelta)); + if (munmap(base, newDelta) < 0) + { + perror("unmap"); + return oldLimit; + } + } + heapSize -= newDelta; + MDPRINTF(("now: %p %p %p = 0x%x (%d) bytes\n", heap, heap + heapSize, heap + heapLimit, heapSize, heapSize)); + assert(0 == (heapSize & ~pageMask)); + } + return heap + heapSize; + } + return oldLimit; +} + + +/* answer the number of bytes available for growing the heap. */ + +sqInt uxMemoryExtraBytesLeft(sqInt includingSwap) +{ + return useMmap ? (heapLimit - heapSize) : 0; +} + + +#else /* HAVE_MMAP */ + +# if COG +void * +uxAllocateMemory(sqInt minHeapSize, sqInt desiredHeapSize) +{ + if (pageMask) { + fprintf(stderr, "uxAllocateMemory: already called\n"); + exit(1); + } + pageSize = getpagesize(); + pageMask = ~(pageSize - 1); +# if SPURVM + return malloc(desiredHeapSize); +# else + return malloc(desiredHeapSize); +# endif +} +# else /* COG */ +void *uxAllocateMemory(sqInt minHeapSize, sqInt desiredHeapSize) { return malloc(desiredHeapSize); } +# endif /* COG */ +char *uxGrowMemoryBy(char * oldLimit, sqInt delta) { return oldLimit; } +char *uxShrinkMemoryBy(char *oldLimit, sqInt delta) { return oldLimit; } +sqInt uxMemoryExtraBytesLeft(sqInt includingSwap) { return 0; } + +#endif /* HAVE_MMAP */ + + + +#if SQ_IMAGE32 && SQ_HOST64 + +usqInt sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ + sqMemoryBase= uxAllocateMemory(minHeapSize, desiredHeapSize); + if (!sqMemoryBase) return 0; + sqMemoryBase -= SQ_FAKE_MEMORY_OFFSET; + return (sqInt)SQ_FAKE_MEMORY_OFFSET; +} + +sqInt sqGrowMemoryBy(sqInt oldLimit, sqInt delta) +{ + return oopForPointer(uxGrowMemoryBy(pointerForOop(oldLimit), delta)); +} + +sqInt sqShrinkMemoryBy(sqInt oldLimit, sqInt delta) +{ + return oopForPointer(uxShrinkMemoryBy(pointerForOop(oldLimit), delta)); +} + +sqInt sqMemoryExtraBytesLeft(sqInt includingSwap) +{ + return uxMemoryExtraBytesLeft(includingSwap); +} + +#else + +usqInt sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) { return (sqInt)(long)uxAllocateMemory(minHeapSize, desiredHeapSize); } +sqInt sqGrowMemoryBy(sqInt oldLimit, sqInt delta) { return (sqInt)(long)uxGrowMemoryBy((char *)(long)oldLimit, delta); } +sqInt sqShrinkMemoryBy(sqInt oldLimit, sqInt delta) { return (sqInt)(long)uxShrinkMemoryBy((char *)(long)oldLimit, delta); } +sqInt sqMemoryExtraBytesLeft(sqInt includingSwap) { return uxMemoryExtraBytesLeft(includingSwap); } + +#endif + +#define roundDownToPage(v) ((v)&pageMask) +#define roundUpToPage(v) (((v)+pageSize-1)&pageMask) +#if COGVM +void +sqMakeMemoryExecutableFromTo(unsigned long startAddr, unsigned long endAddr) +{ + unsigned long firstPage = roundDownToPage(startAddr); + if (mprotect((void *)firstPage, + endAddr - firstPage + 1, + PROT_READ | PROT_WRITE | PROT_EXEC) < 0) + perror("mprotect(x,y,PROT_READ | PROT_WRITE | PROT_EXEC)"); +} + +void +sqMakeMemoryNotExecutableFromTo(unsigned long startAddr, unsigned long endAddr) +{ +# if 0 + unsigned long firstPage = roundDownToPage(startAddr); + /* Arguably this is pointless since allocated memory always does include + * write permission. Annoyingly the mprotect call fails on both linux & + * mac os x. So make the whole thing a nop. + */ + if (mprotect((void *)firstPage, + endAddr - firstPage + 1, + PROT_READ | PROT_WRITE) < 0) + perror("mprotect(x,y,PROT_READ | PROT_WRITE)"); +# endif +} +#endif /* COGVM */ + +# if defined(TEST_MEMORY) + +# define MBytes *1024*1024 + +int main() +{ + char *mem= sqAllocateMemory(4 MBytes, 40 MBytes); + printf("memory allocated at %p\n", mem); + sqShrinkMemoryBy((int)heap + heapSize, 5 MBytes); + sqGrowMemoryBy((int)heap + heapSize, 1 MBytes); + sqGrowMemoryBy((int)heap + heapSize, 1 MBytes); + sqGrowMemoryBy((int)heap + heapSize, 1 MBytes); + sqGrowMemoryBy((int)heap + heapSize, 100 MBytes); + sqShrinkMemoryBy((int)heap + heapSize, 105 MBytes); + return 0; +} + +# endif /* defined(TEST_MEMORY) */ +#endif /* !SPURVM */ diff --git a/platforms/minheadless/unix/sqUnixSpurMemory.c b/platforms/minheadless/unix/sqUnixSpurMemory.c new file mode 100644 index 0000000000..a86f230383 --- /dev/null +++ b/platforms/minheadless/unix/sqUnixSpurMemory.c @@ -0,0 +1,221 @@ +/* sqUnixSpurMemory.c -- dynamic memory management for Spur on unix & Mac OS X. + * + * Author: eliot.miranda@gmail.com + * + * This file is part of Unix Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "sq.h" +#include "sqMemoryAccess.h" +#include "config.h" +#include "debug.h" + +#if SPURVM + +/* Spur uses a segmented heap; it can add or remove segments, provided they + * are above the first segment. For this scheme to be able to allocate lots + * of memory the first alloc to be at as low an address as possible. + * + * We would like subsequent mmaps to be at ascending addresses, without + * impacting other users of mmap. On the systems we have tested the address + * parameter is only observed if either there is no existing overlapping + * mapping in the [address, address + size) range or if MAP_FIXED is used. + * If MAP_FIXED is not used and there is an existing mapping, mmap answers a + * mapping at a high address. + * + * mmap obeys the address hint if it can. So if mmap answers a mapping at other + * than the hint we can infer there is an extant mapping in [hint, hint+bytes). + * We can then free the mapping and continue the search with a hint further up. + * + * So we find the lowest suitable address for the initial mapping at startup + * via sbrk, and then using the hint passed from the Spur memory manager for + * subsequent mappings. When there is an existing mapping we search for the + * next available gap in e.g. 1 Mb increments. This effectively allocates at + * the the nearest address above that requested (something one might expect + * mmap would do anyway). + */ + +static long pageSize = 0; +static unsigned long pageMask = 0; + +# define roundDownToPage(v) ((v)&pageMask) +# define roundUpToPage(v) (((v)+pageSize-1)&pageMask) + +int mmapErrno = 0; + +# if !defined(HAVE_MMAP) +# error "Spur requires mmap" +# endif +# if !defined(MAP_ANON) && !defined(MAP_ANONYMOUS) +# error "Spur assumes MAP_ANON or MAP_ANONYMOUS" +# error "You're going to have to add a file descriptor." +# error "You can cpy the code in sqUnixMemory.c" +# endif + +# if !defined(MAP_ANON) +# define MAP_ANON MAP_ANONYMOUS +# endif + +static int min(int x, int y) { return (x < y) ? x : y; } +static int max(int x, int y) { return (x > y) ? x : y; } + +/* Answer the address of minHeapSize rounded up to page size bytes of memory. */ + +usqInt +sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ + char *hint, *address, *alloc; + unsigned long alignment; + sqInt allocBytes; + + if (pageSize) { + fprintf(stderr, "sqAllocateMemory: already called\n"); + exit(1); + } + pageSize = getpagesize(); + pageMask = ~(pageSize - 1); + + hint = sbrk(0); + + alignment = max(pageSize,1024*1024); + address = (char *)(((usqInt)hint + alignment - 1) & ~(alignment - 1)); + + alloc = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto + (roundUpToPage(desiredHeapSize), address, &allocBytes); + if (!alloc) { + fprintf(stderr, "sqAllocateMemory: initial alloc failed!\n"); + exit(errno); + } + return (usqInt)alloc; +} + +/* Allocate a region of memory of at least size bytes, at or above minAddress. + * If the attempt fails, answer null. If the attempt succeeds, answer the + * start of the region and assign its size through allocatedSizePointer. + */ +void * +sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress, sqInt *allocatedSizePointer) +{ + char *address, *alloc; + long bytes, delta; + + address = (char *)roundUpToPage((unsigned long)minAddress); + bytes = roundUpToPage(size); + delta = max(pageSize,1024*1024); + + while ((unsigned long)(address + bytes) > (unsigned long)address) { + alloc = mmap(address, bytes, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (alloc == MAP_FAILED) { + mmapErrno = errno; + perror("sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto mmap"); + return 0; + } + if (alloc >= address && alloc <= address + delta) { + *allocatedSizePointer = bytes; + return alloc; + } + /* mmap answered a mapping well away from where Spur prefers. Discard + * the mapping and try again delta higher. + */ + if (munmap(alloc, bytes) != 0) + perror("sqAllocateMemorySegment... munmap"); + address += delta; + } + return 0; +} + +/* Deallocate a region of memory previously allocated by + * sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto. Cannot fail. + */ +void +sqDeallocateMemorySegmentAtOfSize(void *addr, sqInt sz) +{ + if (munmap(addr, sz) != 0) + perror("sqDeallocateMemorySegment... munmap"); +} + +# if COGVM +void +sqMakeMemoryExecutableFromTo(unsigned long startAddr, unsigned long endAddr) +{ + unsigned long firstPage = roundDownToPage(startAddr); + unsigned long size = endAddr - firstPage; + if (mprotect((void *)firstPage, + size, + PROT_READ | PROT_WRITE | PROT_EXEC) < 0) + perror("mprotect(x,y,PROT_READ | PROT_WRITE | PROT_EXEC)"); +} + +void +sqMakeMemoryNotExecutableFromTo(unsigned long startAddr, unsigned long endAddr) +{ + unsigned long firstPage = roundDownToPage(startAddr); + unsigned long size = endAddr - firstPage; + /* Arguably this is pointless since allocated memory always includes write + * permission by default. Annoyingly the mprotect call fails on both linux + * and mac os x. So make the whole thing a nop. + */ + if (mprotect((void *)firstPage, + size, + PROT_READ | PROT_WRITE) < 0) + perror("mprotect(x,y,PROT_READ | PROT_WRITE)"); +} +# endif /* COGVM */ + +# if TEST_MEMORY + +# define MBytes *1024UL*1024UL + +int +main() +{ + char *mem; + usqInt i, t = 16 MBytes; + + printf("hint at %p\n", sbrk(0)); + + mem= (char *)sqAllocateMemory(t, t); + printf("memory allocated at %p\n", mem); + /* create some roadbumps */ + for (i = 80 MBytes; i < 2048UL MBytes; i += 80 MBytes) + printf("roadbump created at %p\n", + mmap(mem + i, pageSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0)); + for (;;) { + sqInt segsz = 0; + char *seg = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(32 MBytes, mem + 16 MBytes, &segsz); + if (!seg) + return 0; + t += segsz; + printf("memory extended at %p (total %ld Mb)\n", seg, t / (1 MBytes)); + } + return 0; +} +# endif /* TEST_MEMORY */ +#endif /* SPURVM */ diff --git a/platforms/minheadless/unix/sqUnixThreads.c b/platforms/minheadless/unix/sqUnixThreads.c new file mode 100644 index 0000000000..4dfff339ee --- /dev/null +++ b/platforms/minheadless/unix/sqUnixThreads.c @@ -0,0 +1,244 @@ +/**************************************************************************** +* PROJECT: Unix (pthreads) thread support code for Stack & Cog VM +* FILE: sqUnixThreads.c +* CONTENT: +* +* AUTHOR: Eliot Miranda +* ADDRESS: +* EMAIL: eliot@teleplace.com +* RCSID: $Id$ +* +* NOTES: See the comment of CogThreadManager in the VMMaker package for +* overall design documentation. +* +*****************************************************************************/ + +#define ForCOGMTVMImplementation 1 + +#include "sq.h" + +#if COGMTVM + +#include /* for ioNumProcesors */ +#include /* for ioNumProcesors */ +#include /* for ioNumProcesors */ +#include +#include + +int +ioNewOSThread(void (*func)(void *), void *arg) +{ + pthread_t newThread; + int err; + + if ((err = pthread_create( + &newThread, /* pthread_t *new_thread_ID */ + 0, /* const pthread_attr_t *attr */ + (void *(*)(void *))func,/* void * (*thread_execp)(void *) */ + (void *)arg /* void *arg */))) { + perror("pthread_create"); + return err; + } + return 0; +} + +void +ioExitOSThread(pthread_t thread) +{ + if (thread == pthread_self()) + pthread_exit(0); + error("not yet implemented"); +} + +int +ioNewOSSemaphore(sqOSSemaphore *sem) +{ + int err; + + sem->count = 0; + return (err = pthread_cond_init(&sem->cond,0)) + || (err = pthread_mutex_init(&sem->mutex,0)) + ? err + : 0; +} + +#define DEBUG 1 +#if DEBUG +# include "sqAtomicOps.h" +int thrlogidx = 0; +char *thrlog[THRLOGSZ]; + +void +dumpThreadLog() +{ + int f = thrlogidx, /* first used entry if non-null */ + l = (f-1) & (THRLOGSZ-1), /* last used entry */ + s = thrlog[f] ? f : 0; /* start */ + + if (!thrlog[s]) + return; + + do { + printf(thrlog[s]); + if (s == l) break; + s = (s + 1) & (THRLOGSZ-1); + } + while (1); +} + +extern pthread_key_t tltiIndex; +#endif +void +ioSignalOSSemaphore(sqOSSemaphore *sem) +{ +#if DEBUG + int tid = ioGetThreadLocalThreadIndex(); + int err; + + if ((err = pthread_mutex_lock(&sem->mutex))) + THRLOG("%d !! SIGN pthread_mutex_lock 0x%p => %d\n", tid, sem, err); + if (++sem->count <= 0) { + THRLOG("%d pthread_cond_signal 0x%x\n", tid, sem); + err = pthread_cond_signal(&sem->cond); + THRLOG("%d pthread_cond_signal 0x%x => %d\n", tid, sem, err); + } + else + THRLOG("%d ioSig 0x%x ++count = %d\n", tid, sem, sem->count); + if ((err = pthread_mutex_unlock(&sem->mutex))) + THRLOG("%d !!pthread_mutex_unlock 0x%p => %d\n", tid, sem, err); +#else + (void)pthread_mutex_lock(&sem->mutex); + if (++sem->count <= 0) + (void)pthread_cond_signal(&sem->cond); + (void)pthread_mutex_unlock(&sem->mutex); +#endif +} + +void +ioWaitOnOSSemaphore(sqOSSemaphore *sem) +{ +#if DEBUG + int tid = ioGetThreadLocalThreadIndex(); + int err; + + if ((err = pthread_mutex_lock(&sem->mutex))) + THRLOG("%d !! WAIT pthread_mutex_lock 0x%p => %d\n", tid, sem, err); + if (--sem->count < 0) { + THRLOG("%d pthread_cond_wait 0x%x\n", tid, sem); + err = pthread_cond_wait(&sem->cond, &sem->mutex); + THRLOG("%d proceeding 0x%x (pcw err %d)\n", tid, sem, err); + } + else + THRLOG("%d ioWait 0x%x --count = %d\n", tid, sem, sem->count); + if ((err = pthread_mutex_unlock(&sem->mutex))) + THRLOG("%d !! WAIT pthread_mutex_unlock 0x%p => %d\n", tid, sem, err); +#else + (void)pthread_mutex_lock(&sem->mutex); + if (--sem->count < 0) + (void)pthread_cond_wait(&sem->cond, &sem->mutex); + (void)pthread_mutex_unlock(&sem->mutex); +#endif +} + + +pthread_key_t tltiIndex; /* clients see this as a const read-only export */ + +static void +initThreadLocalThreadIndices(void) +{ + int err = pthread_key_create(&tltiIndex,0); + if (err) + error("pthread_key_create"); +} + +/* + * ioGetThreadLocalThreadIndex & ioSetThreadLocalThreadIndex are defined in + * sqPlatformSpecific.h. + */ + +/* ioOSThreadIsAlive is defined in sqPlatformSpecific.h. + */ + +int +ioNumProcessors(void) +{ +# if defined(CTL_HW) && defined(HW_AVAILCPU) + int count; + size_t size = sizeof(count); + int hw_availproc[2]; + + hw_availproc[0] = CTL_HW; + hw_availproc[1] = HW_AVAILCPU; + + return sysctl(hw_availproc, 2, &count, &size, 0, 0) + ? 1 + : count; +# elif defined(_SC_NPROCESSORS_ONLN) + int count; + + return (count = sysconf(_SC_NPROCESSORS_ONLN)) == -1 + ? 1 + : count; +# else + extern void warning(char *); + warning("could not determine number of processors; assuming 1"); + return 1; +# endif +} +#else /* COGMTVM */ +/* This is for sqVirtualMachine.h's default ownVM implementation. */ +sqInt +amInVMThread() { return ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread()); } +#endif /* COGMTVM */ + +void +ioInitThreads() +{ + extern void ioInitExternalSemaphores(void); +#if !COGMTVM + /* Set the current VM thread. If the main thread isn't the VM thread then + * when that thread is spawned it can reassign ioVMThread. + */ + ioVMThread = ioCurrentOSThread(); +#endif +#if COGMTVM + initThreadLocalThreadIndices(); +#endif + ioInitExternalSemaphores(); +} + +/* this for testing crash dumps */ +static sqInt +indirect(long p) +{ + if ((p & 2)) + error("crashInThisOrAnotherThread"); + return p > 99 + ? indirect(p - 100), indirect(p - 50) /* evade tail recursion opt */ + : *(sqInt *)p; +} + +/* bit 0 = thread to crash in; 1 => this thread + * bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit + */ +sqInt +crashInThisOrAnotherThread(sqInt flags) +{ + if ((flags & 1)) { + if (!(flags & 2)) + return indirect(flags & ~1); + error("crashInThisOrAnotherThread"); + return 0; + } + else { + pthread_t newThread; + + (void)pthread_create( + &newThread, /* pthread_t *new_thread_ID */ + 0, /* const pthread_attr_t *attr */ + (void *(*)(void *))indirect,/* void * (*thread_execp)(void *) */ + (void *)300 /* void *arg */); + sleep(1); + } + return 0; +} diff --git a/platforms/minheadless/windows/sqGnu.h b/platforms/minheadless/windows/sqGnu.h new file mode 100644 index 0000000000..53f973efbe --- /dev/null +++ b/platforms/minheadless/windows/sqGnu.h @@ -0,0 +1,189 @@ +/* Definitions for "gnuified" interp.c; author: Ian Piumarta (ian.piumarta@inria.fr) + * + * December 10th 2008, Eliot Miranda, updated with FP_REG. + * + * NOTES: + * this file is #included *IN PLACE OF* sq.h + */ + +#include "sq.h" + +#ifdef PROFILE +#define BC_CASE(N) case N: _##N: PROFILE_BYTECODE(N); +#define BC_BREAK PROFILE_BYTECODE_END goto *jumpTable[currentBytecode] +#else +#define BC_CASE(N) case N: _##N: +#define BC_BREAK goto *jumpTable[currentBytecode] +#endif + +#if MULTIPLEBYTECODESETS +# define BC_JUMP_TABLE\ + static void *jumpTable[512] = { \ + &&_0, &&_1, &&_2, &&_3, &&_4, &&_5, &&_6, &&_7, &&_8, &&_9, \ + &&_10, &&_11, &&_12, &&_13, &&_14, &&_15, &&_16, &&_17, &&_18, &&_19, \ + &&_20, &&_21, &&_22, &&_23, &&_24, &&_25, &&_26, &&_27, &&_28, &&_29, \ + &&_30, &&_31, &&_32, &&_33, &&_34, &&_35, &&_36, &&_37, &&_38, &&_39, \ + &&_40, &&_41, &&_42, &&_43, &&_44, &&_45, &&_46, &&_47, &&_48, &&_49, \ + &&_50, &&_51, &&_52, &&_53, &&_54, &&_55, &&_56, &&_57, &&_58, &&_59, \ + &&_60, &&_61, &&_62, &&_63, &&_64, &&_65, &&_66, &&_67, &&_68, &&_69, \ + &&_70, &&_71, &&_72, &&_73, &&_74, &&_75, &&_76, &&_77, &&_78, &&_79, \ + &&_80, &&_81, &&_82, &&_83, &&_84, &&_85, &&_86, &&_87, &&_88, &&_89, \ + &&_90, &&_91, &&_92, &&_93, &&_94, &&_95, &&_96, &&_97, &&_98, &&_99, \ + &&_100, &&_101, &&_102, &&_103, &&_104, &&_105, &&_106, &&_107, &&_108, &&_109, \ + &&_110, &&_111, &&_112, &&_113, &&_114, &&_115, &&_116, &&_117, &&_118, &&_119, \ + &&_120, &&_121, &&_122, &&_123, &&_124, &&_125, &&_126, &&_127, &&_128, &&_129, \ + &&_130, &&_131, &&_132, &&_133, &&_134, &&_135, &&_136, &&_137, &&_138, &&_139, \ + &&_140, &&_141, &&_142, &&_143, &&_144, &&_145, &&_146, &&_147, &&_148, &&_149, \ + &&_150, &&_151, &&_152, &&_153, &&_154, &&_155, &&_156, &&_157, &&_158, &&_159, \ + &&_160, &&_161, &&_162, &&_163, &&_164, &&_165, &&_166, &&_167, &&_168, &&_169, \ + &&_170, &&_171, &&_172, &&_173, &&_174, &&_175, &&_176, &&_177, &&_178, &&_179, \ + &&_180, &&_181, &&_182, &&_183, &&_184, &&_185, &&_186, &&_187, &&_188, &&_189, \ + &&_190, &&_191, &&_192, &&_193, &&_194, &&_195, &&_196, &&_197, &&_198, &&_199, \ + &&_200, &&_201, &&_202, &&_203, &&_204, &&_205, &&_206, &&_207, &&_208, &&_209, \ + &&_210, &&_211, &&_212, &&_213, &&_214, &&_215, &&_216, &&_217, &&_218, &&_219, \ + &&_220, &&_221, &&_222, &&_223, &&_224, &&_225, &&_226, &&_227, &&_228, &&_229, \ + &&_230, &&_231, &&_232, &&_233, &&_234, &&_235, &&_236, &&_237, &&_238, &&_239, \ + &&_240, &&_241, &&_242, &&_243, &&_244, &&_245, &&_246, &&_247, &&_248, &&_249, \ + &&_250, &&_251, &&_252, &&_253, &&_254, &&_255, &&_256, &&_257, &&_258, &&_259, \ + &&_260, &&_261, &&_262, &&_263, &&_264, &&_265, &&_266, &&_267, &&_268, &&_269, \ + &&_270, &&_271, &&_272, &&_273, &&_274, &&_275, &&_276, &&_277, &&_278, &&_279, \ + &&_280, &&_281, &&_282, &&_283, &&_284, &&_285, &&_286, &&_287, &&_288, &&_289, \ + &&_290, &&_291, &&_292, &&_293, &&_294, &&_295, &&_296, &&_297, &&_298, &&_299, \ + &&_300, &&_301, &&_302, &&_303, &&_304, &&_305, &&_306, &&_307, &&_308, &&_309, \ + &&_310, &&_311, &&_312, &&_313, &&_314, &&_315, &&_316, &&_317, &&_318, &&_319, \ + &&_320, &&_321, &&_322, &&_323, &&_324, &&_325, &&_326, &&_327, &&_328, &&_329, \ + &&_330, &&_331, &&_332, &&_333, &&_334, &&_335, &&_336, &&_337, &&_338, &&_339, \ + &&_340, &&_341, &&_342, &&_343, &&_344, &&_345, &&_346, &&_347, &&_348, &&_349, \ + &&_350, &&_351, &&_352, &&_353, &&_354, &&_355, &&_356, &&_357, &&_358, &&_359, \ + &&_360, &&_361, &&_362, &&_363, &&_364, &&_365, &&_366, &&_367, &&_368, &&_369, \ + &&_370, &&_371, &&_372, &&_373, &&_374, &&_375, &&_376, &&_377, &&_378, &&_379, \ + &&_380, &&_381, &&_382, &&_383, &&_384, &&_385, &&_386, &&_387, &&_388, &&_389, \ + &&_390, &&_391, &&_392, &&_393, &&_394, &&_395, &&_396, &&_397, &&_398, &&_399, \ + &&_400, &&_401, &&_402, &&_403, &&_404, &&_405, &&_406, &&_407, &&_408, &&_409, \ + &&_410, &&_411, &&_412, &&_413, &&_414, &&_415, &&_416, &&_417, &&_418, &&_419, \ + &&_420, &&_421, &&_422, &&_423, &&_424, &&_425, &&_426, &&_427, &&_428, &&_429, \ + &&_430, &&_431, &&_432, &&_433, &&_434, &&_435, &&_436, &&_437, &&_438, &&_439, \ + &&_440, &&_441, &&_442, &&_443, &&_444, &&_445, &&_446, &&_447, &&_448, &&_449, \ + &&_450, &&_451, &&_452, &&_453, &&_454, &&_455, &&_456, &&_457, &&_458, &&_459, \ + &&_460, &&_461, &&_462, &&_463, &&_464, &&_465, &&_466, &&_467, &&_468, &&_469, \ + &&_470, &&_471, &&_472, &&_473, &&_474, &&_475, &&_476, &&_477, &&_478, &&_479, \ + &&_480, &&_481, &&_482, &&_483, &&_484, &&_485, &&_486, &&_487, &&_488, &&_489, \ + &&_490, &&_491, &&_492, &&_493, &&_494, &&_495, &&_496, &&_497, &&_498, &&_499, \ + &&_500, &&_501, &&_502, &&_503, &&_504, &&_505, &&_506, &&_507, &&_508, &&_509, \ + &&_510, &&_511 \ + }; +#else /* MULTIPLEBYTECODESETS */ +# define BC_JUMP_TABLE\ + static void *jumpTable[256] = { \ + &&_0, &&_1, &&_2, &&_3, &&_4, &&_5, &&_6, &&_7, &&_8, &&_9,\ + &&_10, &&_11, &&_12, &&_13, &&_14, &&_15, &&_16, &&_17, &&_18, &&_19,\ + &&_20, &&_21, &&_22, &&_23, &&_24, &&_25, &&_26, &&_27, &&_28, &&_29,\ + &&_30, &&_31, &&_32, &&_33, &&_34, &&_35, &&_36, &&_37, &&_38, &&_39,\ + &&_40, &&_41, &&_42, &&_43, &&_44, &&_45, &&_46, &&_47, &&_48, &&_49,\ + &&_50, &&_51, &&_52, &&_53, &&_54, &&_55, &&_56, &&_57, &&_58, &&_59,\ + &&_60, &&_61, &&_62, &&_63, &&_64, &&_65, &&_66, &&_67, &&_68, &&_69,\ + &&_70, &&_71, &&_72, &&_73, &&_74, &&_75, &&_76, &&_77, &&_78, &&_79,\ + &&_80, &&_81, &&_82, &&_83, &&_84, &&_85, &&_86, &&_87, &&_88, &&_89,\ + &&_90, &&_91, &&_92, &&_93, &&_94, &&_95, &&_96, &&_97, &&_98, &&_99,\ + &&_100, &&_101, &&_102, &&_103, &&_104, &&_105, &&_106, &&_107, &&_108, &&_109,\ + &&_110, &&_111, &&_112, &&_113, &&_114, &&_115, &&_116, &&_117, &&_118, &&_119,\ + &&_120, &&_121, &&_122, &&_123, &&_124, &&_125, &&_126, &&_127, &&_128, &&_129,\ + &&_130, &&_131, &&_132, &&_133, &&_134, &&_135, &&_136, &&_137, &&_138, &&_139,\ + &&_140, &&_141, &&_142, &&_143, &&_144, &&_145, &&_146, &&_147, &&_148, &&_149,\ + &&_150, &&_151, &&_152, &&_153, &&_154, &&_155, &&_156, &&_157, &&_158, &&_159,\ + &&_160, &&_161, &&_162, &&_163, &&_164, &&_165, &&_166, &&_167, &&_168, &&_169,\ + &&_170, &&_171, &&_172, &&_173, &&_174, &&_175, &&_176, &&_177, &&_178, &&_179,\ + &&_180, &&_181, &&_182, &&_183, &&_184, &&_185, &&_186, &&_187, &&_188, &&_189,\ + &&_190, &&_191, &&_192, &&_193, &&_194, &&_195, &&_196, &&_197, &&_198, &&_199,\ + &&_200, &&_201, &&_202, &&_203, &&_204, &&_205, &&_206, &&_207, &&_208, &&_209,\ + &&_210, &&_211, &&_212, &&_213, &&_214, &&_215, &&_216, &&_217, &&_218, &&_219,\ + &&_220, &&_221, &&_222, &&_223, &&_224, &&_225, &&_226, &&_227, &&_228, &&_229,\ + &&_230, &&_231, &&_232, &&_233, &&_234, &&_235, &&_236, &&_237, &&_238, &&_239,\ + &&_240, &&_241, &&_242, &&_243, &&_244, &&_245, &&_246, &&_247, &&_248, &&_249,\ + &&_250, &&_251, &&_252, &&_253, &&_254, &&_255\ + }; +#endif /* MULTIPLEBYTECODESETS */ + +/* Compatibility between the old and the new names of the Gnuification macros */ +#define JUMP_TABLE BC_JUMP_TABLE +#define CASE(n) BC_CASE(n) +#define BREAK BC_BREAK + + /* + IP_REG, SP_REG, CB_REG + the machine registers in which to place localIP, localSP and + currentBytecode. Wins big on register-deficient architectures -- + especially Intel. + */ +#if defined(__mips__) +# define IP_REG asm("$16") +# define SP_REG asm("$17") +# define CB_REG asm("$18") +#elif defined(__sparc__) +# define IP_REG asm("%l0") +# define SP_REG asm("%l1") +# define CB_REG asm("%l2") +#elif defined(__alpha__) +# define IP_REG asm("$9") +# define SP_REG asm("$10") +# define CB_REG asm("$11") +#elif defined(__i386__) +# define IP_REG asm("%esi") +# define SP_REG asm("%edi") +# define CB_REG asm("%ebx") +//# define JP_REG asm("%ebx") +#elif defined(PPC) || defined(_POWER) || defined(_IBMR2) +# define IP_REG asm("26") +# define SP_REG asm("27") +# define CB_REG asm("28") +#elif defined(__hppa__) +# define IP_REG asm("%r18") +# define SP_REG asm("%r17") +# define CB_REG asm("%r16") +#elif defined(__mc68000__) +# define IP_REG asm("a5") +# define SP_REG asm("a4") +# define CB_REG asm("d7") +#endif + +#if !defined(CB_REG) +# define CB_REG /* nada */ +#endif +#if !defined(IP_REG) +# define IP_REG /* nada */ +#endif +#if !defined(SP_REG) +# define SP_REG /* nada */ +#endif +#if !defined(FP_REG) +# define FP_REG /* nada */ +#endif + + +#ifdef PROFILE + +#define PROFILE_BYTECODE(x) __asm__("movl %0, _bcProfileCurrent" : : "i" (x)) + +#define PROFILE_BYTECODE_END __asm__("\ + cmpl $0, _profilerActive;\ + je 0f;\ + pushl %ebx;\ + pushl %edx;\ + movl _bcProfileCurrent, %ebx;\ + pushl %eax; \ + rdtsc; \ + subl _bcProfileLow, %eax; \ + sbbl _bcProfileHigh, %edx; \ + incl _bcProfileCountTable(, %ebx, 4);\ + leal _bcProfileTable(, %%ebx, 8), %ebx;\ + addl %eax, 0(%ebx);\ + adcl %edx, 4(%ebx);\ + addl %eax, _bcProfileLow; \ + adcl %edx, _bcProfileHigh; \ + popl %eax;\ + popl %edx;\ + popl %ebx;\ + 0:\ + "); + +#endif diff --git a/platforms/minheadless/windows/sqPlatformSpecific-Win32.c b/platforms/minheadless/windows/sqPlatformSpecific-Win32.c new file mode 100644 index 0000000000..59b6967414 --- /dev/null +++ b/platforms/minheadless/windows/sqPlatformSpecific-Win32.c @@ -0,0 +1,680 @@ +/* sqPlatformSpecific-Win32.c -- Platform specific interface implementation for Windows + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ +/** + * Note: The code present in this file is a result of refactoring the code present + * in the old Squeak Win32 ports. For purpose of copyright, each one of the functions + * present in this file may have an actual author that is different to the author + * of this file. + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include + +#include +#include +#include +#include "sq.h" +#include "sqaio.h" +#include "sqMemoryAccess.h" +#include "sqWin32Backtrace.h" +#include "config.h" + +# define fopen_for_append(filename) fopen(filename,"a+t") + +/* default fpu control word: + _RC_NEAR: round to nearest + _PC_53 : double precision arithmetic (instead of extended) + _EM_XXX: silent operations (no signals please) +*/ +#define FPU_DEFAULT (_RC_NEAR + _PC_53 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL) + +#define MAXFRAMES 64 + +/****************************************************************************/ +/* Exception handling */ +/****************************************************************************/ +/* The following installs us a global exception filter for *all* exceptions */ +/* in Squeak. This is necessary since the C support of Mingw32 for SEH is */ +/* not as sophisticated as MSVC's support. However, with this new handling */ +/* scheme the entire thing becomes actually a lot simpler... */ +/****************************************************************************/ +static LPTOP_LEVEL_EXCEPTION_FILTER TopLevelFilter = NULL; + +/* forwarded declaration */ +static void printCrashDebugInformation(LPEXCEPTION_POINTERS exp); + +#ifndef PROCESS_SYSTEM_DPI_AWARE +#define PROCESS_SYSTEM_DPI_AWARE 1 +#endif + +#ifndef PROCESS_PER_MONITOR_DPI_AWARE +#define PROCESS_PER_MONITOR_DPI_AWARE 2 +#endif + +typedef HRESULT WINAPI (*SetProcessDpiAwarenessFunctionPointer) (int awareness); + +extern int ioIsHeadless(void); +extern const char *getVersionInfo(int verbose); +extern LONG CALLBACK sqExceptionFilter(LPEXCEPTION_POINTERS exp); + +HANDLE vmWakeUpEvent = 0; + +static void +enableHighDPIAwareness() +{ + SetProcessDpiAwarenessFunctionPointer setProcessDpiAwareness; + HMODULE shcore; + + /* Load the library with the DPI awareness library */ + shcore = LoadLibraryA("Shcore.dll"); + if(!shcore) + return; + + /* Get a function pointer to the set DPI awareness function. */ + setProcessDpiAwareness = (SetProcessDpiAwarenessFunctionPointer)GetProcAddress(shcore, "SetProcessDpiAwareness"); + if(!setProcessDpiAwareness) + { + FreeLibrary(shcore); + return; + } + + /* Set the DPI awareness. */ + if(setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) != S_OK) + setProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE); + + FreeLibrary(shcore); +} + +void +ioInitPlatformSpecific(void) +{ + /* Setup the FPU */ + _controlfp(FPU_DEFAULT, _MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC); + + /* Create the wake up event. */ + vmWakeUpEvent = CreateEvent(NULL, 1, 0, NULL); + + /* Use UTF-8 for the console. */ + if (GetConsoleCP()) + { + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + } + + enableHighDPIAwareness(); +} + +void +aioInit(void) +{ +} + +long +aioPoll(long microSeconds) +{ + return 0; +} + +/* New filename converting function; used by the interpreterProxy function +ioFilenamefromStringofLengthresolveAliases. Most platforms can ignore the +resolveAlias boolean - it seems to only be of use by OSX but is crucial there. +*/ +sqInt +sqGetFilenameFromString(char * aCharBuffer, char * aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + memcpy(aCharBuffer, aFilenameString, filenameLength); + aCharBuffer[filenameLength] = 0; + return 0; +} + +sqInt +ioBeep(void) +{ + return 0; +} + +sqInt +ioExit(void) +{ + exit(0); +} + +sqInt +ioExitWithErrorCode(int errorCode) +{ + exit(errorCode); +} + +sqInt +ioRelinquishProcessorForMicroseconds(sqInt microSeconds) +{ + /* wake us up if something happens */ + ResetEvent(vmWakeUpEvent); + MsgWaitForMultipleObjects(1, &vmWakeUpEvent, FALSE, + microSeconds / 1000, QS_ALLINPUT); + ioProcessEvents(); /* keep up with mouse moves etc. */ + return microSeconds; +} + +/* NOTE: Why do we need this? When running multi-threaded code such as in +the networking code and in midi primitives +we will signal the interpreter several semaphores. +(Predates the internal synchronization of signalSemaphoreWithIndex ()) */ + +int +synchronizedSignalSemaphoreWithIndex(int semaIndex) +{ + int result; + + /* Do our job - this is now synchronized in signalSemaphoreWithIndex */ + result = signalSemaphoreWithIndex(semaIndex); + /* wake up interpret() if sleeping */ + SetEvent(vmWakeUpEvent); + return result; +} + +void +ioProfileStatus(sqInt *running, void **exestartpc, void **exelimitpc, + void **vmhst, long *nvmhbin, void **eahst, long *neahbin) +{ +} + +void +ioControlProfile(int on, void **vhp, long *nvb, void **ehp, long *neb) +{ +} + +long +ioControlNewProfile(int on, unsigned long buffer_size) +{ + return 0; +} + +void +ioNewProfileStatus(sqInt *running, long *buffersize) +{ +} + +long +ioNewProfileSamplesInto(void *sampleBuffer) +{ + return 0; +} + +void +ioClearProfile(void) +{ +} + +sqInt +ioDisablePowerManager(sqInt disableIfNonZero) +{ + return true; +} + +void +findExecutablePath(const char *localVmName, char *dest, size_t destSize) +{ + const char *lastSeparator = strrchr(localVmName, '/'); +#ifdef _WIN32 + const char *lastSeparator2 = strrchr(localVmName, '\\'); + if (!lastSeparator || lastSeparator < lastSeparator2) + lastSeparator = lastSeparator2; +#endif + + if (!sqIsAbsolutePath(localVmName)) + { + /* TODO: Get the current working directory*/ + strcpy(dest, "./"); + } + + if (lastSeparator) + strncat(dest, localVmName, lastSeparator - localVmName + 1); +} + +#if COGVM +#include + +/* +* Support code for Cog. +* a) Answer whether the C frame pointer is in use, for capture of the C stack +* pointers. +* b) answer the amount of stack room to ensure in a Cog stack page, including +* the size of the redzone, if any. +*/ +# if defined(_M_IX86) || defined(_M_I386) || defined(_X86_) || defined(i386) || defined(__i386) || defined(__i386__) \ + || defined(x86_64) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(x64) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IA64) +/* +* Cog has already captured CStackPointer before calling this routine. Record +* the original value, capture the pointers again and determine if CFramePointer +* lies between the two stack pointers and hence is likely in use. This is +* necessary since optimizing C compilers for x86 may use %ebp as a general- +* purpose register, in which case it must not be captured. +*/ +int +isCFramePointerInUse() +{ + extern usqIntptr_t CStackPointer, CFramePointer; + extern void(*ceCaptureCStackPointers)(void); + usqIntptr_t currentCSP = CStackPointer; + + currentCSP = CStackPointer; + ceCaptureCStackPointers(); + assert(CStackPointer < currentCSP); + return CFramePointer >= CStackPointer && CFramePointer <= currentCSP; +} +# endif /* defined(i386) || defined(__i386) || defined(__i386__) */ + +/* Answer an approximation of the size of the redzone (if any). Do so by +* sending a signal to the process and computing the difference between the +* stack pointer in the signal handler and that in the caller. Assumes stacks +* descend. +*/ + +#if !defined(min) +# define min(x,y) (((x)>(y))?(y):(x)) +#endif +static char * volatile p = 0; + +static void +sighandler(int sig) { p = (char *)&sig; } + +static int +getRedzoneSize() +{ +#if defined(SIGPROF) /* cygwin */ + struct sigaction handler_action, old; + handler_action.sa_sigaction = sighandler; + handler_action.sa_flags = SA_NODEFER | SA_SIGINFO; + sigemptyset(&handler_action.sa_mask); + (void)sigaction(SIGPROF, &handler_action, &old); + + do kill(getpid(), SIGPROF); while (!p); + (void)sigaction(SIGPROF, &old, 0); + return (char *)min(&old, &handler_action) - sizeof(struct sigaction) - p; +#else /* cygwin */ + void(*old)(int) = signal(SIGBREAK, sighandler); + + do raise(SIGBREAK); while (!p); + return (char *)&old - p; +#endif /* cygwin */ +} + +sqInt reportStackHeadroom; +static int stackPageHeadroom; + +/* Answer the redzone size plus space for any signal handlers to run in. +* N.B. Space for signal handers may include space for the dynamic linker to +* run in since signal handlers may reference other functions, and linking may +* be lazy. The reportheadroom switch can be used to check empirically that +* there is sufficient headroom. +*/ +int +osCogStackPageHeadroom() +{ + if (!stackPageHeadroom) + stackPageHeadroom = getRedzoneSize() + 1024; + return stackPageHeadroom; +} +#endif /* COGVM */ + +static LONG CALLBACK squeakExceptionHandler(LPEXCEPTION_POINTERS exp) +{ + DWORD result; + + /* #1: Try to handle exception in the regular (memory access) + exception filter if virtual memory support is enabled */ +#ifndef NO_VIRTUAL_MEMORY + result = sqExceptionFilter(exp); +#else + result = EXCEPTION_CONTINUE_SEARCH; +#endif + + /* #2: If that didn't work, try to handle any FP problems */ + if(result != EXCEPTION_CONTINUE_EXECUTION) + { + DWORD code = exp->ExceptionRecord->ExceptionCode; + if((code >= EXCEPTION_FLT_DENORMAL_OPERAND) && (code <= EXCEPTION_FLT_UNDERFLOW)) + { + /* turn on the default masking of exceptions in the FPU and proceed */ + _controlfp(FPU_DEFAULT, _MCW_EM | _MCW_RC | _MCW_PC | _MCW_IC); + result = EXCEPTION_CONTINUE_EXECUTION; + } + } + + /* #3: If that didn't work either try passing it on to the old + top-level filter */ + if(result != EXCEPTION_CONTINUE_EXECUTION) + { + if(TopLevelFilter) + result = TopLevelFilter(exp); + } + + /* #4: If that didn't work either give up and print a crash debug information */ +#if defined(NDEBUG) || defined(__MINGW32__) + if(result != EXCEPTION_CONTINUE_EXECUTION) + { + printCrashDebugInformation(exp); + result = EXCEPTION_EXECUTE_HANDLER; + } +#endif + + return result; +} + +void +InstallExceptionHandler(void) +{ + TopLevelFilter = SetUnhandledExceptionFilter(squeakExceptionHandler); +} + +void +UninstallExceptionHandler(void) +{ + SetUnhandledExceptionFilter(TopLevelFilter); + TopLevelFilter = NULL; +} + +int +sqExecuteFunctionWithCrashExceptionCatching(sqFunctionThatCouldCrash function, void *userdata) +{ + int result = 0; + +#if !NO_FIRST_LEVEL_EXCEPTION_HANDLER +# ifndef _MSC_VER + /* Install our top-level exception handler */ + InstallExceptionHandler(); +# else + __try + { +# endif +#endif /* !NO_FIRST_LEVEL_EXCEPTION_HANDLER */ + + result = function(userdata); + +#if !NO_FIRST_LEVEL_EXCEPTION_HANDLER +# ifdef _MSC_VER + } __except(squeakExceptionHandler(GetExceptionInformation())) { + /* Do nothing */ + } +# else + /* remove the top-level exception handler */ + UninstallExceptionHandler(); +# endif +#endif /* !NO_FIRST_LEVEL_EXCEPTION_HANDLER */ + + return result; +} + +static void +dumpStackIfInMainThread(FILE *optionalFile) +{ + extern void printCallStack(void); + + if (!optionalFile) { + if (ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) { + printf("\n\nSmalltalk stack dump:\n"); + printCallStack(); + } + else + printf("\nCan't dump Smalltalk stack. Not in VM thread\n"); + return; + } + if (ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())) { + FILE tmpStdout = *stdout; + fprintf(optionalFile, "\n\nSmalltalk stack dump:\n"); + *stdout = *optionalFile; + printCallStack(); + *optionalFile = *stdout; + *stdout = tmpStdout; + fprintf(optionalFile,"\n"); + } + else + fprintf(optionalFile,"\nCan't dump Smalltalk stack. Not in VM thread\n"); +} + +#if STACKVM +static void dumpPrimTrace(FILE *optionalFile) +{ + extern void dumpPrimTraceLog(void); + + if (optionalFile) { + FILE tmpStdout = *stdout; + *stdout = *optionalFile; + dumpPrimTrace(0); + *optionalFile = *stdout; + *stdout = tmpStdout; + } + else { + printf("\nPrimitive trace:\n"); + dumpPrimTraceLog(); + printf("\n"); + } +} +#else +# define dumpPrimTrace(f) 0 +#endif + + +void +printCommonCrashDumpInfo(FILE *f) { + + /*fprintf(f,"\n\n%s", hwInfoString); + fprintf(f,"\n%s", osInfoString); + fprintf(f,"\n%s", gdInfoString); + */ + + /* print VM version information */ + fprintf(f,"%s\n", getVersionInfo(1)); + fflush(f); + fprintf(f,"\n" + "Current byte code: %d\n" + "Primitive index: %" PRIdSQINT "\n", + getCurrentBytecode(), + methodPrimitiveIndex()); + fflush(f); + /* print loaded plugins */ + fprintf(f,"\nLoaded plugins:\n"); + { + int index = 1; + char *pluginName; + while( (pluginName = ioListLoadedModule(index)) != NULL) { + fprintf(f,"\t%s\n", pluginName); + fflush(f); + index++; + } + } + + printModuleInfo(f); + fflush(f); +} + +static void +printCrashDebugInformation(LPEXCEPTION_POINTERS exp) +{ + void *callstack[MAXFRAMES]; + symbolic_pc symbolic_pcs[MAXFRAMES]; + int nframes, inVMThread; + char crashInfo[1024]; + FILE *f; + int byteCode = -2; + + UninstallExceptionHandler(); + + if ((inVMThread = ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread()))) + /* Retrieve current byte code. + If this fails the IP is probably wrong */ + TRY { + byteCode = getCurrentBytecode(); + } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + byteCode = -1; + } + + TRY { +#if defined(_M_IX86) || defined(_M_I386) || defined(_X86_) || defined(i386) || defined(__i386__) + if (inVMThread) + ifValidWriteBackStackPointersSaveTo((void *)exp->ContextRecord->Ebp, + (void *)exp->ContextRecord->Esp, + 0, + 0); + callstack[0] = (void *)exp->ContextRecord->Eip; + nframes = backtrace_from_fp((void*)exp->ContextRecord->Ebp, + callstack+1, + MAXFRAMES-1); +#elif defined(x86_64) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(x64) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IA64) + if (inVMThread) + ifValidWriteBackStackPointersSaveTo((void *)exp->ContextRecord->Rbp, + (void *)exp->ContextRecord->Rsp, + 0, + 0); + callstack[0] = (void *)exp->ContextRecord->Rip; + nframes = backtrace_from_fp((void*)exp->ContextRecord->Rbp, + callstack+1, + MAXFRAMES-1); +#else +# error "unknown architecture, cannot dump stack" +#endif + symbolic_backtrace(++nframes, callstack, symbolic_pcs); + sqFatalErrorPrintfNoExit( +"Sorry but the VM has crashed.\n\n\ +Exception code: %08X\n\ +Exception address: %08X\n\ +Current byte code: %d\n\ +Primitive index: %d\n\n\ +Crashed in %s thread\n\n\ +This information will be stored in the file\n\ +%s\\%s\n\ +with a complete stack dump", + exp->ExceptionRecord->ExceptionCode, + exp->ExceptionRecord->ExceptionAddress, + byteCode, + methodPrimitiveIndex(), + inVMThread ? L"the VM" : L"some other", + "."/*vmLogDirA*/, + "crash.dmp"); + + /* TODO: prepend the log directory to the crash.dmp file. */ + f = fopen_for_append("crash.dmp"); + printf("File for crash.dmp: %p\n", f); + if(f) + { + time_t crashTime = time(NULL); + fprintf(f,"---------------------------------------------------------------------\n"); + fprintf(f,"%s\n", ctime(&crashTime)); + /* Print the exception code */ + fprintf(f,"Exception code: %08lX\nException addr: %0*" PRIXSQPTR "\n", + exp->ExceptionRecord->ExceptionCode, + (int) sizeof(exp->ExceptionRecord->ExceptionAddress)*2, + (usqIntptr_t) exp->ExceptionRecord->ExceptionAddress); + if(exp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { + /* For access violations print what actually happened */ + fprintf(f,"Access violation (%s) at %0*" PRIXSQPTR "\n", + (exp->ExceptionRecord->ExceptionInformation[0] ? "write access" : "read access"), + (int) sizeof(exp->ExceptionRecord->ExceptionInformation[1])*2, + exp->ExceptionRecord->ExceptionInformation[1]); + } +#if defined(_M_IX86) || defined(_M_I386) || defined(_X86_) || defined(i386) || defined(__i386__) + fprintf(f,"EAX:%08lX\tEBX:%08lX\tECX:%08lX\tEDX:%08lX\n", + exp->ContextRecord->Eax, + exp->ContextRecord->Ebx, + exp->ContextRecord->Ecx, + exp->ContextRecord->Edx); + fprintf(f,"ESI:%08lX\tEDI:%08lX\tEBP:%08lX\tESP:%08lX\n", + exp->ContextRecord->Esi, + exp->ContextRecord->Edi, + exp->ContextRecord->Ebp, + exp->ContextRecord->Esp); + fprintf(f,"EIP:%08lX\tEFL:%08lX\n", + exp->ContextRecord->Eip, + exp->ContextRecord->EFlags); + fprintf(f,"FP Control: %08lX\nFP Status: %08lX\nFP Tag: %08lX\n", + exp->ContextRecord->FloatSave.ControlWord, + exp->ContextRecord->FloatSave.StatusWord, + exp->ContextRecord->FloatSave.TagWord); +#elif defined(x86_64) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__amd64__) || defined(x64) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IA64) + fprintf(f,"RAX:%016" PRIxSQPTR "\tRBX:%016" PRIxSQPTR "\tRCX:%016" PRIxSQPTR "\tRDX:%016" PRIxSQPTR "\n", + exp->ContextRecord->Rax, + exp->ContextRecord->Rbx, + exp->ContextRecord->Rcx, + exp->ContextRecord->Rdx); + fprintf(f,"RSI:%016" PRIxSQPTR "\tRDI:%016" PRIxSQPTR "\tRBP:%016" PRIxSQPTR "\tRSP:%016" PRIxSQPTR "\n", + exp->ContextRecord->Rsi, + exp->ContextRecord->Rdi, + exp->ContextRecord->Rbp, + exp->ContextRecord->Rsp); + fprintf(f,"R8 :%016" PRIxSQPTR "\tR9 :%016" PRIxSQPTR "\tR10:%016" PRIxSQPTR "\tR11:%016" PRIxSQPTR "\n", + exp->ContextRecord->R8, + exp->ContextRecord->R9, + exp->ContextRecord->R10, + exp->ContextRecord->R11); + fprintf(f,"R12:%016" PRIxSQPTR "\tR13:%016" PRIxSQPTR "\tR14:%016" PRIxSQPTR "\tR15:%016" PRIxSQPTR "\n", + exp->ContextRecord->R12, + exp->ContextRecord->R13, + exp->ContextRecord->R14, + exp->ContextRecord->R15); + fprintf(f,"RIP:%016" PRIxSQPTR "\tEFL:%08lx\n", + exp->ContextRecord->Rip, + exp->ContextRecord->EFlags); + fprintf(f,"FP Control: %08x\nFP Status: %08x\nFP Tag: %08x\n", + exp->ContextRecord->FltSave.ControlWord, + exp->ContextRecord->FltSave.StatusWord, + exp->ContextRecord->FltSave.TagWord); +#else +#error "unknown architecture, cannot pick dump registers" +#endif + + fprintf(f, "\n\nCrashed in %s thread\n\n", + inVMThread ? "the VM" : "some other"); + + printCommonCrashDumpInfo(f); + dumpPrimTrace(f); + print_backtrace(f, nframes, MAXFRAMES, callstack, symbolic_pcs); + dumpStackIfInMainThread(f); + fclose(f); + } + + /* print recently called prims to stdout */ + dumpPrimTrace(0); + /* print C stack to stdout */ + print_backtrace(stdout, nframes, MAXFRAMES, callstack, symbolic_pcs); + /* print the caller's stack to stdout */ + dumpStackIfInMainThread(0); +#if COGVM + reportMinimumUnusedHeadroom(); +#endif + } EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + sqFatalErrorPrintf("The VM has crashed. Sorry"); + } +} + + +void *os_exports[][3] = +{ + { 0, 0, 0 } +}; diff --git a/platforms/minheadless/windows/sqPlatformSpecific-Win32.h b/platforms/minheadless/windows/sqPlatformSpecific-Win32.h new file mode 100644 index 0000000000..1960c05261 --- /dev/null +++ b/platforms/minheadless/windows/sqPlatformSpecific-Win32.h @@ -0,0 +1,188 @@ +/* win32 sqPlatformSpecific.h -- Platform-specific prototypes and definitions */ + +/* How to use this file: + This file is for general platform-specific macros and declarations. + The goal is to keep most of the other header files generic across platforms. + To override a definition or macro from sq.h, you must first #undef it, then + provide the new definition. + +*/ + + + +#ifdef WIN32 +/* Override necessary definitions */ +#undef putchar +#include "sqWin32Alloc.h" + + +#ifdef _MSC_VER +#include +#define HAVE_BOOLEAN 1 /* for jpegReaderWriter plugin compatibility */ +#endif + + +#ifdef _MSC_VER +#define squeakFileOffsetType __int64 +#else +#define squeakFileOffsetType unsigned long long +#endif + +#ifdef WIN32_FILE_SUPPORT + +#undef sqImageFile +#undef sqImageFileClose +#undef sqImageFileOpen +#undef sqImageFilePosition +#undef sqImageFileRead +#undef sqImageFileSeek +#undef sqImageFileSeekEnd +#undef sqImageFileWrite + +#define sqImageFile usqIntptr_t +sqInt sqImageFileClose(sqImageFile h); +sqImageFile sqImageFileOpen(const char *fileName, const char *mode); +squeakFileOffsetType sqImageFilePosition(sqImageFile h); +size_t sqImageFileRead(void *ptr, size_t sz, size_t count, sqImageFile h); +squeakFileOffsetType sqImageFileSeek(sqImageFile h, squeakFileOffsetType pos); +squeakFileOffsetType sqImageFileSeekEnd(sqImageFile h, squeakFileOffsetType pos); +size_t sqImageFileWrite(const void *ptr, size_t sz, size_t count, sqImageFile h); +#else /* when no WIN32_FILE_SUPPORT, add necessary stub for using regular Cross/plugins/FilePlugin functions */ +#include +#include /* _get_osfhandle */ +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif + +#define fsync(filenumber) FlushFileBuffers((HANDLE)_get_osfhandle(filenumber)) +#endif /* WIN32_FILE_SUPPORT */ + +/* pluggable primitive support */ +#if defined(_MSC_VER) || defined(__MINGW32__) +# undef EXPORT +# define EXPORT(returnType) __declspec( dllexport ) returnType +# undef VM_EXPORT +# define VM_EXPORT __declspec( dllexport ) + +# if defined(BUILD_VM_CORE) && !defined(VM_CORE_STATIC) +# undef VM_FUNCTION_EXPORT +# define VM_FUNCTION_EXPORT(returnType) __declspec( dllexport ) returnType +# else +# undef VM_FUNCTION_EXPORT +# define VM_FUNCTION_EXPORT(returnType) __declspec( dllimport ) returnType +# endif +#endif + + +/* missing functions */ +#ifdef _MSC_VER +/* see on msdn the list of functions available + * CRT Alphabetical Function Reference + * https://msdn.microsoft.com/en-US/library/634ca0c2.aspx */ +# include +# include +# ifndef alloca +# define alloca _alloca +# endif +# if _MSC_VER < 1800 /* not available before MSVC 2013 */ +# define atoll(x) _atoi64(x) +# define strtoll(beg,end,base) _strtoi64(beg,end,base) + double round(double); +# endif +# if _MSC_VER < 1900 /* not available before MSVC 2015 */ +# define snprintf _snprintf +# ifndef isnan +# define isnan _isnan +# endif +# endif +# if _MSC_VER < 1300 /* maybe not available before MSVC 7.0 2003 ??? */ +# define fabsf(x) ((float)fabs((double)(x))) +# endif +# define bzero(pointer,size) ZeroMemory(pointer,size) +#else +# include +# define bzero(pointer,size) memset(pointer, 0, size) +#endif + +#ifdef __GNUC__ +# if __GNUC__ < 3 +# define fabsf(x) ((float)fabs((double)(x))) /* not sure if really necessary, but was in original file */ +# endif +#endif + +#else +error "Not Win32!" +#endif /* WIN32 */ + +int ioSetCursorARGB(sqInt bitsIndex, sqInt w, sqInt h, sqInt x, sqInt y); + +/* poll and profile thread priorities. The stack vm uses a thread to cause the + * VM to poll for I/O, check for delay expiry et al at regular intervals. Both + * VMs use a thread to sample the pc for VM profiling. The poll thread needs + * to have a priority higher than the main VM thread and the poll thread needs + * to have a priority higher than the poll thread to be able to profile it. + * We would like POLL_THREAD_PRIORITY to be THREAD_PRIORITY_TIME_CRITICAL - 1 + * but SetThreadPriority fails with this value on Windows XP. + * + * N.B. THREAD_PRIORITY_TIME_CRITICAL a.k.a. THREAD_BASE_PRIORITY_LOWRT + * THREAD_PRIORITY_MAX a.k.a. THREAD_BASE_PRIORITY_MAX + * See WinBase.h & WinNT.h. + */ +#if STACKVM +# define POLL_THREAD_PRIORITY THREAD_PRIORITY_HIGHEST +#endif /* STACKVM */ +#define PROF_THREAD_PRIORITY THREAD_PRIORITY_TIME_CRITICAL + +#if COGVM +extern void sqMakeMemoryExecutableFromTo(usqIntptr_t, usqIntptr_t); +extern void sqMakeMemoryNotExecutableFromTo(usqIntptr_t, usqIntptr_t); + +extern int isCFramePointerInUse(void); +extern int osCogStackPageHeadroom(void); +extern void reportMinimumUnusedHeadroom(void); +#endif + +/* Thread support for thread-safe signalSemaphoreWithIndex and/or the COGMTVM */ +#if STACKVM || NewspeakVM +# define sqLowLevelYield() Sleep(0) +/* these are used both in the STACKVM & the COGMTVM */ +# define sqOSThread void * +# define ioOSThreadsEqual(a,b) ((a) == (b)) +# if COGMTVM +/* Please read the comment for CogThreadManager in the VMMaker package for + * documentation of this API. + */ +# define sqOSSemaphore void * +# if !ForCOGMTVMImplementation /* this is a read-only export */ +extern const unsigned long tltiIndex; +# endif +# define ioGetThreadLocalThreadIndex() ((long)TlsGetValue(tltiIndex)) +# define ioSetThreadLocalThreadIndex(v) (TlsSetValue(tltiIndex,(void*)(v))) +# define ioTransferTimeslice() Sleep(0) +# define ioMilliSleep(ms) Sleep(ms) +# endif /* COGMTVM */ +#endif /* STACKVM || NewspeakVM */ + +#if defined(__GNUC__) +# if !defined(VM_LABEL) +# define VM_LABEL(foo) asm("\n.globl L" #foo "\nL" #foo ":") +# endif +#endif +#if !defined(VM_LABEL) || COGVM +# undef VM_LABEL +# define VM_LABEL(foo) 0 +#endif + +#ifdef _MSC_VER +/* disable "function XXXX: no return value" */ +#pragma warning(disable:4035) +/* optional C SEH macros */ +#define TRY __try +#define EXCEPT(filter) __except(filter) +#define FINALLY __finally +#else +/* optional C SEH macros */ +#define TRY +#define EXCEPT(filter) if (0) +#define FINALLY +#endif \ No newline at end of file diff --git a/platforms/minheadless/windows/sqWin32.h b/platforms/minheadless/windows/sqWin32.h new file mode 100644 index 0000000000..108ce0548f --- /dev/null +++ b/platforms/minheadless/windows/sqWin32.h @@ -0,0 +1,19 @@ +#ifndef SQ_WIN32_H +#define SQ_WIN32_H + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +void printLastError(const TCHAR *prefix); +int sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...); + +void sqWin32PrintLastError(const char *message); + +TCHAR *fromSqueak(const char *sqPtr, int sqSize); +TCHAR *fromSqueak2(const char *sqPtr, int sqSize); + +extern BOOL fIsConsole; + +#endif /*SQ_WIN32_H*/ diff --git a/platforms/minheadless/windows/sqWin32Alloc.c b/platforms/minheadless/windows/sqWin32Alloc.c new file mode 100644 index 0000000000..1851b3c723 --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Alloc.c @@ -0,0 +1,207 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32Alloc.c +* CONTENT: Virtual Memory Management +* +* AUTHOR: Andreas Raab (ar) +* ADDRESS: University of Magdeburg, Germany +* EMAIL: raab@isg.cs.uni-magdeburg.de +* +*****************************************************************************/ +#include +#include "sq.h" +#include "sqWin32.h" + +#if !defined(NO_VIRTUAL_MEMORY) && !SPURVM /* Spur uses sqWin32SpurAlloc.c */ + +/* For Qwaq Forums: Disallow memory shrinking to avoid crashes + due to GC/OpenGL relocation problems within glDrawElements. + It appears that in rare circumstances we trigger a full GC + which moves the data away from under OGLs feet and if the + memory gets released at this point OGL may crash. +*/ +#define DO_NOT_SHRINK + + +static LPSTR pageBase; /* base address of allocated memory */ +static DWORD pageMask; /* bit mask for the start of a memory page */ +static DWORD pageSize; /* size of a memory page */ +static DWORD nowReserved; /* 'publicly' reserved virtual memory */ +static LPSTR pageLimit; /* upper limit of commited pages */ +static DWORD maxReserved; /* maximum reserved virtual memory */ +static DWORD usedMemory; /* amount of memory currently in use */ + +#ifndef NDEBUG +/* in debug mode, let the system crash so that we can see where it happened */ +#define EXCEPTION_WRONG_ACCESS EXCEPTION_CONTINUE_SEARCH +#else +/* in release mode, execute the exception handler notifying the user what happened */ +#define EXCEPTION_WRONG_ACCESS EXCEPTION_EXECUTE_HANDLER +#endif + +LONG CALLBACK sqExceptionFilter(LPEXCEPTION_POINTERS exp) +{ + /* always wrong access - we handle memory differently now */ + return EXCEPTION_WRONG_ACCESS; +} + +/************************************************************************/ +/* sqAllocateMemory: Initialize virtual memory */ +/************************************************************************/ +void * +sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ SYSTEM_INFO sysInfo; + DWORD initialCommit, commit; + + /* determine page boundaries */ + GetSystemInfo(&sysInfo); + pageSize = sysInfo.dwPageSize; + pageMask = ~(pageSize - 1); + + /* round the requested size up to the next page boundary */ + nowReserved = (desiredHeapSize + pageSize) & pageMask; + + /* round the initial commited size up to the next page boundary */ + initialCommit = (minHeapSize + pageSize) & pageMask; + + /* Here, we only reserve the maximum memory to be used + It will later be committed during actual access */ + maxReserved = MAX_VIRTUAL_MEMORY; + do { + pageBase = VirtualAlloc(NULL,maxReserved,MEM_RESERVE, PAGE_NOACCESS); + if(!pageBase) { + if(maxReserved == nowReserved) break; + /* make it smaller in steps of 128MB */ + maxReserved -= 128*1024*1024; + if(maxReserved < nowReserved) maxReserved = nowReserved; + } + } while(!pageBase); + if(!pageBase) { + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "Unable to allocate memory (%d bytes requested)", + maxReserved); + return pageBase; + } + /* commit initial memory as requested */ + commit = nowReserved; + if(!VirtualAlloc(pageBase, commit, MEM_COMMIT, PAGE_READWRITE)) { + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "Unable to commit memory (%d bytes requested)", + commit); + return NULL; + } + pageLimit = pageBase + commit; + usedMemory += commit; + return pageBase; +} + +/************************************************************************/ +/* sqGrowMemoryBy: Grow object memory if possible */ +/************************************************************************/ +int +sqGrowMemoryBy(int oldLimit, int delta) { + /* round delta UP to page size */ + if(fShowAllocations) { + warnPrintf("Growing memory by %d...", delta); + } + delta = (delta + pageSize) & pageMask; + if(!VirtualAlloc(pageLimit, delta, MEM_COMMIT, PAGE_READWRITE)) { + if(fShowAllocations) { + warnPrintf("failed\n"); + } + /* failed to grow */ + return oldLimit; + } + /* otherwise, expand pageLimit and return new top limit */ + if(fShowAllocations) { + warnPrintf("okay\n"); + } + pageLimit += delta; + usedMemory += delta; + return (int)pageLimit; +} + +/************************************************************************/ +/* sqShrinkMemoryBy: Shrink object memory if possible */ +/************************************************************************/ +int +sqShrinkMemoryBy(int oldLimit, int delta) { + /* round delta DOWN to page size */ + if(fShowAllocations) { + warnPrintf("Shrinking by %d...",delta); + } +#ifdef DO_NOT_SHRINK + { + /* Experimental - do not unmap memory and avoid OGL crashes */ + if(fShowAllocations) warnPrintf(" - ignored\n"); + return oldLimit; + } +#endif + delta &= pageMask; + if(!VirtualFree(pageLimit-delta, delta, MEM_DECOMMIT)) { + if(fShowAllocations) { + warnPrintf("failed\n"); + } + /* failed to shrink */ + return oldLimit; + } + /* otherwise, shrink pageLimit and return new top limit */ + if(fShowAllocations) { + warnPrintf("okay\n"); + } + pageLimit -= delta; + usedMemory -= delta; + return (int)pageLimit; +} + +/************************************************************************/ +/* sqMemoryExtraBytesLeft: Return memory available to Squeak */ +/************************************************************************/ +int +sqMemoryExtraBytesLeft(int includingSwap) { + DWORD bytesLeft; + MEMORYSTATUS mStat; + + ZeroMemory(&mStat,sizeof(mStat)); + mStat.dwLength = sizeof(mStat); + GlobalMemoryStatus(&mStat); + bytesLeft = mStat.dwAvailPhys; + if(includingSwap) { + bytesLeft += mStat.dwAvailPageFile; + }; + /* max bytes is also limited by maxReserved page size */ + if(bytesLeft > (maxReserved - usedMemory)) { + bytesLeft = maxReserved - usedMemory; + } + return bytesLeft; +} + +#define roundDownToPage(v) ((v)&pageMask) +#define roundUpToPage(v) (((v)+pageSize-1)&pageMask) + +# if COGVM +void +sqMakeMemoryExecutableFromTo(usqIntptr_t startAddr, usqIntptr_t endAddr) +{ + DWORD previous; + + if (!VirtualProtect((void *)startAddr, + endAddr - startAddr + 1, + PAGE_EXECUTE_READWRITE, + &previous)) + sqWin32PrintLastError("VirtualProtect(x,y,PAGE_EXECUTE_READWRITE)"); +} + +void +sqMakeMemoryNotExecutableFromTo(usqIntptr_t startAddr, usqIntptr_t endAddr) +{ + DWORD previous; + + if (!VirtualProtect((void *)startAddr, + endAddr - startAddr + 1, + PAGE_READWRITE, + &previous)) + sqWin32PrintLastError("VirtualProtect(x,y,PAGE_EXECUTE_READWRITE)"); +} +# endif /* COGVM */ +#endif /* !defined(NO_VIRTUAL_MEMORY) && !SPURVM */ diff --git a/platforms/minheadless/windows/sqWin32Alloc.h b/platforms/minheadless/windows/sqWin32Alloc.h new file mode 100644 index 0000000000..c18069f3fc --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Alloc.h @@ -0,0 +1,32 @@ +#ifndef __SQ_WIN32_ALLOC_H +#define __SQ_WIN32_ALLOC_H + +#ifndef NO_VIRTUAL_MEMORY + +/* + Limit the default size for virtual memory to 512MB to avoid nasty + spurious problems when large dynamic libraries are loaded later. + Applications needing more virtual memory can increase the size by + defining it appropriately - here we try to cater for the common + case by using a "reasonable" size that will leave enough space for + other libraries. +*/ +#ifndef MAX_VIRTUAL_MEMORY +#define MAX_VIRTUAL_MEMORY 512*1024*1024 +#endif + +/* Memory initialize-release */ +#undef sqAllocateMemory +#undef sqGrowMemoryBy +#undef sqShrinkMemoryBy +#undef sqMemoryExtraBytesLeft + +void *sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize); +#define allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, fileStream, headerSize) \ +sqAllocateMemory(minimumMemory, heapSize) +int sqGrowMemoryBy(int oldLimit, int delta); +int sqShrinkMemoryBy(int oldLimit, int delta); +int sqMemoryExtraBytesLeft(int includingSwap); + +#endif /* NO_VIRTUAL_MEMORY */ +#endif /* __SQ_WIN32_ALLOC_H */ diff --git a/platforms/minheadless/windows/sqWin32Backtrace.c b/platforms/minheadless/windows/sqWin32Backtrace.c new file mode 100644 index 0000000000..9f54b16cb3 --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Backtrace.c @@ -0,0 +1,631 @@ +/* + * "quick" hack backtrace specific to x86 and win32 compiled with mingw or MSVC. + * Extracts symbols and addresses for the main .exe from a .map file. + * Extracts symbols and addresses for loaded dlls from the dlls themselves. + * + * Exports two functions, backtrace which answers an array of return pcs on the + * call stack of the caller, and symbolic_backtrace which answers an array of + * function name, module name, offset tuples for the supplied pcs. These are + * modelled after the BSD backtrace & backtrace_symbols library functions. + * + * Why are we not using StackWalk64 you ask? StackWalk64 does not work with + * mingw binaries. See e.g. the v8 (a.k.a. chromium) source code. + * + * Eliot Miranda, 7 january 2010. + */ + +#include +#include +#include + +//#undef NDEBUG if you want asserts enabled in the production build +//#define DBGPRINT if you want debug printing turned on + +#include "sq.h" +#include "sqAssert.h" +#include "sqWin32Backtrace.h" +#include "sqWin32.h" +#if COGVM +# include "cogmethod.h" +# if NewspeakVM +# include "nssendcache.h" +# endif +# include "cogit.h" +#endif + +#ifdef _MSC_VER +#include +#endif + +typedef struct frame { + struct frame *savedfp; + void *retpc; +} Frame; + +/* This may be in winnt.h. We know it is not in MinGW 0.3 but is in MinGW v3.17. + * In between we cannot say. + */ +#if defined(__MINGW32_MAJOR_VERSION) && __MINGW32_MAJOR_VERSION < 2 +/* see http://en.wikipedia.org/wiki/Win32_Thread_Information_Block + * & e.g. http://www.nirsoft.net/kernel_struct/vista/NT_TIB.html + */ +typedef struct _NT_TIB +{ + void *ExceptionList; + Frame *StackBase; + Frame *StackLimit; +} ThreadInformationBlock, NT_TIB; +#endif + +#define ulong usqIntptr_t /* enough for holding a pointer - unsigned long does not fit in LLP64 */ + +int +backtrace(void **retpcs, int nrpcs) +{ + void **__fp; + +# if defined(_M_IX86) || defined(_M_I386) || defined(_X86_) || defined(i386) || defined(__i386__) + #if defined(_MSC_VER) + __asm { + mov EAX, EBP + mov [__fp], EAX + } + #elif defined(__GNUC__) + asm volatile ("movl %%ebp, %0" : "=r"(__fp) : ); + #else + # error "don't know how to derive ebp" + #endif +#elif defined(__amd64__) || defined(__amd64) || defined(x86_64) || defined(__x86_64__) || defined(__x86_64) || defined(x64) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IA64) + #if defined(_MSC_VER) + /* __asm { + mov RAX, RBP + mov [__fp], RAX + }*/ + __fp = (void **) _AddressOfReturnAddress(); + #elif defined(__GNUC__) + asm volatile ("movq %%rbp, %0" : "=r"(__fp) : ); + #else + # error "don't know how to derive ebp" + #endif +#else +#error "unknown architecture, cannot pick frame pointer" +#endif + + return backtrace_from_fp(*__fp, retpcs, nrpcs); +} + +int +backtrace_from_fp(void *startfp, void **retpcs, int nrpcs) +{ + Frame *fp; + NT_TIB *tib; + int i = 0; + +# if defined(_M_IX86) || defined(_M_I386) || defined(_X86_) || defined(i386) || defined(__i386__) + #if defined(_MSC_VER) + __asm { + mov EAX, FS:[18h] + mov [tib], EAX + } + #elif defined(__GNUC__) + asm volatile ("movl %%fs:0x18, %0" : "=r" (tib) : ); + #else + # error "don't know how to derive tib" + #endif +#elif defined(__amd64__) || defined(__amd64) || defined(x86_64) || defined(__x86_64__) || defined(__x86_64) || defined(x64) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IA64) + #if defined(_MSC_VER) + /* __asm { + mov RAX, GS:[30h] + mov [tib], RAX + } */ + tib = (NT_TIB *) __readgsqword(0x30); + #elif defined(__GNUC__) + asm volatile ("movq %%gs:0x30, %0" : "=r" (tib) : ); + #else + # error "don't know how to derive tib" + #endif +#else +#error "unknown architecture, cannot pick frame pointer" +#endif + + fp = startfp; + + while (i < nrpcs) { + Frame *savedfp; + + if (!fp->retpc) + break; + retpcs[i++] = fp->retpc; + savedfp = fp->savedfp; + +#define validfp(fp,sp) (((usqInt)(fp) & (sizeof(fp)-1)) == 0 && (char *)(fp) > (char *)(sp)) + + if (savedfp >= (Frame *)tib->StackBase + || !validfp(savedfp,startfp) + || savedfp <= fp) + break; + + fp = savedfp; + } + return i; +} + +/* N.B. This is from psapi.h but we do not link against psapi.dll, so define + * this ourselves. + */ +typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; +} MODULEINFO, *LPMODULEINFO; +typedef struct _dll_exports { + HMODULE module; + char name[MAX_PATH]; + MODULEINFO info; + void (*find_symbol)(struct _dll_exports *, void *, symbolic_pc *); + int n; + char initialized; + ulong *functions; + union { char **funcNames; ulong *funcNameOffsets; sqInt *methods; } u; + int *sorted_ordinals; +} dll_exports; + +static dll_exports *dll_exports_for_pc(void *retpc); + +/* + * Answer pairs of function name, offset in symbolic_pc for the n retpcs. + */ +int +symbolic_backtrace(int n, void **retpcs, symbolic_pc *spc) +{ + int i; + + for (i = 0; i < n; i++) { + dll_exports *exports = dll_exports_for_pc(retpcs[i]); + + if (exports) + exports->find_symbol(exports, retpcs[i], spc + i); + else + spc[i].fnameOrSelector = spc[i].mname = 0, spc[i].offset = 0; + } + return n; +} + +#if COGVM + sqInt addressCouldBeObj(sqInt address); + sqInt byteSizeOf(sqInt oop); + void *firstFixedField(sqInt); +#endif + +void +print_backtrace(FILE *f, int nframes, int maxframes, + void **retpcs, symbolic_pc *symbolic_pcs) +{ + int i; + + fprintf(f, "\nStack backtrace:\n"); +#if COGVM + for (i = 0; i < nframes; ++i) { + char *name; int namelen; + if (addressCouldBeObj((sqInt)symbolic_pcs[i].fnameOrSelector)) { + name = firstFixedField((sqInt)symbolic_pcs[i].fnameOrSelector); + namelen = byteSizeOf((sqInt)symbolic_pcs[i].fnameOrSelector); + } + else { + if (!(name = symbolic_pcs[i].fnameOrSelector)) + name = "???"; + namelen = strlen(name); + } + fprintf(f, + "\t[%p] %.*s + 0x%" PRIxSQPTR " in %s\n", + retpcs[i], + namelen, name, + symbolic_pcs[i].offset, + symbolic_pcs[i].mname); + } +#else + for (i = 0; i < nframes; ++i) + fprintf(f, + "\t[%p] %s + 0x%" PRIxSQPTR " in %s\n", + retpcs[i], + symbolic_pcs[i].fnameOrSelector + ? symbolic_pcs[i].fnameOrSelector + : "???", + symbolic_pcs[i].offset, + symbolic_pcs[i].mname); +#endif + if (nframes == maxframes) + fprintf(f, "\t...\n"); +} + +BOOL (WINAPI *EnumProcessModules)(HANDLE,HMODULE*,DWORD,LPDWORD) = NULL; +BOOL (WINAPI *GetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD)=NULL; + +static DWORD moduleCount = 0; +static dll_exports *all_exports = 0; + +static int +expcmp(const void *a, const void *b) +{ return (sqIntptr_t)((dll_exports *)a)->info.lpBaseOfDll - (sqIntptr_t)((dll_exports *)b)->info.lpBaseOfDll; } + +static void find_in_dll(dll_exports *exports, void *pc, symbolic_pc *spc); +static void find_in_exe(dll_exports *exports, void *pc, symbolic_pc *spc); +#if COGVM +static void find_in_cog(dll_exports *exports, void *pc, symbolic_pc *spc); +extern sqInt nilObject(void); +extern usqInt stackLimitAddress(void); +#endif + +static void +get_modules(void) +{ + DWORD moduleCount2, i; + HANDLE me = GetCurrentProcess(); + HANDLE hPsApi = LoadLibraryA("psapi.dll"); + HMODULE *modules; + + EnumProcessModules = (void*)GetProcAddress(hPsApi, "EnumProcessModules"); + GetModuleInformation=(void*)GetProcAddress(hPsApi, "GetModuleInformation"); + + if (!EnumProcessModules(me, (HMODULE *)&modules, sizeof(modules), &moduleCount)) { + printLastError(TEXT("EnumProcessModules 1")); + return; + } + modules = malloc(moduleCount); +#if COGVM +# define EXTRAMODULES 1 +#else +# define EXTRAMODULES 0 +#endif + all_exports = calloc(moduleCount / sizeof(HMODULE) + EXTRAMODULES, + sizeof(dll_exports)); + if (!modules || !all_exports) { + printLastError(TEXT("get_modules out of memory")); + if (modules) + free(modules); + return; + } + + if (!EnumProcessModules(me, modules, moduleCount, &moduleCount2)) { + printLastError(TEXT("EnumProcessModules 2")); + free(modules); + return; + } + moduleCount /= sizeof(HMODULE); + + for (i = 0; i < moduleCount; i++) { + all_exports[i].module = modules[i]; + if (!GetModuleFileNameA(modules[i], all_exports[i].name, MAX_PATH)) + printLastError(TEXT("GetModuleFileName")); + if (!GetModuleInformation(me, modules[i], &all_exports[i].info, sizeof(MODULEINFO))) + printLastError(TEXT("GetModuleInformation")); + all_exports[i].find_symbol = find_in_dll; + } + free(modules); + assert(GetModuleHandle(0) == all_exports[0].module); + all_exports[0].find_symbol = find_in_exe; +#if COGVM + /* Do not attempt to find addresses in Cog code until VM is initialized!! */ + if (*(char **)stackLimitAddress()) { + strcpy(all_exports[moduleCount].name,"CogCode"); + all_exports[moduleCount].module = (void *)cogCodeBase(); + all_exports[moduleCount].info.lpBaseOfDll = (void *)cogCodeBase(); + /* startOfMemory() => nilObject() is temporary; FIX ME */ + all_exports[moduleCount].info.SizeOfImage = nilObject() - cogCodeBase(); + all_exports[moduleCount].find_symbol = find_in_cog; + ++moduleCount; + } +#endif + qsort(all_exports, moduleCount, sizeof(dll_exports), expcmp); +} + +static dll_exports * +dll_exports_for_pc(void *retpc) +{ + unsigned int i; + + if (!all_exports) + get_modules(); + + if (!all_exports) + return 0; + + for (i = 0; i < moduleCount; i++) + if (all_exports[i].info.lpBaseOfDll <= retpc + && (void *)((ulong)all_exports[i].info.lpBaseOfDll + + all_exports[i].info.SizeOfImage) >= retpc) + return all_exports + i; + + return 0; +} + +static void compute_dll_symbols(dll_exports *exports); + +static void +find_in_dll(dll_exports *exports, void *pcval, symbolic_pc *spc) +{ + int i; + ulong pc = (ulong)pcval - (ulong)exports->info.lpBaseOfDll; + + spc->mname = strrchr(exports->name,'\\') + ? strrchr(exports->name,'\\') + 1 + : (char *)&exports->name; + + if (!exports->initialized) + compute_dll_symbols(exports); + + for (i = 0; i < exports->n; i++) { + ulong addr = exports->functions[exports->sorted_ordinals[i]]; + if (pc >= addr + && ( i + 1 >= exports->n + || pc < exports->functions[exports->sorted_ordinals[i + 1]])) { + spc->fnameOrSelector = (char *) + (exports->u.funcNameOffsets[exports->sorted_ordinals[i]] + + (ulong)exports->module); + spc->offset = pc - addr; + return; + } + } + spc->fnameOrSelector = 0; + spc->offset = pc; +} + +static void compute_exe_symbols(dll_exports *exports); + +static void +find_in_exe(dll_exports *exports, void *pc, symbolic_pc *spc) +{ + int i; + + spc->mname = strrchr(exports->name,'\\') + ? strrchr(exports->name,'\\') + 1 + : (char *)&exports->name; + + if (!exports->initialized) + compute_exe_symbols(exports); + + for (i = 0; i < exports->n; i++) { + ulong addr = exports->functions[i]; + if ((ulong)pc >= addr + && ( i + 1 >= exports->n + || (ulong)pc < exports->functions[i + 1])) { + spc->fnameOrSelector = exports->u.funcNames[i]; + spc->offset = (ulong)pc - addr; + return; + } + } + spc->fnameOrSelector = 0; + spc->offset = (ulong)pc - (ulong)exports->module; +} + +static void +compute_exe_symbols(dll_exports *exports) +{ +#if defined(_MSV_VER) +# error parse of MSVC .map file as yet unimplemented +/* Create the file using "cl .... /link /map" + * Parse it by looking for lines beginning with " 0001:" where 0001 is the + * segment number of the .text segment. Representative lines look like + + 0000:00000000 __except_list 00000000 + 0001:00000000 _printLastError 00401000 f main.obj + 0001:0000005c _crashreport 0040105c f main.obj + 0001:000000ed _main 004010ed f main.obj + + * Be sure to look for global and static symbols. The character "f" tells you + * that this is a line for a function. + */ +#else /* assume a BSD-style nm output as in + * nm --numeric-sort --defined-only -f bsd $(VMEXE) >$(VMMAP) + * where typical lines look like +00400000 A __image_base__ +00401000 t .text +00401000 t __gnu_exception_handler@4 +00401150 t ___mingw_CRTStartup +00401280 T _mainCRTStartup +004012a0 T _WinMainCRTStartup +004012c0 T _atexit +004012d0 T __onexit +004012e0 t .text +004012e0 T _btext +004012f0 t .text +004012f0 t _genoperandoperand + * The characters t & T indicate this is a text symbol. Note duplicates. + */ + +/* Read the entire file into memory. We can use the string as the string table. + * Once we've counted the lines we can parse and read the start addresses. + */ + char filename[MAX_PATH]; + char *contents; + FILE *f; + int pos, len, nlines, n; + + strcpy(filename, exports->name); + strcpy(strrchr(filename,'.')+1,"map"); + + if (!(f = fopen(filename,"r"))) { + printLastError(TEXT("fopen")); + return; + } + fseek(f,0,SEEK_END); + len = ftell(f); + fseek(f,0,SEEK_SET); + if (!(contents = malloc(len))) { + printLastError(TEXT("malloc")); + fclose(f); + return; + } + if (fread(contents, sizeof(char), len, f) != len) { + printLastError(TEXT("fread")); + fclose(f); + return; + } + fclose(f); + + pos = nlines = 0; + while (pos < len) { + if (contents[pos] == '\n') { + nlines++; + contents[pos] = 0; + } + pos++; + } + + if (!(exports->functions = calloc(nlines,sizeof(ulong))) + || !(exports->u.funcNames = calloc(nlines,sizeof(char *)))) { + printLastError(TEXT("malloc")); + fclose(f); + return; + } + pos = n = 0; + while (pos < len) { + ulong addr; + char type, *symname; + + /* Note: Scan format should better be SCNuSQPTR according to C99, if ever different from print format - but we did not define that macro */ + asserta(sscanf(contents + pos, "%" PRIxSQPTR " %c", &addr, &type) == 2); + symname = strrchr(contents + pos, ' ') + 1; + if ((type == 't' || type == 'T') + && strcmp(symname,".text")) { + exports->functions[n] = addr; + exports->u.funcNames[n] = symname; + ++n; + } + pos += strlen(contents + pos) + 1; + } + exports->n = n; +#endif +#if DBGPRINT + for (n = 0; n < exports->n; n++) + printf("exe [%p] %s\n", + exports->functions[exports->sorted_ordinals[n]], + exports->u.funcNames[exports->sorted_ordinals[n]]); +#endif + exports->initialized = 1; +} + +/* See e.g. PEDump + http://msdn.microsoft.com/en-us/library/ms809762.aspx + http://msdn.microsoft.com/en-us/library/bb985994.aspx + */ + +static ulong *funcs_for_ordcmp; +static int +ordcmp(const void *a, const void *b) +{ return funcs_for_ordcmp[*(int *)a] - funcs_for_ordcmp[*(int *)b]; } + +static void +compute_dll_symbols(dll_exports *exports) +{ + char *dllbase = (char *)exports->module; + PIMAGE_EXPORT_DIRECTORY pExportDir; + unsigned int i, j, n; + PWORD ordinals; + ulong *functions; + ulong exportsStartRVA, exportsEndRVA; + + PIMAGE_NT_HEADERS dllhdr; + + dllhdr = (PIMAGE_NT_HEADERS)(dllbase + + ((PIMAGE_DOS_HEADER)exports->module)->e_lfanew); + + if (IsBadReadPtr(dllhdr, sizeof(dllhdr->Signature)) + || dllhdr->Signature != IMAGE_NT_SIGNATURE) { + fprintf(stderr,"Not a Portable Executable (PE) EXE\n"); + return; + } + + exportsStartRVA = dllhdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; + + exportsEndRVA = exportsStartRVA + dllhdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + + pExportDir = (PIMAGE_EXPORT_DIRECTORY) (dllbase + exportsStartRVA); + +#if 0 + time_t timeStamp = pExportDir->TimeDateStamp; + printf(" TimeDateStamp: %08X -> %s", + pExportDir->TimeDateStamp, ctime(&timeStamp) ); + printf(" Version: %u.%02u\n", pExportDir->MajorVersion, + pExportDir->MinorVersion); + printf(" Ordinal base: %08X\n", pExportDir->Base); + printf(" # of functions: %08X\n", pExportDir->NumberOfFunctions); + printf(" # of Names: %08X\n", pExportDir->NumberOfNames); +#endif + + ordinals = (PWORD) (dllbase + (DWORD)pExportDir->AddressOfNameOrdinals); + functions = (PDWORD)(dllbase + (DWORD)pExportDir->AddressOfFunctions); + + if (!(exports->sorted_ordinals = calloc(pExportDir->NumberOfNames, sizeof(int)))) { + printLastError(TEXT("compute_dll_symbols calloc")); + return; + } + exports->functions = functions; + exports->u.funcNameOffsets = (ulong*)(dllbase + (DWORD)pExportDir->AddressOfNames); + + for (i = n = 0; i < pExportDir->NumberOfFunctions; i++) + if (functions[i]) // Skip over gaps in exported function ordinals + // Only record if the function has a name. + for (j=0; j < pExportDir->NumberOfNames; j++) + if (ordinals[j] == i) { + exports->sorted_ordinals[n++] = i; + break; + } + + exports->n = n; + exports->initialized = 1; + funcs_for_ordcmp = exports->functions; + qsort(exports->sorted_ordinals, n, sizeof(int), ordcmp); + +#if DBGPRINT // this to dump the symbols once sorted for checking + for (i = 0; i < n; i++) + printf("%3d %lx %s\n", + exports->sorted_ordinals[i], + exports->functions[exports->sorted_ordinals[i]] + + (ulong)exports->module, + exports->u.funcNameOffsets[exports->sorted_ordinals[i]] + + (ulong)exports->module); +#endif +} + +#if COGVM +static void +find_in_cog(dll_exports *exports, void *pc, symbolic_pc *spc) +{ + CogMethod *cogMethod; + + spc->mname = (char *)&exports->name; + + if ((spc->fnameOrSelector = codeEntryNameFor(pc))) + spc->offset = (ulong)pc - (ulong)codeEntryFor(pc); + else if ((cogMethod = methodFor(pc))) { + spc->fnameOrSelector = cogMethod->selector == nilObject() + ? "Cog method with nil selector" + : (char *)(cogMethod->selector); + spc->offset = (ulong)pc - (ulong)cogMethod; + } + else + spc->offset = (ulong)pc - (ulong)exports->info.lpBaseOfDll; +} +#endif + +void +printModuleInfo(FILE *f) +{ + unsigned int i; + + if (!all_exports) + get_modules(); + + fprintf(f, "\nModule information:\n"); + for (i = 0; i < moduleCount; i++) { + fprintf(f, + "\t%0*" PRIxSQPTR " - %0*" PRIxSQPTR ": %s\n", + (int) sizeof(all_exports[i].info.lpBaseOfDll)*2, + (ulong)all_exports[i].info.lpBaseOfDll, + (int) sizeof(all_exports[i].info.lpBaseOfDll)*2, + ((ulong)all_exports[i].info.lpBaseOfDll) + all_exports[i].info.SizeOfImage, + all_exports[i].name); + fflush(f); + } +} diff --git a/platforms/minheadless/windows/sqWin32Backtrace.h b/platforms/minheadless/windows/sqWin32Backtrace.h new file mode 100644 index 0000000000..ae2f063c9e --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Backtrace.h @@ -0,0 +1,8 @@ +typedef struct { char *mname; void *fnameOrSelector; sqIntptr_t offset; } symbolic_pc; + +extern int backtrace(void *retpcs[], int nrpcs); +extern int backtrace_from_fp(void *fp,void *retpcs[], int nrpcs); +extern int symbolic_backtrace(int n, void *retpcs[], symbolic_pc *spcs); +extern void print_backtrace(FILE *f, int nframes, int maxframes, + void *retpcs[], symbolic_pc *symbolic_pcs); +extern void printModuleInfo(FILE *f); diff --git a/platforms/minheadless/windows/sqWin32Common.c b/platforms/minheadless/windows/sqWin32Common.c new file mode 100644 index 0000000000..10dc24ef2c --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Common.c @@ -0,0 +1,42 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "sq.h" + +BOOL fIsConsole = 0; +BOOL fLowRights = 0; + +#define MAX_BUFFER MAX_PATH +static TCHAR w_buffer1[MAX_BUFFER]; /* wide buffer 1 */ +static TCHAR w_buffer2[MAX_BUFFER]; /* wide buffer 2 */ +static char a_buffer1[MAX_BUFFER]; /* ansi buffer 1 */ +TCHAR squeakIniName[MAX_PATH + 1];; + +static TCHAR * +fromSqueakInto(const char *sqPtr, int sqSize, TCHAR* buffer) +{ + sqUTF8ToUTF16Copy(buffer, sqSize, sqPtr); + return buffer; +} + +TCHAR * +fromSqueak(const char *sqPtr, int sqSize) +{ + return fromSqueakInto(sqPtr, sqSize, w_buffer1); +} + +TCHAR * +fromSqueak2(const char *sqPtr, int sqSize) +{ + return fromSqueakInto(sqPtr, sqSize, w_buffer2); +} + +void +sqWin32PrintLastError(const char *message) +{ + char buffer[256]; + DWORD errorCode; + + errorCode = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT), buffer, sizeof(buffer), 0); + sqWarnPrintf("%s: %s\n", message, buffer); +} diff --git a/platforms/minheadless/windows/sqWin32Directory.c b/platforms/minheadless/windows/sqWin32Directory.c new file mode 100644 index 0000000000..31231819fb --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Directory.c @@ -0,0 +1,419 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32Directory.c +* CONTENT: Directory management +* +* AUTHOR: Andreas Raab (ar) +* ADDRESS: University of Magdeburg, Germany +* EMAIL: raab@isg.cs.uni-magdeburg.de +* +* UPDATES: +* 1) Support for long path names added by using UNC prefix in that case +* (Marcel Taeumel, Hasso Plattner Institute, Postdam, Germany) +* +*****************************************************************************/ +#include + +#include "sq.h" +#include "sqWin32File.h" + +extern struct VirtualMachine *interpreterProxy; + +#define FAIL() { return interpreterProxy->primitiveFail(); } + +/*** + The interface to the directory primitive is path based. + That is, the client supplies a Squeak string describing + the path to the directory on every call. To avoid traversing + this path on every call, a cache is maintained of the last + path seen. +***/ + +/*** Constants ***/ +#define ENTRY_FOUND 0 +#define NO_MORE_ENTRIES 1 +#define BAD_PATH 2 + +/* figure out if a case sensitive duplicate of the given path exists. + useful for trying to stay in sync with case-sensitive platforms. */ +int caseSensitiveFileMode = 0; + +int +hasCaseSensitiveDuplicate(WCHAR *path) { + WCHAR *src, *dst, *prev; + WCHAR* findPath = NULL; + WIN32_FIND_DATAW findData; /* cached find data */ + HANDLE findHandle = 0; /* cached find handle */ + + if(!caseSensitiveFileMode) return 0; + + if(!path) return 0; + if(*path == 0) return 0; + + findPath = (WCHAR*)alloca((wcslen(path) + 1) * sizeof(WCHAR)); + + /* figure out the root of the path (we can't test it) */ + dst = findPath; + src = path; + *dst++ = *src++; + *dst++ = *src++; + if(path[0] == L'\\' && path[1] == L'\\') { + /* \\server\name resp. an UNC path */ + while(*src != 0 && *src != L'\\') *dst++ = *src++; + } else if(path[1] != L':' || path[2] != L'\\') { + /* Oops??? What is this??? */ + printf("hasCaseSensitiveDuplicate: Unrecognized path root\n"); + return 0; + } + *dst = 0; + + /* from the root, enumerate all the path components and find + potential mismatches */ + while(true) { + /* skip backslashes */ + while(*src != 0 && *src == L'\\') src++; + if(!*src) return 0; /* we're done */ + /* copy next path component into findPath */ + *dst++ = L'\\'; + prev = dst; + while(*src != 0 && *src != L'\\') *dst++ = *src++; + *dst = 0; + /* now let's go find it */ + findHandle = FindFirstFileW(findPath, &findData); + /* not finding a path means there is no duplicate */ + if(findHandle == INVALID_HANDLE_VALUE) return 0; + FindClose(findHandle); + { + WCHAR *tmp = findData.cFileName; + while(*tmp) if(*tmp++ != *prev++) break; + if(*tmp == *prev) return 1; /* duplicate */ + } + } +} + +typedef union { + struct { + DWORD dwLow; + DWORD dwHigh; + }; + squeakFileOffsetType offset; +} win32FileOffset; + +DWORD +convertToSqueakTime(SYSTEMTIME st) +{ DWORD secs; + DWORD dy; + static DWORD nDaysPerMonth[14] = { + 0, 0, 31, 59, 90, 120, 151, + 181, 212, 243, 273, 304, 334, 365 }; + /* Squeak epoch is Jan 1, 1901 */ + dy = st.wYear - 1901; /* compute delta year */ + secs = dy * 365 * 24 * 60 * 60 /* base seconds */ + + (dy >> 2) * 24 * 60 * 60; /* seconds of leap years */ + /* check if month > 2 and current year is a leap year */ + if(st.wMonth > 2 && (dy & 0x0003) == 0x0003) + secs += 24 * 60 * 60; /* add one day */ + /* add the days from the beginning of the year */ + secs += (nDaysPerMonth[st.wMonth] + st.wDay - 1) * 24 * 60 * 60; + /* add the hours, minutes, and seconds */ + secs += st.wSecond + 60*(st.wMinute + 60*st.wHour); + return secs; +} + +int +dir_Create(char *pathString, int pathLength) +{ + WCHAR *win32Path = NULL; + + /* convert the path name into a null-terminated C string */ + ALLOC_WIN32_PATH(win32Path, pathString, pathLength); + + return CreateDirectoryW(win32Path,NULL); +} + +int +dir_Delimitor(void) +{ + return '\\'; +} + +int +dir_Lookup(char *pathString, int pathLength, int index, +/* outputs: */ char *name, int *nameLength, int *creationDate, int *modificationDate, + int *isDirectory, squeakFileOffsetType *sizeIfFile) +{ + /* Lookup the index-th entry of the directory with the given path, starting + at the root of the file system. Set the name, name length, creation date, + creation time, directory flag, and file size (if the entry is a file). + Return: + 0 if a entry is found at the given index + 1 if the directory has fewer than index entries + 2 if the given path has bad syntax or does not reach a directory + */ + static WIN32_FIND_DATAW findData; /* cached find data */ + static HANDLE findHandle = 0; /* cached find handle */ + static int lastIndex = 0; /* cached last index */ + static WCHAR *lastString = NULL; /* cached last path */ + static int lastStringLength = 0; /* cached length of last path */ + WCHAR *win32Path = NULL; + int win32PathLength = 0; + FILETIME fileTime; + SYSTEMTIME sysTime; + int i, sz; + + /* default return values */ + *name = 0; + *nameLength = 0; + *creationDate = 0; + *modificationDate = 0; + *isDirectory = false; + *sizeIfFile = 0; + + /* convert the path name into a null-terminated C string */ + ALLOC_WIN32_PATH(win32Path, pathString, pathLength); + win32PathLength = wcslen(win32Path); + + /* check for a dir cache hit (but NEVER on the top level) */ + if(win32PathLength > 0 && + lastStringLength == win32PathLength && + lastIndex + 1 == index) { + for(i=0;i 'Z') + || (nameString[1] != ':')) { + return NO_MORE_ENTRIES; + } + mask = GetLogicalDrives(); + if (mask & (1 << (drive - 'A'))) { + /* found the drive ! */ + name[0] = drive; + name[1] = ':'; + *nameLength = 2; + *creationDate = 0; + *modificationDate = 0; + *isDirectory = true; + *sizeIfFile = 0; + return ENTRY_FOUND; + } + return NO_MORE_ENTRIES; + } +#endif /* !defined(_WIN32_WCE) */ + + if (hasCaseSensitiveDuplicate(win32Path)) { + return BAD_PATH; + } + + /* Ensure trailing delimiter and add filename. */ + if(win32Path[win32PathLength-1] != L'\\') win32PathLength++; + fsz = MultiByteToWideChar(CP_UTF8, 0, nameString, nameStringLength, NULL, 0); + sz = win32PathLength; + win32PathLength += fsz; + if(win32PathLength >= 32767) FAIL(); + REALLOC_WIN32_PATH(win32Path, win32PathLength); + win32Path[win32PathLength-fsz-1] = L'\\'; + MultiByteToWideChar(CP_UTF8, 0, nameString, nameStringLength, win32Path+sz, fsz); + win32Path[win32PathLength] = 0; // Not needed. See REALLOC_WIN32_PATH. + + if(!GetFileAttributesExW(win32Path, 0, &winAttrs)) { + return NO_MORE_ENTRIES; + } + + memcpy(name, nameString, nameStringLength); + *nameLength = nameStringLength; + + FileTimeToLocalFileTime(&winAttrs.ftCreationTime, &fileTime); + FileTimeToSystemTime(&fileTime, &sysTime); + *creationDate = convertToSqueakTime(sysTime); + FileTimeToLocalFileTime(&winAttrs.ftLastWriteTime, &fileTime); + FileTimeToSystemTime(&fileTime, &sysTime); + *modificationDate = convertToSqueakTime(sysTime); + + if (winAttrs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + *isDirectory= true; + else { + win32FileOffset ofs; + ofs.dwLow = winAttrs.nFileSizeLow; + ofs.dwHigh = winAttrs.nFileSizeHigh; + *sizeIfFile = ofs.offset; + } + + return ENTRY_FOUND; +} + + +int +dir_SetMacFileTypeAndCreator(char *filename, int filenameSize, + char *fType, char *fCreator) +{ + /* Win32 files are untyped, and the creator is correct by default */ + return true; +} + +int +dir_GetMacFileTypeAndCreator(char *filename, int filenameSize, + char *fType, char *fCreator) +{ + /* Win32 files are untyped, and the creator is correct by default */ + FAIL(); +} + +int +dir_Delete(char *pathString, int pathLength) { + /* Delete the existing directory with the given path. */ + WCHAR *win32Path = NULL; + + /* convert the file name into a null-terminated C string */ + ALLOC_WIN32_PATH(win32Path, pathString, pathLength); + + if(hasCaseSensitiveDuplicate(win32Path)) return false; + return RemoveDirectoryW(win32Path) == 0 ? false : true; +} diff --git a/platforms/minheadless/windows/sqWin32HandleTable.h b/platforms/minheadless/windows/sqWin32HandleTable.h new file mode 100644 index 0000000000..5e2fa342d1 --- /dev/null +++ b/platforms/minheadless/windows/sqWin32HandleTable.h @@ -0,0 +1,127 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32HandleTable.h +* CONTENT: Handle table +* +* AUTHOR: Andreas Raab (ar) +* ADDRESS: +* EMAIL: andreas.raab@gmx.de +* +* NOTES: +* 1) This is a simple handle table which can be used to verify whether +* a HANDLE is genuine (e.g., created by a plugin) or not. Useful to +* ensure that operations are only defined over handles that were +* originally created by a plugin and wasn't fabricated in some other way. +* Used (for example) by the file plugin to ensure integrity of file +* operations. See sqWin32FilePrims.c for an example. +* 2) The HandleTable is used by merely including it and then defining +* HandleTable *myTable; +* to use it. Again, see sqWin32FilePrims.c for an example. +*****************************************************************************/ + +#ifndef SQ_WIN32_HANDLE_TABLE +#define SQ_WIN32_HANDLE_TABLE + +typedef struct { + int count; /* number of elements in table */ + int size; /* size of data array */ + HANDLE *data; /* actual data */ +} HandleTable; + +/* Private. Scan table for the slot containing either NULL or the item */ +static int FindHandleInTable(HandleTable *table, HANDLE item) { + int start, index; + HANDLE element, *data = table->data; + + if(0 == table->size) return -1; /* so we don't explode below */ + /* Compute initial index */ + start = ((usqIntptr_t)item) % table->size; + /* search from (hash mod size) to end */ + for(index = start; index < table->size; index++) { + element = data[index]; + if(NULL == element || item == element) return index; + } + /* search from 0 to where we started */ + for(index = 0; index < start; index++) { + element = data[index]; + if(NULL == element || item == element) return index; + } + return -1; /* no match and no empty slot */ +} + +/* Private. Grow table to specified size. */ +static void GrowTableToSize(HandleTable *table, int newSize) { + HANDLE *oldData = table->data; + int i, oldSize = table->size; + /* grow table size */ + table->size = newSize; + /* allocate new table array */ + table->data = (HANDLE*) calloc(newSize,sizeof(HANDLE)); + /* copy elements from old to new */ + for(i = 0; i < oldSize; i++) { + HANDLE element = oldData[i]; + int index = FindHandleInTable(table, element); + table->data[index] = element; + } + /* free old data */ + if(oldData) free(oldData); +} + +/* Private. Fix a collision chain after removing an element */ +static void FixCollisionsInTable(HandleTable *table, int index) { + HANDLE element, *data = table->data; + int newIndex, oldIndex = index; + int length = table->size; + while(1) { + if(++oldIndex == length) oldIndex = 0; + element = data[oldIndex]; + if(NULL == element) break; /* we're done here */ + newIndex = FindHandleInTable(table, element); + if(newIndex != oldIndex) { + HANDLE tmp = data[oldIndex]; + data[oldIndex] = data[newIndex]; + data[newIndex] = tmp; + } + } +} + +/* Public. Add a handle to an existing table */ +static void AddHandleToTable(HandleTable *table, HANDLE item) { + int index; + + if(NULL == item) return; /* silently ignore NULL handles */ + index = FindHandleInTable(table, item); + if(index == -1) { /* grow and retry */ + GrowTableToSize(table, table->size > 1 ? table->size*2-1 : 5); + index = FindHandleInTable(table, item); + } + table->data[index] = item; + table->count++; +} + +/* Public. Remove a handle from some table */ +static void RemoveHandleFromTable(HandleTable *table, HANDLE item) { + int index; + if(NULL == item) return; /* silently ignore NULL handles */ + index = FindHandleInTable(table, item); + if(index == -1) return; /* not in table */ + if(NULL == table->data[index]) return; /* not in table */ + table->data[index] = NULL; /* clear entry */ + table->count--; + FixCollisionsInTable(table, index); /* fix collision chain */ + /* Shrink table by half if 75% free but allow no less than 5 entries*/ + if(table->size > 5 && table->count*4 < table->size ) { + GrowTableToSize(table, (table->size + 1) / 2); + } +} + +/* Public. Test if a handle is in this table */ +static int IsHandleInTable(HandleTable *table, HANDLE item) { + int index; + if(NULL == item) return 0; /* silently ignore NULL handles */ + index = FindHandleInTable(table, item); + if(index == -1) return 0; + return table->data[index] == item; +} + +#endif /* SQ_WIN32_HANDLE_TABLE */ diff --git a/platforms/minheadless/windows/sqWin32Heartbeat.c b/platforms/minheadless/windows/sqWin32Heartbeat.c new file mode 100644 index 0000000000..ebb36e563e --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Heartbeat.c @@ -0,0 +1,479 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32Heartbeat.c +* CONTENT: Win32 time functions and heartbeat logic for Stack VM +* +* AUTHOR: Eliot Miranda +* +* NOTES: +* August 3rd, 2008, EEM added heart-beat thread. +* +*****************************************************************************/ + +#include +#include + +#include "sq.h" +#include "sqAssert.h" +#include "sqMemoryFence.h" + +/* + * Win32 FILETIMEs are 10th's of microseconds since 1601. Smalltalk times + * are seconds from 1901. Let's call a 10th of a microsecond a "tock". + */ + +#if _MSC_VER +# define SecondsFrom1601To1901 9467020800i64 /*See PRINT_TIME_CONSTANTS below*/ +# define MicrosecondsFrom1601To1901 9467020800000000i64 + +# define MicrosecondsPerSecond 1000000i64 +# define MillisecondsPerSecond 1000i64 + +# define MicrosecondsPerMillisecond 1000i64 + +# define TocksPerSecond 10000000i64 +# define TocksPerMillisecond 10000i64 +# define TocksPerMicrosecond 10i64 +# define LLFMT "I64d" +#else +# define SecondsFrom1601To1901 9467020800LL /*See PRINT_TIME_CONSTANTS below*/ +# define MicrosecondsFrom1601To1901 9467020800000000LL + +# define MicrosecondsPerSecond 1000000LL +# define MillisecondsPerSecond 1000LL + +# define MicrosecondsPerMillisecond 1000LL + +# define TocksPerSecond 10000000LL +# define TocksPerMillisecond 10000LL +# define TocksPerMicrosecond 10LL +# define LLFMT "lld" +#endif + +static DWORD dwTimerPeriod = 0; +static DWORD timerID = 0; + +sqLong +ioHighResClock(void) { + sqLong value = 0; +#ifdef __GNUC__ + __asm__ __volatile__ ("rdtsc" : "=A" (value)); +#endif + return value; +} + +/* Compute the current VM time basis, the number of microseconds from 1901. + * + * Alas Windows' system time functions GetSystemTime et al have low resolution; + * 15 ms. So we use timeGetTime for higher resolution and use it as an offset to + * the system time, resetting when timeGetTime wraps. Since timeGetTime wraps we + * need some basis information which is passed in as pointers to provide us with + * both the heartbeat clock and an instantaneous clock for the VM thread. + */ + +static unsigned __int64 +currentUTCMicroseconds(unsigned __int64 *utcTickBaseUsecsp, DWORD *lastTickp, DWORD *baseTickp) +{ + FILETIME utcNow; + DWORD currentTick = timeGetTime(); + DWORD prevTick = *lastTickp; + + *lastTickp = currentTick; + + /* If the timeGetTime millisecond clock wraps (as it will every 49.71 days) + * resync to the system time. + */ + if (currentTick < prevTick) { + + *baseTickp = currentTick; + GetSystemTimeAsFileTime(&utcNow); + *utcTickBaseUsecsp = *(unsigned __int64 *)&utcNow + / TocksPerMicrosecond + - MicrosecondsFrom1601To1901; + return *utcTickBaseUsecsp; + } + return *utcTickBaseUsecsp + + (currentTick - *baseTickp) * MicrosecondsPerMillisecond; +} + + +/* The 64-bit clocks. utcMicrosecondClock is utc microseconds from 1901. + * localMicrosecondClock is local microseconds from 1901. The 32-bit clock + * millisecondClock is milliseconds since system start. These are updated + * by the heartbeat thread at up to 1KHz. + */ +static unsigned volatile __int64 utcMicrosecondClock; +static unsigned volatile __int64 localMicrosecondClock; +static unsigned volatile long millisecondClock; /* for the ioMSecs clock. */ +static unsigned __int64 utcStartMicroseconds; +static signed __int64 vmGMTOffset = 0; + +/* The bases that relate timeGetTime's 32-bit wrapping millisecond clock to the + * non-wrapping 64-bit microsecond clocks. + */ +static unsigned __int64 utcTickBaseMicroseconds; +static DWORD lastTick = (DWORD)-1; +static DWORD baseTick; +static unsigned __int64 vmThreadUtcTickBaseMicroseconds; +static DWORD vmThreadLastTick = (DWORD)-1; +static DWORD vmThreadBaseTick; + +#define microToMilliseconds(usecs) ((((usecs) - utcStartMicroseconds) \ + / MicrosecondsPerMillisecond) \ + & MillisecondClockMask) + +#define LOG_CLOCK 1 + +#if LOG_CLOCK +# define LOGSIZE 1024 +static unsigned __int64 useclog[LOGSIZE]; +static unsigned long mseclog[LOGSIZE]; +static int logClock = 1; +static unsigned int ulogidx = (unsigned int)-1; +static unsigned int mlogidx = (unsigned int)-1; +# define logusecs(usecs) do { sqLowLevelMFence(); \ + if (logClock) useclog[++ulogidx % LOGSIZE] = (usecs); \ + } while (0) +# define logmsecs(msecs) do { sqLowLevelMFence(); \ + if (logClock) mseclog[++mlogidx % LOGSIZE] = (msecs); \ + } while (0) +void +ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *runInNOutp, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip) +{ + logClock = *runInNOutp; + sqLowLevelMFence(); + *runInNOutp = LOGSIZE; + *usecsp = useclog; + *uip = ulogidx % LOGSIZE; + *msecsp = mseclog; + *mip = mlogidx % LOGSIZE; +} +#else /* LOG_CLOCK */ +# define logusecs(usecs) 0 +# define logmsecs(msecs) 0 +void +ioGetClockLogSizeUsecsIdxMsecsIdx(sqInt *np, void **usecsp, sqInt *uip, void **msecsp, sqInt *mip) +{ + *np = *uip = *mip = 0; + *usecsp = *msecsp = 0; +} +#endif /* LOG_CLOCK */ + +/* + * Update the utc and local microsecond clocks. Since this is invoked from + * a high-priority thread, and since the clocks are 64-bit values that are read + * concurrently by the VM, care must be taken to access these values atomically + * on 32-bit systems. If they are not accessed atomically there is a possibility + * of fetching the two halves of the clock from different ticks which would + * cause a jump in the clock of 2^32 microseconds (1 hr, 11 mins, 34 secs). + * + * Since an interrupt could occur between any two instructions the clock must be + * read atomically as well as written atomically. If possible this can be + * implemented without locks using atomic 64-bit reads and writes. + */ + +#include "sqAtomicOps.h" + +static void +updateMicrosecondClock() +{ + unsigned __int64 newUtcMicrosecondClock, newLocalMicrosecondClock; + + newUtcMicrosecondClock = currentUTCMicroseconds(&utcTickBaseMicroseconds, + &lastTick, + &baseTick); + /* The native clock may go backwards, e.g. when timeGetTime wraps and we sync + * with the system clock, which, having only 15ms resolution, could cause a + * backward step. Of course this will cause problems if the clock is manually + * adjusted. To which the doctor says, "don't do that". + */ + if (!asserta(newUtcMicrosecondClock >= utcMicrosecondClock)) { + logusecs(0); /* if logging log a backward step as 0 */ + return; + } + newLocalMicrosecondClock = newUtcMicrosecondClock + vmGMTOffset; + + set64(utcMicrosecondClock,newUtcMicrosecondClock); + set64(localMicrosecondClock,newLocalMicrosecondClock); + millisecondClock = microToMilliseconds(newUtcMicrosecondClock); + + logusecs(newUtcMicrosecondClock); + logmsecs(millisecondClock); +} + +void +ioUpdateVMTimezone() +{ + __int64 utcNow, localNow; + + updateMicrosecondClock(); + GetSystemTimeAsFileTime((FILETIME *)&utcNow); + FileTimeToLocalFileTime((FILETIME *)&utcNow,(FILETIME *)&localNow); + vmGMTOffset = (localNow - utcNow) / (__int64)TocksPerMicrosecond; +} + +void +ioInitTime(void) +{ + ioUpdateVMTimezone(); + updateMicrosecondClock(); /* this can now compute localUTCMicroseconds */ + utcStartMicroseconds = utcMicrosecondClock; +} + +unsigned volatile long long +ioUTCMicroseconds() { return get64(utcMicrosecondClock); } + +unsigned volatile long long +ioLocalMicroseconds() { return get64(localMicrosecondClock); } + +sqInt +ioLocalSecondsOffset() { return vmGMTOffset / MicrosecondsPerSecond; } + +/* This is an expensive interface for use by Smalltalk or vm profiling code that + * wants the time now rather than as of the last heartbeat. + */ +unsigned volatile long long +ioUTCMicrosecondsNow() +{ + return currentUTCMicroseconds(&vmThreadUtcTickBaseMicroseconds, + &vmThreadLastTick, + &vmThreadBaseTick); +} + +unsigned long long +ioUTCStartMicroseconds() { return utcStartMicroseconds; } + +unsigned volatile long long +ioLocalMicrosecondsNow() { return ioUTCMicrosecondsNow() + vmGMTOffset; }; + +/* ioMSecs answers the millisecondClock as of the last tick. */ +long +ioMSecs() { return millisecondClock; } + +/* ioMicroMSecs answers the millisecondClock right now */ +long ioMicroMSecs(void) { return microToMilliseconds(ioUTCMicrosecondsNow());} + +/* returns the local wall clock time */ +sqInt +ioSeconds(void) { return get64(localMicrosecondClock) / MicrosecondsPerSecond; } + +sqInt +ioSecondsNow(void) { return ioLocalMicrosecondsNow() / MicrosecondsPerSecond; } + +sqInt +ioUTCSeconds(void) { return get64(utcMicrosecondClock) / MicrosecondsPerSecond; } + +sqInt +ioUTCSecondsNow(void) { return ioUTCMicrosecondsNow() / MicrosecondsPerSecond; } + + +typedef enum { dead, condemned, nascent, quiescent, active } machine_state; + +static machine_state beatThreadState = nascent; + +#if !defined(DEFAULT_BEAT_MS) +# define DEFAULT_BEAT_MS 1 +#endif +static long beatMilliseconds = DEFAULT_BEAT_MS; +static HANDLE beatSemaphore; +static HANDLE heartbeatThread; +static unsigned __int64 frequencyMeasureStart; +static unsigned long heartbeats; + +static void +heartbeat() +{ + updateMicrosecondClock(); + if (get64(frequencyMeasureStart) == 0) { + set64(frequencyMeasureStart,utcMicrosecondClock); + heartbeats = 0; + } + else + heartbeats += 1; + checkHighPriorityTickees(utcMicrosecondClock); + forceInterruptCheckFromHeartbeat(); +} + +static DWORD WINAPI +beatThreadStateMachine(void *careLess) +{ + beatThreadState = active; + while (beatThreadState != condemned) { + DWORD res = WaitForSingleObject(beatSemaphore, + beatThreadState == active + ? beatMilliseconds + : INFINITE); + if (res == WAIT_TIMEOUT +#if !defined(_WIN32_WCE) // for pulsing by timeSetEvent below + || res == WAIT_OBJECT_0 +#endif + ) + heartbeat(); + else if (res == WAIT_FAILED) + abortMessage(TEXT("Fatal: WaitForSingleObject(beatSemaphore) %ld"), + GetLastError()); + else + printLastError(TEXT("beatThreadStateMachine WaitForSingleObject")); + } + beatThreadState = dead; + return 0; +} + +/* Answer the average heartbeats per second since the stats were last reset. + */ +unsigned long +ioHeartbeatFrequency(int reset) +{ + unsigned long duration = (ioUTCMicroseconds() - get64(frequencyMeasureStart)) + / MicrosecondsPerSecond; + unsigned frequency = duration ? heartbeats / duration : 0; + + if (reset) { + unsigned __int64 zero = 0; + set64(frequencyMeasureStart,zero); + } + return frequency; +} + +/* + * If we're using just a poll thread (current default) then it also decides + * the delay resolution and so we choose THREAD_PRIORITY_TIME_CRITICAL. + */ +void +ioInitHeartbeat() +{ + DWORD uselessThreadId; +extern sqInt suppressHeartbeatFlag; + + if (suppressHeartbeatFlag) return; + + beatSemaphore = CreateSemaphore(NULL, /*no security*/ + 0, /*no initial signals*/ + 65535, /*no limit on num signals*/ + NULL /*anonymous*/); + heartbeatThread = CreateThread(0, /* default security attributes (none) */ + 2048, /* thread stack bytes (ignored!! read it and weep) */ + beatThreadStateMachine, + 0, /* beatThreadStateMachine argument */ + 0 | STACK_SIZE_PARAM_IS_A_RESERVATION, /* creation flags, 0 => run immediately, SSPIAR implies don't commit memory to stack */ + &uselessThreadId); + if (!heartbeatThread + || !SetThreadPriority(heartbeatThread, POLL_THREAD_PRIORITY)) + abortMessage(TEXT("Fatal error: poll thread init failure %ld"), GetLastError()); + + while (beatThreadState == nascent) + if (WaitForSingleObject(beatSemaphore, 1) == WAIT_FAILED) + abortMessage(TEXT("Fatal: WaitForSingleObject(beatSemaphore) %ld"), + GetLastError()); + + ioSetHeartbeatMilliseconds(beatMilliseconds); +} + +int +ioHeartbeatMilliseconds() { return beatMilliseconds; } + +void +ioSetHeartbeatMilliseconds(int ms) +{ + if (ms != beatMilliseconds) { + beatMilliseconds = ms; + /* + * We only break the poll state machine out of its loop if it is + * inactive. Otherwise the I/O poll frequency will only change when + * the current wait finishes. This is consistent with the Unix/Mac OS + * platform which cannot break out of its nanosleep. If you change + * this you should change sqUnixHeartbeat.c to match. + */ + if (beatThreadState != active) { + beatThreadState = active; + if (!ReleaseSemaphore(beatSemaphore,1 /* 1 signal */,NULL)) + abortMessage(TEXT("Fatal: ReleaseSemaphore(beatSemaphore) %ld"), + GetLastError()); + } + } +#if !defined(_WIN32_WCE) + /* Belt and braces. Use timeSetEvent to signal beatSemaphore periodically + * to avoid cases where Windows doesn't honour the timeout in a timely + * fashion in the above WaitForSingleObject. + */ + if (dwTimerPeriod != ms) { + void ioReleaseTime(void); + TIMECAPS tCaps; + + ioReleaseTime(); + dwTimerPeriod = 0; + if(timeGetDevCaps(&tCaps,sizeof(tCaps)) != 0) + return; + dwTimerPeriod = max(tCaps.wPeriodMin,beatMilliseconds); + if (timeBeginPeriod(dwTimerPeriod) != 0) + return; + timerID = timeSetEvent( dwTimerPeriod, + 0, + (LPTIMECALLBACK)beatSemaphore, + 0, + TIME_PERIODIC | + TIME_CALLBACK_EVENT_PULSE); + } +#endif /* defined(_WIN32_WCE) */ +} + +void +ioReleaseTime(void) +{ +#if !defined(_WIN32_WCE) + if (timerID) { + timeKillEvent(timerID); + timeEndPeriod(dwTimerPeriod); + timerID = 0; + } +#endif /* !defined(_WIN32_WCE) */ +} + + +#if PRINT_TIME_CONSTANTS +SYSTEMTIME StartOfThe20thCentury + = { 1901, /* Blame Aloysius Lilius? */ + 1, /* January */ + 2, /* (Date fromSeconds: 0) dayOfWeekName => #Tuesday */ + 1, /* 1st of January */ + 0, /* 0th Hour */ + 0, /* 0th minute */ + 0, /* 0th second */ + 0 }; /* 0th millisecond */ +int +main() +{ + FILETIME startOfThe20thCentury; + TCHAR buffer[1024]; + FILETIME utcNow, localNow; + + GetDateFormat(LOCALE_SYSTEM_DEFAULT, + DATE_LONGDATE, + &StartOfThe20thCentury, + 0, + buffer, + 1024); + printf("StartOfThe20thCentury is %s\n\n", buffer); + + SystemTimeToFileTime(&StartOfThe20thCentury, &startOfThe20thCentury); + printf("#define MicrosecondsFrom1601To1901 %" LLFMT "\n", + *(unsigned __int64 *)&startOfThe20thCentury / TocksPerMicrosecond); + printf("#define SecondsFrom1601To1901 %" LLFMT "\n\n", + *(unsigned __int64 *)&startOfThe20thCentury / TocksPerSecond); + GetSystemTimeAsFileTime(&utcNow); + FileTimeToLocalFileTime(&utcNow,&localNow); + printf("Now Smalltalk seconds is therefore %" LLFMT "\n", + (*(unsigned __int64 *)&localNow / TocksPerSecond) + - (*(unsigned __int64 *)&startOfThe20thCentury / TocksPerSecond)); +} + +sqInt +forceInterruptCheckFromHeartbeat() +{ +} + +int __cdecl +abortMessage(const TCHAR *fmt,...) +{ +} +#endif /* PRINT_TIME_CONSTANTS */ diff --git a/platforms/minheadless/windows/sqWin32Main.c b/platforms/minheadless/windows/sqWin32Main.c new file mode 100644 index 0000000000..8df1ab77dc --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Main.c @@ -0,0 +1,43 @@ +/* sqWin32Main.c -- main entry point for the standalone Squeak VM for the Win32 + * subsystem in Windows + * + * Copyright (C) 2016 by Ronie Salgado + * All rights reserved. + * + * This file is part of Minimalistic Headless Squeak. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: roniesalg@gmail.com + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "OpenSmalltalkVM.h" + +int CALLBACK WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow +) +{ + return osvm_main(__argc, (const char **)__argv); +} diff --git a/platforms/minheadless/windows/sqWin32SpurAlloc.c b/platforms/minheadless/windows/sqWin32SpurAlloc.c new file mode 100644 index 0000000000..519c5ef093 --- /dev/null +++ b/platforms/minheadless/windows/sqWin32SpurAlloc.c @@ -0,0 +1,292 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32SpurAlloc.c +* CONTENT: Virtual Memory Management For Spur +* +* AUTHOR: Eliot Miranda +* EMAIL: eliot.miranda@gmail.com +* +*****************************************************************************/ + +#include +#include +#include "sq.h" +#include "sqWin32.h" + +#if SPURVM /* Non-spur uses sqWin32Alloc.c */ + +/* Why does this have to be *here*?? eem 6/24/2014 */ +#if !defined(NDEBUG) +/* in debug mode, let the system crash so that we can see where it happened */ +#define EXCEPTION_WRONG_ACCESS EXCEPTION_CONTINUE_SEARCH +#else +/* in release mode, execute the exception handler notifying the user what happened */ +#define EXCEPTION_WRONG_ACCESS EXCEPTION_EXECUTE_HANDLER +#endif + +LONG CALLBACK sqExceptionFilter(LPEXCEPTION_POINTERS exp) +{ + /* always wrong access - we handle memory differently now */ + return EXCEPTION_WRONG_ACCESS; +} + +static DWORD pageMask; /* bit mask for the start of a memory page */ +static DWORD pageSize; /* size of a memory page */ +static char *minAppAddr; /* SYSTEM_INFO lpMinimumApplicationAddress */ +static char *maxAppAddr; /* SYSTEM_INFO lpMaximumApplicationAddress */ + +# define roundDownToPage(v) ((v)&pageMask) +# define roundUpToPage(v) (((v)+pageSize-1)&pageMask) + +/************************************************************************/ +/* sqAllocateMemory: Initialize virtual memory */ +/************************************************************************/ +void * +sqAllocateMemory(usqInt minHeapSize, usqInt desiredHeapSize) +{ + char *hint, *address, *alloc; + usqIntptr_t alignment; + sqInt allocBytes; + SYSTEM_INFO sysInfo; + + if (pageSize) { + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "sqAllocateMemory already called"); + exit(1); + } + + /* determine page boundaries & available address space */ + GetSystemInfo(&sysInfo); + pageSize = sysInfo.dwPageSize; + pageMask = ~(pageSize - 1); + minAppAddr = sysInfo.lpMinimumApplicationAddress; + maxAppAddr = sysInfo.lpMaximumApplicationAddress; + + /* choose a suitable starting point. In MinGW the malloc heap is below the + * program, so take the max of a malloc and something form uninitialized + * data. + */ + hint = malloc(1); + free(hint); + hint = max(hint,(char *)&fIsConsole); + + alignment = max(pageSize,1024*1024); + address = (char *)(((usqInt)hint + alignment - 1) & ~(alignment - 1)); + + alloc = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto + (roundUpToPage(desiredHeapSize), address, &allocBytes); + if (!alloc) { + exit(errno); + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "sqAllocateMemory: initial alloc failed!\n"); + exit(1); + } + return alloc; +} + +#define roundDownToPage(v) ((v)&pageMask) +#define roundUpToPage(v) (((v)+pageSize-1)&pageMask) + +/* Allocate a region of memory of at least size bytes, at or above minAddress. + * If the attempt fails, answer null. If the attempt succeeds, answer the + * start of the region and assign its size through allocatedSizePointer. + * + * This from the VirtualFree doc is rather scary: + dwSize [in] + + The size of the region of memory to be freed, in bytes. + + If the dwFreeType parameter is MEM_RELEASE, this parameter must be 0 + (zero). The function frees the entire region that is reserved in the + initial allocation call to VirtualAlloc. + * + * So we rely on the SpurMemoryManager to free exactly the segments that were + * allocated. + */ +#define SizeForRelease(bytes) 0 + +static int +address_space_used(char *address, usqInt bytes) +{ + MEMORY_BASIC_INFORMATION info; + int addressSpaceUnused; + + if (address < minAppAddr || address > maxAppAddr) + return 1; + if (!VirtualQuery(address, &info, sizeof(info))) + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "Unable to VirtualQuery range [%p, %p), Error: %u", + address, (char *)address + bytes, GetLastError()); + + addressSpaceUnused = info.BaseAddress == address + && info.RegionSize >= bytes + && info.State == MEM_FREE; + + return !addressSpaceUnused; +} + +void * +sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(sqInt size, void *minAddress, sqInt *allocatedSizePointer) +{ + char *address, *alloc; + usqInt bytes, delta; + + address = (char *)roundUpToPage((usqIntptr_t)minAddress); + bytes = roundUpToPage(size); + delta = max(pageSize,1024*1024); + +# define printProbes 0 +# define printMaps 0 + while ((usqIntptr_t)(address + bytes) > (usqIntptr_t)address) { + if (printProbes && fIsConsole) + printf("probing [%p,%p)\n", address, address + bytes); + if (address_space_used(address, bytes)) { + address += delta; + continue; + } + alloc = VirtualAlloc(address, bytes, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); + /* For some reason (large page support?) we can ask for a page-aligned + * address such as 0xNNNNf000 but VirtualAlloc will answer 0xNNNN0000. + * So accept allocs above minAddress rather than allocs above address + */ + if (alloc >= (char *)minAddress && alloc <= address + delta) { + if (printMaps && fIsConsole) + fprintf(stderr, + "VirtualAlloc [%p,%p) above %p)\n", + address, address+bytes, minAddress); + *allocatedSizePointer = bytes; + return alloc; + } + if (!alloc) { + DWORD lastError = GetLastError(); +#if 0 /* Can't report this without making the system unusable... */ + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Error:"), + "Unable to VirtualAlloc committed memory at desired address (%" PRIuSQINT " bytes requested at %p, above %p), Error: %lu", + bytes, address, minAddress, lastError); +#else + if (fIsConsole) + fprintf(stderr, + "Unable to VirtualAlloc committed memory at desired address (%" PRIuSQINT " bytes requested at %p, above %p), Error: %lu\n", + bytes, address, minAddress, lastError); +#endif + return 0; + } + /* VirtualAlloc answered a mapping well away from where Spur prefers. + * Discard the mapping and try again delta higher. + */ + if (alloc && !VirtualFree(alloc, SizeForRelease(bytes), MEM_RELEASE)) + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Warning:"), + "Unable to VirtualFree committed memory (%" PRIuSQINT " bytes requested), Error: %ul", + bytes, GetLastError()); + address += delta; + } + return 0; +} + +/* Deallocate a region of memory previously allocated by + * sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto. Cannot fail. + */ +void +sqDeallocateMemorySegmentAtOfSize(void *addr, sqInt sz) +{ + if (!VirtualFree(addr, SizeForRelease(sz), MEM_RELEASE)) + sqMessageBox(MB_OK | MB_ICONSTOP, TEXT("VM Warning:"), + "Unable to VirtualFree committed memory (%" PRIuSQINT " bytes requested), Error: %ul", + sz, GetLastError()); +} + +# if COGVM +void +sqMakeMemoryExecutableFromTo(usqIntptr_t startAddr, usqIntptr_t endAddr) +{ + DWORD previous; + SIZE_T size; + + size = endAddr - startAddr; + if (!VirtualProtect((void *)startAddr, + size, + PAGE_EXECUTE_READWRITE, + &previous)) + sqWin32PrintLastError("VirtualProtect(x,y,PAGE_EXECUTE_READWRITE)"); +} + +void +sqMakeMemoryNotExecutableFromTo(usqIntptr_t startAddr, usqIntptr_t endAddr) +{ + DWORD previous; + SIZE_T size; + + size = endAddr - startAddr; + if (!VirtualProtect((void *)startAddr, + size, + PAGE_READWRITE, + &previous)) + sqWin32PrintLastError("VirtualProtect(x,y,PAGE_EXECUTE_READWRITE)"); +} +# endif /* COGVM */ + +# if TEST_MEMORY + +# define MBytes *1024UL*1024UL + +BOOL fIsConsole = 1; + +int +main() +{ + char *mem; + usqInt i, t = 16 MBytes; + + mem= (char *)sqAllocateMemory(t, t); + printf("memory allocated at %p\n", mem); + *mem = 1; + /* create some roadbumps */ + for (i = 80 MBytes; i < 2048UL MBytes; i += 80 MBytes) { + void *alloc = VirtualAlloc(mem + i, pageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); + printf("roadbump created at %p (%p)\n", mem + i, alloc); + *(char *)alloc = 1; + } + for (;;) { + sqInt segsz = 0; + char *seg = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(32 MBytes, mem + 16 MBytes, &segsz); + if (!seg) + return 0; + *seg = 1; + t += segsz; + printf("memory extended at %p (total %ld Mb)\n", seg, t / (1 MBytes)); + } + return 0; +} +int __cdecl +sqMessageBox(DWORD dwFlags, const TCHAR *titleString, const char* fmt, ...) +{ + va_list args; + int result; + char buf[1024]; + + strcpy(buf, titleString); + strcat(buf, fmt); + strcat(buf, "\n"); + va_start(args, fmt); +#if 0 + result = vfprintf(stderr, buf, args); +#else + result = vprintf(buf, args); +#endif + va_end(args); + printLastError((char *)titleString); + return result; +} +void printLastError(TCHAR *prefix) +{ LPVOID lpMsgBuf; + DWORD lastError; + + lastError = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL ); + fprintf(stderr,TEXT("%s (%d) -- %s\n"), prefix, lastError, lpMsgBuf); + LocalFree( lpMsgBuf ); +} +# endif /* TEST_MEMORY */ +#endif /* SPURVM */ diff --git a/platforms/minheadless/windows/sqWin32Threads.c b/platforms/minheadless/windows/sqWin32Threads.c new file mode 100644 index 0000000000..5ee2844c1b --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Threads.c @@ -0,0 +1,272 @@ +/**************************************************************************** +* PROJECT: Squeak port for Win32 (NT / Win95) +* FILE: sqWin32Threads.c +* CONTENT: Win32 thread support code for Cog & Stack VMs +* +* AUTHOR: Eliot Miranda +* +* NOTES: See the comment of CogThreadManager in the VMMaker package for +* overall design documentation. +* +*****************************************************************************/ + +#include +#include +#include +#include + +#define ForCOGMTVMImplementation 1 + +#include "sq.h" +#include "sqAssert.h" +#include "sqWin32.h" /* for printLastError */ + +/* + * A note on thread handles and Ids. The only globally shared and globally + * unique handle/id Win32 provides for threads is the value answered by the + * last argument of CreateThread and GetCurrentThreadId, but this is not a + * handle. GetCurrentThread answers a constant that means "this thread". + * The handle returned by CreateThread is not directly accessible to the + * thread that has been created, and does not seem to work reliably when + * used as the argument of GetExitCodeThread. + * + * Therefore we need to create a process-wide handle. We do so via + * DuplicateHandle because OpenThread seems to answer a handle that again + * isn't reliable with GetExitCodeThread. But DuplicateHandle must be used + * with the pseudo-handle returned by GetCurrentThread, and hence we need a + * wrapper around the thread creation function that duplicates the handle + * before calling the thread start function. (Sigh...) + */ + +DWORD tlthIndex = (DWORD)-1; /* process-wide thread handle thread-local key */ +DWORD tltiIndex = (DWORD)-1; /* thread index thread-local key */ + +static void +initThreadLocalThreadIndices(void) +{ + if (tlthIndex == (DWORD)-1) { + tlthIndex = TlsAlloc(); + tltiIndex = TlsAlloc(); + if (tlthIndex == TLS_OUT_OF_INDEXES + || tltiIndex == TLS_OUT_OF_INDEXES) { /* illiterate swine! */ + printLastError(TEXT("ThreadLocalThreadIndices TlsAlloc failed")); + exit(1); + } + } +} + +/* + * ioGetThreadLocalThreadIndex & ioSetThreadLocalThreadIndex are defined in + * sqPlatformSpecific.h. + */ + +static DWORD +duplicateAndSetThreadHandleForCurrentThread(void) +{ + HANDLE threadHandle; + DWORD lastError; + + if (DuplicateHandle(GetCurrentProcess(), // source process handle + GetCurrentThread(), // source handle (N.B. pseudo) + GetCurrentProcess(), // target process handle + &threadHandle, // out param + 0,// desired access, ignored if DUPLICATE_SAME_ACCESS + TRUE, // handle is inheritable + DUPLICATE_SAME_ACCESS)) { // options + assert(threadHandle); + TlsSetValue(tlthIndex,threadHandle); + return 0; + } + lastError = GetLastError(); + printLastError(TEXT("DuplicateHandle")); + return lastError; +} + +/* + * Re TlsGetValue. From msdn: + * "The data stored in a TLS slot can have a value of 0 because it still has its + * initial value or because the thread called the TlsSetValue function with 0. + * Therefore, if the return value is 0, you must check whether GetLastError + * returns ERROR_SUCCESS before determining that the function has failed. + * If GetLastError returns ERROR_SUCCESS, then the function has succeeded and + * the data stored in the TLS slot is 0. Otherwise, the function has failed." + * + * But since we never store a null thread handle we can omit this check. + */ +HANDLE +ioCurrentOSThread() +{ + HANDLE threadHandle = TlsGetValue(tlthIndex); + + if (!threadHandle) + (void)duplicateAndSetThreadHandleForCurrentThread(); + + assert(TlsGetValue(tlthIndex)); + return TlsGetValue(tlthIndex); +} + +#if COGMTVM +typedef struct { + void (*func)(void *); + void *arg; + } InitTuple; + +static void * +angel(void *arg) +{ + InitTuple it = *(InitTuple *)arg; + DWORD err = duplicateAndSetThreadHandleForCurrentThread(); + + free(arg); + if (err) + ExitThread(err); + it.func(it.arg); + return 0; +} + +int +ioNewOSThread(void (*func)(void *), void *arg) +{ + HANDLE newThread; + InitTuple *it = malloc(sizeof(InitTuple)); + + if (!it) + return ERROR_OUTOFMEMORY; + + it->func = func; + it->arg = arg; + newThread = CreateThread(0, /* no security */ + 0, /* default stack size */ + (LPTHREAD_START_ROUTINE)angel, + (void *)it, + STACK_SIZE_PARAM_IS_A_RESERVATION, /* creation flags 0 => run immediately */ + 0 /* thread id; we don't use it */); + + if (!newThread) { + int err = GetLastError(); + return err == 0 ? -1 : err; + } + /* we need to close this handle so that closing the duplicated handle will + * actually release resources. Keeping this handle open will prevent that. + */ + (void)CloseHandle(newThread); + return 0; +} + +int +ioOSThreadIsAlive(HANDLE thread) +{ + DWORD result; + + return GetExitCodeThread(thread, &result) + ? FALSE + : GetLastError() == STILL_ACTIVE; +} + +void +ioExitOSThread(HANDLE thread) +{ + if (thread == ioCurrentOSThread()) { + ioReleaseOSThreadState(thread); + ExitThread(0); + /*NOTREACHED*/ + } + TerminateThread(thread, 0); + ioReleaseOSThreadState(thread); +} + +void +ioReleaseOSThreadState(HANDLE thread) +{ + (void)CloseHandle(thread); +} + +int +ioNumProcessors(void) +{ + char *nprocs = getenv("NUMBER_OF_PROCESSORS"); + + return nprocs ? atoi(nprocs) : 1; +} + +int +ioNewOSSemaphore(sqOSSemaphore *sem) +{ + *sem = CreateSemaphore( 0, /* don't need no stinkin' security */ + 0, /* initial signal count */ + LONG_MAX, /* sky's the limit */ + 0 /* don't need no stinkin' name */); + if (!*sem) + printLastError("ioNewOSSemaphore CreateSemaphore"); + return *sem ? 0 : GetLastError(); +} + +void +ioDestroyOSSemaphore(sqOSSemaphore *sem) { CloseHandle(*sem); } + +void +ioSignalOSSemaphore(sqOSSemaphore *sem) +{ + if (!ReleaseSemaphore(*sem, 1, 0)) + abortMessage(TEXT("Fatal: ReleaseMutex(*sem) %ld"), + GetLastError()); +} + +void +ioWaitOnOSSemaphore(sqOSSemaphore *sem) +{ + if (WaitForSingleObject(*sem, INFINITE) == WAIT_FAILED) + abortMessage(TEXT("Fatal: WaitForSingleObject(*sem) %ld"), + GetLastError()); +} +#else /* COGMTVM */ +/* This is for sqVirtualMachine.h's default ownVM implementation. */ +sqInt +amInVMThread() { return ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread()); } +#endif /* COGMTVM */ + +void +ioInitThreads() +{ + extern void ioInitExternalSemaphores(void); + initThreadLocalThreadIndices(); +#if !COGMTVM + ioVMThread = ioCurrentOSThread(); +#endif + ioInitExternalSemaphores(); +} + +/* this for testing crash dumps */ +static sqInt +indirect(sqIntptr_t p) +{ + if ((p & 2)) + error("crashInThisOrAnotherThread"); + return p > 99 + ? indirect(p - 100), indirect(p - 50) /* evade tail recursion opt */ + : *(sqInt *)p; +} + +/* bit 0 = thread to crash in; 1 => this thread + * bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit + */ +sqInt +crashInThisOrAnotherThread(sqInt flags) +{ + if ((flags & 1)) { + if (!(flags & 2)) + return indirect(flags & ~1); + error("crashInThisOrAnotherThread"); + return 0; + } + else { + CreateThread(0, /* no security */ + 0, /* default stack size */ + (LPTHREAD_START_ROUTINE)indirect, + (void *)300, + STACK_SIZE_PARAM_IS_A_RESERVATION, /* creation flags 0 => run immediately */ + 0 /* thread id; we don't use it */); + Sleep(1000); + } + return 0; +} diff --git a/platforms/minheadless/windows/sqWin32Time.c b/platforms/minheadless/windows/sqWin32Time.c new file mode 100644 index 0000000000..385f158c8a --- /dev/null +++ b/platforms/minheadless/windows/sqWin32Time.c @@ -0,0 +1,149 @@ +/**************************************************************************** +* sqWin32Time.c +* Time functions for non-heartbeat (non STACK) VMs, extracted from +* trunk sqWin32Window.c +*****************************************************************************/ + +#include + +#include "sq.h" + +#if !STACKVM +/* + * Win32 FILETIMEs are 10th's of microseconds since 1601. Smalltalk times + * are seconds from 1901. Let's call a 10th of a microsecond a "tock". + */ + +#if _MSC_VER +# define SecondsFrom1601To1901 9467020800i64 /*See PRINT_TIME_CONSTANTS below*/ +# define MicrosecondsFrom1601To1901 9467020800000000i64 + +# define MicrosecondsPerSecond 1000000i64 +# define MillisecondsPerSecond 1000i64 + +# define MicrosecondsPerMillisecond 1000000i64 + +# define TocksPerSecond 10000000i64 +# define TocksPerMillisecond 10000i64 +# define TocksPerMicrosecond 10i64 +# define LLFMT "I64d" +#else +# define SecondsFrom1601To1901 9467020800LL /*See PRINT_TIME_CONSTANTS below*/ +# define MicrosecondsFrom1601To1901 9467020800000000LL + +# define MicrosecondsPerSecond 1000000LL +# define MillisecondsPerSecond 1000LL + +# define MicrosecondsPerMillisecond 1000LL + +# define TocksPerSecond 10000000LL +# define TocksPerMillisecond 10000LL +# define TocksPerMicrosecond 10LL +# define LLFMT "lld" +#endif + +/* returns the local wall clock time */ +int +ioSeconds(void) +{ SYSTEMTIME sysTime; + GetLocalTime(&sysTime); + return convertToSqueakTime(sysTime); +} + +int +ioMSecs() +{ + /* Make sure the value fits into Squeak SmallIntegers */ +#ifndef _WIN32_WCE + return timeGetTime() & MillisecondClockMask; +#else + return GetTickCount() & MillisecondClockMask; +#endif +} + +/* Note: ioMicroMSecs returns *milli*seconds */ +int +ioMicroMSecs(void) +{ + /* Make sure the value fits into Squeak SmallIntegers */ + return timeGetTime() & MillisecondClockMask; +} + +/* Compute the current VM time basis, the number of microseconds from 1901. + * + * Alas Windows' system time functions GetSystemTime et al have low resolution; + * 15 ms. So we use timeGetTime for higher resolution and use it as an offset to + * the system time, resetting when timeGetTime wraps. Since timeGetTime wraps we + * need some basis information which is passed in as pointers to provide us with + * both the heartbeat clock and an instantaneous clock for the VM thread. + * This is still insufficient since timeGetTime driefts relative to wall time. + * We should apply some periodic adjustment but for now just drift aimlessly. + */ + +/* The bases that relate timeGetTime's 32-bit wrapping millisecond clock to the + * non-wrapping 64-bit microsecond clocks. + */ +static unsigned __int64 utcTickBaseMicroseconds; +static DWORD lastTick = (DWORD)-1; +static DWORD baseTick; + +static unsigned __int64 +currentUTCMicroseconds(unsigned __int64 *utcTickBaseUsecsp, DWORD *lastTickp, DWORD *baseTickp) +{ + FILETIME utcNow; + DWORD currentTick = timeGetTime(); + DWORD prevTick = *lastTickp; + + *lastTickp = currentTick; + + /* If the timeGetTime millisecond clock wraps (as it will every 49.71 days) + * resync to the system time. + */ + if (currentTick < prevTick) { + + *baseTickp = currentTick; + GetSystemTimeAsFileTime(&utcNow); + *utcTickBaseUsecsp = *(unsigned __int64 *)&utcNow + / TocksPerMicrosecond + - MicrosecondsFrom1601To1901; + return *utcTickBaseUsecsp; + } + return *utcTickBaseUsecsp + + (currentTick - *baseTickp) * MicrosecondsPerMillisecond; +} + +unsigned volatile long long +ioUTCMicroseconds() { return currentUTCMicroseconds(&utcTickBaseMicroseconds, &lastTick, &baseTick); } + +/* This is an expensive interface for use by profiling code that wants the time + * now rather than as of the last heartbeat. + */ +unsigned volatile long long +ioUTCMicrosecondsNow() { return currentUTCMicroseconds(&utcTickBaseMicroseconds, &lastTick, &baseTick); } + +static DWORD dwTimerPeriod; + +void +ioInitTime() +{ +# if !defined(_WIN32_WCE) + TIMECAPS tCaps; + + dwTimerPeriod = 0; + if(timeGetDevCaps(&tCaps,sizeof(tCaps)) != 0) + return; + dwTimerPeriod = tCaps.wPeriodMin; + if (timeBeginPeriod(dwTimerPeriod) != 0) + return; +# endif +} + +void +ioReleaseTime(void) +{ +# if !defined(_WIN32_WCE) + if (dwTimerPeriod) + timeEndPeriod(dwTimerPeriod); +# endif /* !defined(_WIN32_WCE) */ +} +#endif /* STACKVM */ diff --git a/platforms/win32/plugins/FilePlugin/sqWin32File.h b/platforms/win32/plugins/FilePlugin/sqWin32File.h index c95c02636b..5159b8c91b 100644 --- a/platforms/win32/plugins/FilePlugin/sqWin32File.h +++ b/platforms/win32/plugins/FilePlugin/sqWin32File.h @@ -28,6 +28,7 @@ name cannot exceed MAX_PATH minus 12). **/ +#include #define ALLOC_WIN32_PATH(out_path, in_name, in_size) { \ int sz = MultiByteToWideChar(CP_UTF8, 0, in_name, in_size, NULL, 0); \ diff --git a/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c b/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c index 9c39cc7f9f..97e9bfc968 100644 --- a/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c +++ b/platforms/win32/plugins/FilePlugin/sqWin32FilePrims.c @@ -353,11 +353,7 @@ size_t sqFileReadIntoAt(SQFile *f, size_t count, char* byteArrayIndex, size_t st if (!sqFileValid(f)) FAIL(); - if (f->isStdioStream) - ReadConsole(FILE_HANDLE(f), (LPVOID) (byteArrayIndex+startIndex), count, - &dwReallyRead, NULL); - else - ReadFile(FILE_HANDLE(f), (LPVOID) (byteArrayIndex+startIndex), count, + ReadFile(FILE_HANDLE(f), (LPVOID) (byteArrayIndex+startIndex), count, &dwReallyRead, NULL); return dwReallyRead; } @@ -446,10 +442,7 @@ size_t sqFileWriteFromAt(SQFile *f, size_t count, char* byteArrayIndex, size_t s if (!(sqFileValid(f) && f->writable)) FAIL(); - if (f->isStdioStream) - WriteConsole(FILE_HANDLE(f), (LPVOID) (byteArrayIndex + startIndex), count, &dwReallyWritten, NULL); - else - WriteFile(FILE_HANDLE(f), (LPVOID) (byteArrayIndex + startIndex), count, &dwReallyWritten, NULL); + WriteFile(FILE_HANDLE(f), (LPVOID) (byteArrayIndex + startIndex), count, &dwReallyWritten, NULL); if (dwReallyWritten != count) FAIL(); @@ -465,7 +458,7 @@ sqInt sqImageFileClose(sqImageFile h) return CloseHandle((HANDLE)(h-1)); } -sqImageFile sqImageFileOpen(char *fileName, char *mode) +sqImageFile sqImageFileOpen(const char *fileName, const char *mode) { char *modePtr; int writeFlag = 0; WCHAR *win32Path = NULL; @@ -533,7 +526,15 @@ squeakFileOffsetType sqImageFileSeek(sqImageFile h, squeakFileOffsetType pos) return ofs.offset; } -size_t sqImageFileWrite(void *ptr, size_t sz, size_t count, sqImageFile h) +squeakFileOffsetType sqImageFileSeekEnd(sqImageFile h, squeakFileOffsetType pos) +{ + win32FileOffset ofs; + ofs.offset = pos; + ofs.dwLow = SetFilePointer((HANDLE)(h - 1), ofs.dwLow, &ofs.dwHigh, FILE_END); + return ofs.offset; +} + +size_t sqImageFileWrite(const void *ptr, size_t sz, size_t count, sqImageFile h) { DWORD dwReallyWritten; WriteFile((HANDLE)(h-1), (LPVOID) ptr, count*sz, &dwReallyWritten, NULL); diff --git a/platforms/win32/plugins/SecurityPlugin/sqWin32Security.c b/platforms/win32/plugins/SecurityPlugin/sqWin32Security.c index 53908291ec..f8a801b488 100644 --- a/platforms/win32/plugins/SecurityPlugin/sqWin32Security.c +++ b/platforms/win32/plugins/SecurityPlugin/sqWin32Security.c @@ -8,6 +8,7 @@ #include #include /* CSIDL_XXX */ #include "sq.h" +#include "sqWin32.h" #ifndef HKEY_SQUEAK_ROOT /* the default place in the registry to look for */ @@ -16,20 +17,27 @@ static HRESULT (__stdcall *shGetFolderPath)(HWND, int, HANDLE, DWORD, WCHAR*); -static TCHAR untrustedUserDirectory[MAX_PATH]; +static WCHAR untrustedUserDirectory[MAX_PATH]; static int untrustedUserDirectoryLen; -static TCHAR secureUserDirectory[MAX_PATH]; +static WCHAR secureUserDirectory[MAX_PATH]; static int secureUserDirectoryLen; -static TCHAR resourceDirectory[MAX_PATH]; +static WCHAR resourceDirectory[MAX_PATH]; static int resourceDirectoryLen; +static char untrustedUserDirectoryUTF8[MAX_PATH]; +static char secureUserDirectoryUTF8[MAX_PATH]; +static char resourceDirectoryUTF8[MAX_PATH]; + /* imported from sqWin32Prefs.c */ -extern TCHAR squeakIniName[MAX_PATH]; +extern WCHAR squeakIniName[MAX_PATH]; /* imported from sqWin32Main.c */ extern BOOL fLowRights; /* started as low integrity process, need to use alternate untrustedUserDirectory */ +extern int sqAskSecurityYesNoQuestion(const char *question); +extern const char *sqGetCurrentImagePath(void); + /***************************************************************************/ /***************************************************************************/ /***************************************************************************/ @@ -46,13 +54,14 @@ sqInt ioHasEnvironmentAccess(void) { return allowEnvironmentAccess; } /***************************************************************************/ /* file security */ static int allowFileAccess = 1; /* full access to files */ -static const TCHAR U_DOT[] = TEXT("."); +static const WCHAR SEC_U_DOT[] = L"."; +static const WCHAR SEC_U_BACKSLASH[] = L"\\"; -static int testDotDot(TCHAR *pathName, int index) { +static int testDotDot(WCHAR *pathName, int index) { while(pathName[index]) { - if(pathName[index] == U_DOT[0]) { - if(pathName[index-1] == U_DOT[0]) { - if (pathName[index-2] == U_BACKSLASH[0]) { + if(pathName[index] == SEC_U_DOT[0]) { + if(pathName[index-1] == SEC_U_DOT[0]) { + if (pathName[index-2] == SEC_U_BACKSLASH[0]) { return 0; /* Gotcha! */ } } @@ -62,9 +71,9 @@ static int testDotDot(TCHAR *pathName, int index) { return 1; } -static int lstrncmp(TCHAR *s1, TCHAR *s2, int len) { - int s1Len = lstrlen(s1); - int s2Len = lstrlen(s2); +static int lstrncmpW(WCHAR *s1, WCHAR *s2, int len) { + int s1Len = lstrlenW(s1); + int s2Len = lstrlenW(s2); int max = min(s1Len, min(s2Len, len)); int i; for (i = 0; i < max; i++) { @@ -77,12 +86,12 @@ static int lstrncmp(TCHAR *s1, TCHAR *s2, int len) { return 0; } -static int isAccessiblePathName(TCHAR *pathName, int writeFlag) { - int pathLen = lstrlen(pathName); +static int isAccessiblePathName(WCHAR *pathName, int writeFlag) { + int pathLen = lstrlenW(pathName); if (pathLen > (MAX_PATH - 1)) return 0; if (pathLen >= untrustedUserDirectoryLen - && 0 == lstrncmp(pathName, untrustedUserDirectory, untrustedUserDirectoryLen)) { + && 0 == lstrncmpW(pathName, untrustedUserDirectory, untrustedUserDirectoryLen)) { if (pathLen > untrustedUserDirectoryLen + 2) return testDotDot(pathName, untrustedUserDirectoryLen+2); return 1; @@ -91,7 +100,7 @@ static int isAccessiblePathName(TCHAR *pathName, int writeFlag) { return 0; if (pathLen >= resourceDirectoryLen - && 0 == lstrncmp(pathName, resourceDirectory, resourceDirectoryLen)) { + && 0 == lstrncmpW(pathName, resourceDirectory, resourceDirectoryLen)) { if (pathLen > resourceDirectoryLen + 2) return testDotDot(pathName, resourceDirectoryLen+2); return 1; @@ -99,7 +108,7 @@ static int isAccessiblePathName(TCHAR *pathName, int writeFlag) { return 0; } -static int isAccessibleFileName(TCHAR *fileName, int writeFlag) { +static int isAccessibleFileName(WCHAR *fileName, int writeFlag) { return isAccessiblePathName(fileName, writeFlag); } @@ -188,134 +197,141 @@ int ioHasSocketAccess() { return allowSocketAccess; } /* SecurityPlugin primitive support */ char *ioGetSecureUserDirectory(void) { - return secureUserDirectory; + return secureUserDirectoryUTF8; } char *ioGetUntrustedUserDirectory(void) { - return untrustedUserDirectory; + return untrustedUserDirectoryUTF8; } /* helper function to expand %MYDOCUMENTSFOLDER% */ -int expandMyDocuments(char *pathname, char *replacement, char *result) +int expandMyDocuments(WCHAR *pathname, WCHAR *replacement, WCHAR *result) { - TCHAR search4[MAX_PATH+1]; - TCHAR *start; +/* WCHAR search4[MAX_PATH+1]; + WCHAR *start; - lstrcpy(search4, TEXT("%MYDOCUMENTS%")); + lstrcpyW(search4, L"%MYDOCUMENTS%"); - if(!(start = strstr(pathname, search4))) return 0; + if(!(start = wstrstr(pathname, search4))) return 0; - strncpy(result, pathname, start-pathname); + wstrncpy(result, pathname, start-pathname); result[start-pathname] = '\0'; - sprintf(result+(start-pathname),"%s%s", replacement, start+strlen(search4)); - - return strlen(result); + sprintf(result+(start-pathname),"%s%s", replacement, start+lstrlenW(search4)); +*/ + /* TODO: Implement this properly. */ + return 0; } +static void expandVariableInDirectory(WCHAR *directory, WCHAR *wDir, WCHAR *wTmp) +{ + /* Expand environment variables. */ + lstrcpyW(wDir, directory); + ExpandEnvironmentStringsW(wDir, wTmp, MAX_PATH - 1); + /* Expand relative paths to absolute paths */ + GetFullPathNameW(wTmp, MAX_PATH, wDir, NULL); + lstrcpyW(directory, wDir); +} /* note: following is called from VM directly, not from plugin */ sqInt ioInitSecurity(void) { DWORD dwType, dwSize, ok; - TCHAR tmp[MAX_PATH+1]; + WCHAR tmp[MAX_PATH+1]; WCHAR wTmp[MAX_PATH+1]; WCHAR wDir[MAX_PATH+1]; - TCHAR myDocumentsFolder[MAX_PATH+1]; + WCHAR myDocumentsFolder[MAX_PATH+1]; HKEY hk; int dirLen; /* establish the secure user directory */ - lstrcpy(secureUserDirectory, imagePath); - dirLen = lstrlen(secureUserDirectory); + sqUTF8ToUTF16Copy(secureUserDirectory, sizeof(secureUserDirectory)/sizeof(secureUserDirectory[0]), sqGetCurrentImagePath()); + dirLen = lstrlenW(secureUserDirectory); dwSize = MAX_PATH-dirLen; - GetUserName(secureUserDirectory+dirLen, &dwSize); + GetUserNameW(secureUserDirectory+dirLen, &dwSize); /* establish untrusted user directory */ - lstrcpy(untrustedUserDirectory, TEXT("C:\\My Squeak\\%USERNAME%")); + lstrcpyW(untrustedUserDirectory, L"C:\\My Squeak\\%USERNAME%"); /* establish untrusted user directory */ - lstrcpy(resourceDirectory, imagePath); - if (resourceDirectory[lstrlen(resourceDirectory)-1] == '\\') { - resourceDirectory[lstrlen(resourceDirectory)-1] = 0; + sqUTF8ToUTF16Copy(resourceDirectory, sizeof(resourceDirectory) / sizeof(resourceDirectory[0]), sqGetCurrentImagePath()); + if (resourceDirectory[lstrlenW(resourceDirectory)-1] == '\\') { + resourceDirectory[lstrlenW(resourceDirectory)-1] = 0; } /* Look up shGetFolderPathW */ - shGetFolderPath = (void*)GetProcAddress(LoadLibrary("SHFolder.dll"), + shGetFolderPath = (void*)GetProcAddress(LoadLibraryA("SHFolder.dll"), "SHGetFolderPathW"); if(shGetFolderPath) { /* If we have shGetFolderPath use My Documents/My Squeak */ - WCHAR widepath[MAX_PATH]; int sz; /*shGetfolderPath does not return utf8*/ - if(shGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, widepath) == S_OK) { - WideCharToMultiByte(CP_ACP,0,widepath,-1,untrustedUserDirectory, - MAX_PATH,NULL,NULL); - sz = strlen(untrustedUserDirectory); + if(shGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, untrustedUserDirectory) == S_OK) { + sz = lstrlenW(untrustedUserDirectory); if(untrustedUserDirectory[sz-1] != '\\') - strcat(untrustedUserDirectory, "\\"); - lstrcpy(myDocumentsFolder,untrustedUserDirectory); - strcat(untrustedUserDirectory, "My Squeak"); + lstrcatW(untrustedUserDirectory, L"\\"); + lstrcpyW(myDocumentsFolder,untrustedUserDirectory); + lstrcatW(untrustedUserDirectory, L"My Squeak"); } } /* Query Squeak.ini for network installations */ - GetPrivateProfileString(TEXT("Security"), TEXT("SecureDirectory"), + GetPrivateProfileStringW(L"Security", L"SecureDirectory", secureUserDirectory, secureUserDirectory, MAX_PATH, squeakIniName); if(fLowRights) {/* use alternate untrustedUserDirectory */ - GetPrivateProfileString(TEXT("Security"), TEXT("UserDirectoryLow"), + GetPrivateProfileStringW(L"Security", L"UserDirectoryLow", untrustedUserDirectory, untrustedUserDirectory, MAX_PATH, squeakIniName); } else { - GetPrivateProfileString(TEXT("Security"), TEXT("UserDirectory"), + GetPrivateProfileStringW(L"Security", L"UserDirectory", untrustedUserDirectory, untrustedUserDirectory, MAX_PATH, squeakIniName); } - GetPrivateProfileString(TEXT("Security"), TEXT("ResourceDirectory"), + GetPrivateProfileStringW(L"Security", L"ResourceDirectory", resourceDirectory, resourceDirectory, MAX_PATH, squeakIniName); /* Attempt to read local user settings from registry */ - ok = RegOpenKey(HKEY_CURRENT_USER, HKEY_SQUEAK_ROOT, &hk); + ok = RegOpenKeyA(HKEY_CURRENT_USER, HKEY_SQUEAK_ROOT, &hk); /* Read the secure directory from the subkey. */ dwSize = MAX_PATH; - ok = RegQueryValueEx(hk,"SecureDirectory",NULL, &dwType, + ok = RegQueryValueExW(hk, L"SecureDirectory",NULL, &dwType, (LPBYTE) tmp, &dwSize); if(ok == ERROR_SUCCESS) { if(tmp[dwSize-2] != '\\') { tmp[dwSize-1] = '\\'; tmp[dwSize] = 0; } - strcpy(secureUserDirectory, tmp); + lstrcpyW(secureUserDirectory, tmp); } /* Read the user directory from the subkey. */ dwSize = MAX_PATH; - ok = RegQueryValueEx(hk,"UserDirectory",NULL, &dwType, + ok = RegQueryValueExW(hk, L"UserDirectory",NULL, &dwType, (LPBYTE) tmp, &dwSize); if(ok == ERROR_SUCCESS) { if(tmp[dwSize-2] != '\\') { tmp[dwSize-1] = '\\'; tmp[dwSize] = 0; } - strcpy(untrustedUserDirectory, tmp); + lstrcpyW(untrustedUserDirectory, tmp); } /* Read the resource directory from the subkey. */ dwSize = MAX_PATH; - ok = RegQueryValueEx(hk,"ResourceDirectory",NULL, &dwType, + ok = RegQueryValueExW(hk, L"ResourceDirectory",NULL, &dwType, (LPBYTE) tmp, &dwSize); if(ok == ERROR_SUCCESS) { if(tmp[dwSize-2] != '\\') { tmp[dwSize-1] = '\\'; tmp[dwSize] = 0; } - strcpy(resourceDirectory, tmp); + lstrcpyW(resourceDirectory, tmp); } RegCloseKey(hk); @@ -323,41 +339,30 @@ sqInt ioInitSecurity(void) { if(shGetFolderPath) { dwSize = expandMyDocuments(untrustedUserDirectory, myDocumentsFolder, tmp); if(dwSize > 0 && dwSize < MAX_PATH) - strcpy(untrustedUserDirectory, tmp); + lstrcpyW(untrustedUserDirectory, tmp); dwSize = expandMyDocuments(secureUserDirectory, myDocumentsFolder, tmp); if(dwSize > 0 && dwSize < MAX_PATH) - strcpy(secureUserDirectory, tmp); + lstrcpyW(secureUserDirectory, tmp); dwSize = expandMyDocuments(resourceDirectory, myDocumentsFolder, tmp); if(dwSize > 0 && dwSize < MAX_PATH) - strcpy(resourceDirectory, tmp); + lstrcpyW(resourceDirectory, tmp); } - /* Expand any environment variables in user directory. */ - MultiByteToWideChar(CP_ACP, 0, untrustedUserDirectory, -1, wDir, MAX_PATH); - ExpandEnvironmentStringsW(wDir, wTmp, MAX_PATH-1); - /* Expand relative paths to absolute paths */ - GetFullPathNameW(wTmp, MAX_PATH, wDir, NULL); - WideCharToMultiByte(CP_UTF8,0,wDir,-1,untrustedUserDirectory,MAX_PATH,NULL,NULL); - - /* same for the secure directory*/ - MultiByteToWideChar(CP_ACP, 0, secureUserDirectory, -1, wDir, MAX_PATH); - ExpandEnvironmentStringsW(wDir, wTmp, MAX_PATH-1); - /* Expand relative paths to absolute paths */ - GetFullPathNameW(wTmp, MAX_PATH, wDir, NULL); - WideCharToMultiByte(CP_UTF8,0,wDir,-1,secureUserDirectory,MAX_PATH,NULL,NULL); - - /* and for the resource directory*/ - MultiByteToWideChar(CP_ACP, 0, resourceDirectory, -1, wDir, MAX_PATH); - ExpandEnvironmentStringsW(wDir, wTmp, MAX_PATH-1); - /* Expand relative paths to absolute paths */ - GetFullPathNameW(wTmp, MAX_PATH, wDir, NULL); - WideCharToMultiByte(CP_UTF8,0,wDir,-1,resourceDirectory,MAX_PATH,NULL,NULL); - - secureUserDirectoryLen = lstrlen(secureUserDirectory); - untrustedUserDirectoryLen = lstrlen(untrustedUserDirectory); - resourceDirectoryLen = lstrlen(resourceDirectory); + /* Expand the directories. */ + expandVariableInDirectory(untrustedUserDirectory, wDir, wTmp); + expandVariableInDirectory(secureUserDirectory, wDir, wTmp); + expandVariableInDirectory(resourceDirectory, wDir, wTmp); + + secureUserDirectoryLen = lstrlenW(secureUserDirectory); + untrustedUserDirectoryLen = lstrlenW(untrustedUserDirectory); + resourceDirectoryLen = lstrlenW(resourceDirectory); + + /* Keep a UTF-8 copy*/ + sqUTF16ToUTF8Copy(untrustedUserDirectoryUTF8, sizeof(untrustedUserDirectoryUTF8), untrustedUserDirectory); + sqUTF16ToUTF8Copy(secureUserDirectoryUTF8, sizeof(secureUserDirectoryUTF8), secureUserDirectory); + sqUTF16ToUTF8Copy(resourceDirectoryUTF8, sizeof(resourceDirectoryUTF8), resourceDirectory); return 1; } @@ -370,11 +375,11 @@ sqInt ioInitSecurity(void) { int _ioSetImageWrite(int enable) { if(enable == allowImageWrite) return 1; if(!allowImageWrite) { - if(MessageBox(stWindow, TEXT("WARNING: Re-enabling the ability to write the image is a serious security hazard. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if(!sqAskSecurityYesNoQuestion("WARNING: Re-enabling the ability to write the image is a serious security hazard. Do you want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if(!sqAskSecurityYesNoQuestion("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if(!sqAskSecurityYesNoQuestion("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?")) return 0; } allowImageWrite = enable; @@ -384,11 +389,11 @@ int _ioSetImageWrite(int enable) { int _ioSetFileAccess(int enable) { if(enable == allowFileAccess) return 1; if(!allowFileAccess) { - if(MessageBox(stWindow, TEXT("WARNING: Re-enabling the ability to access arbitrary files is a serious security hazard. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: Re-enabling the ability to write the image is a serious security hazard. Do you want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?")) return 0; } allowFileAccess = enable; @@ -398,11 +403,11 @@ int _ioSetFileAccess(int enable) { int _ioSetSocketAccess(int enable) { if(enable == allowSocketAccess) return 1; if(!allowSocketAccess) { - if(MessageBox(stWindow, TEXT("WARNING: Re-enabling the ability to use sockets is a serious security hazard. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: Re-enabling the ability to write the image is a serious security hazard. Do you want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: Untrusted code could WIPE OUT your entire hard disk, STEAL your credit card information and send your PERSONAL documents to the entire world. Do you really want to continue?")) return 0; - if(MessageBox(stWindow, TEXT("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?"), TEXT("Squeak Security Alert"), MB_YESNO | MB_ICONSTOP) != IDYES) + if (!sqAskSecurityYesNoQuestion("WARNING: This is your last chance. If you proceed you will have to deal with the implications on your own. WE ARE REJECTING EVERY RESPONSIBILITY IF YOU CLICK ON YES. Do you want to continue?")) return 0; } allowSocketAccess = enable; diff --git a/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c b/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c index b7c4a2f026..e538fe642b 100644 --- a/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c +++ b/platforms/win32/plugins/SocketPlugin/sqWin32NewNet.c @@ -24,7 +24,6 @@ #include #include #include -#include #include "sq.h" #include "SocketPlugin.h" diff --git a/platforms/win32/vm/sqPlatformSpecific.h b/platforms/win32/vm/sqPlatformSpecific.h index e0f3d408dc..ed4ab94f65 100644 --- a/platforms/win32/vm/sqPlatformSpecific.h +++ b/platforms/win32/vm/sqPlatformSpecific.h @@ -36,15 +36,17 @@ #undef sqImageFilePosition #undef sqImageFileRead #undef sqImageFileSeek +#undef sqImageFileSeekEnd #undef sqImageFileWrite #define sqImageFile usqIntptr_t sqInt sqImageFileClose(sqImageFile h); -sqImageFile sqImageFileOpen(char *fileName, char *mode); +sqImageFile sqImageFileOpen(const char *fileName, const char *mode); squeakFileOffsetType sqImageFilePosition(sqImageFile h); size_t sqImageFileRead(void *ptr, size_t sz, size_t count, sqImageFile h); squeakFileOffsetType sqImageFileSeek(sqImageFile h, squeakFileOffsetType pos); -size_t sqImageFileWrite(void *ptr, size_t sz, size_t count, sqImageFile h); +squeakFileOffsetType sqImageFileSeekEnd(sqImageFile h, squeakFileOffsetType pos); +size_t sqImageFileWrite(const void *ptr, size_t sz, size_t count, sqImageFile h); #else /* when no WIN32_FILE_SUPPORT, add necessary stub for using regular Cross/plugins/FilePlugin functions */ #include #include /* _get_osfhandle */ diff --git a/platforms/win32/vm/sqWin32Window.c b/platforms/win32/vm/sqWin32Window.c index 0a8b5bcf86..f556e3a1b4 100644 --- a/platforms/win32/vm/sqWin32Window.c +++ b/platforms/win32/vm/sqWin32Window.c @@ -188,6 +188,16 @@ EXPORT(void) setIoProcessEventsHandler(void * handler) { } #endif +extern int sqAskSecurityYesNoQuestion(const char *question) +{ + return MessageBoxA(stWindow, question, "Squeak Security Alert", MB_YESNO | MB_ICONSTOP) == IDYES; +} + +extern const char *sqGetCurrentImagePath(void) +{ + return imagePath; +} + /****************************************************************************/ /* Synchronization functions */ /****************************************************************************/ diff --git a/spursrc/vm/cogit.h b/spursrc/vm/cogit.h index 7b58bc550b..ead899674b 100644 --- a/spursrc/vm/cogit.h +++ b/spursrc/vm/cogit.h @@ -136,7 +136,7 @@ VM_EXPORT sqInt traceStores; /*** Macros ***/ -#define blockAlignment(self) 8 +#define blockAlignment() 8 #define breakOnImplicitReceiver() (traceFlags & 64) #define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline #define ceCannotResumePC() ((usqInt)ceCannotResumeTrampoline) diff --git a/spursrc/vm/cogitIA32.c b/spursrc/vm/cogitIA32.c index 134c73e400..95c18d3ec5 100644 --- a/spursrc/vm/cogitIA32.c +++ b/spursrc/vm/cogitIA32.c @@ -1938,7 +1938,7 @@ static usqInt youngReferrers; blockStarts = (numBlocks) ? alloca(sizeof(BlockStart) * (numBlocks)) : 0; \ } while (0) #define backEnd() backEnd -#define blockAlignment(self) 8 +#define blockAlignment() 8 #define blockStartAt(index) (&blockStarts[index]) #define breakOnImplicitReceiver() (traceFlags & 64) #define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline diff --git a/spursrc/vm/cointerp.c b/spursrc/vm/cointerp.c index 6b704fc8c5..745dd40e41 100644 --- a/spursrc/vm/cointerp.c +++ b/spursrc/vm/cointerp.c @@ -56134,7 +56134,7 @@ scavengingGCTenuringIf(sqInt tenuringCriterion) sqInt i; sqInt probe; - assert(GIV(remapBufferCount) == 0); + assert(GIV(remapBufferCount) == 0); if (!(asserta(((((eden()).limit)) - GIV(freeStart)) > (interpreterAllocationReserveBytes())))) { /* begin tab */ putchar(' '); diff --git a/third-party/freetype2.spec b/third-party/freetype2.spec index 3ac1d7c4dd..32c87d6bb6 100644 --- a/third-party/freetype2.spec +++ b/third-party/freetype2.spec @@ -5,3 +5,4 @@ freetype2_spec_product_name_macOS:=libfreetype.6.dylib freetype2_spec_product_name_linux:= freetype2_spec_product_name_windows:=libfreetype.dll freetype2_spec_symlinks_macOS:=libfreetype*.dylib + diff --git a/third-party/libsdl2.spec b/third-party/libsdl2.spec index 9cf0977cac..6effd7a4ef 100644 --- a/third-party/libsdl2.spec +++ b/third-party/libsdl2.spec @@ -1,9 +1,17 @@ -libsdl2_spec_download_url:=http://www.libsdl.org/release/SDL2-2.0.5.tar.gz -libsdl2_spec_archive_name:=SDL2-2.0.5.tar.gz -libsdl2_spec_unpack_dir_name:=SDL2-2.0.5 +#libsdl2_spec_download_url:=http://www.libsdl.org/release/SDL2-2.0.5.tar.gz +#libsdl2_spec_archive_name:=SDL2-2.0.5.tar.gz +#libsdl2_spec_unpack_dir_name:=SDL2-2.0.5 +#libsdl2_spec_product_name_macOS:=libSDL2-2.0.0.dylib +#libsdl2_spec_product_name_linux:=libSDL2-2.0.so.0.4.1 +#libsdl2_spec_product_name_windows:=SDL2.dll +#libsdl2_spec_symlinks_macOS:=libSDL2*.dylib +#libsdl2_spec_symlinks_linux:=libSDL2*so* + +libsdl2_spec_download_url:=http://www.libsdl.org/release/SDL2-2.0.7.tar.gz +libsdl2_spec_archive_name:=SDL2-2.0.7.tar.gz +libsdl2_spec_unpack_dir_name:=SDL2-2.0.7 libsdl2_spec_product_name_macOS:=libSDL2-2.0.0.dylib libsdl2_spec_product_name_linux:=libSDL2-2.0.so.0.4.1 libsdl2_spec_product_name_windows:=SDL2.dll libsdl2_spec_symlinks_macOS:=libSDL2*.dylib libsdl2_spec_symlinks_linux:=libSDL2*so* - \ No newline at end of file