From b75902ff217bc50d861423a7b2ab78528a292b57 Mon Sep 17 00:00:00 2001 From: Andy Li Date: Sun, 23 Jul 2023 11:03:28 +0000 Subject: [PATCH] New upstream version 4.3.0 --- .devcontainer/devcontainer.json | 24 + .devcontainer/docker-compose.yml | 28 + .../library-scripts/common-debian.sh | 454 ++++++ .../library-scripts/docker-debian.sh | 309 ++++ .earthlyignore | 6 + .github/workflows/cancel.yml | 14 + .github/workflows/main.yml | 719 ++++++---- CONTRIBUTING.md | 54 +- Earthfile | 421 ++++++ Makefile | 17 +- Makefile.win | 11 +- dune-project | 4 +- extra/BUILDING.md | 6 +- extra/CHANGES.txt | 112 +- extra/ImportAll.hx | 3 + extra/choco/haxe.nuspec | 6 +- extra/github-actions/Main.hx | 3 +- extra/github-actions/build-linux.yml | 61 - extra/github-actions/build-mac.yml | 18 +- extra/github-actions/build-windows.yml | 39 +- extra/github-actions/cache-opam.yml | 6 + ...install-neko.yml => install-neko-unix.yml} | 13 +- extra/github-actions/install-neko-windows.yml | 11 + extra/github-actions/install-nsis.yml | 12 + .../install-ocaml-libs-windows.yml | 11 + .../github-actions/install-ocaml-windows.yml | 17 + .../install-ocaml-windows64.yml | 16 + extra/github-actions/test-linux.yml | 46 - extra/github-actions/test-windows.yml | 7 + extra/github-actions/workflows/main.yml | 377 ++++- extra/haxelib_src/CHANGES.md | 9 + extra/haxelib_src/haxelib.json | 6 +- extra/haxelib_src/run.n | Bin 227318 -> 246493 bytes extra/haxelib_src/schema.json | 15 + extra/haxelib_src/src/haxelib/Data.hx | 72 +- extra/haxelib_src/src/haxelib/client/Main.hx | 20 + extra/haxelib_src/src/haxelib/client/Vcs.hx | 60 +- .../libBadDefineJson/doc/defines.json | 5 + .../libraries/libBadDefineJson/haxelib.json | 13 + .../libraries/libBadMetaJson/doc/meta.json | 1 + .../libraries/libBadMetaJson/haxelib.json | 13 + .../libraries/libBadMetaJson2/doc/meta.json | 5 + .../libraries/libBadMetaJson2/haxelib.json | 13 + .../libDocumentationFiles/doc/defines.json | 13 + .../libDocumentationFiles/doc/meta.json | 14 + .../libDocumentationFiles/haxelib.json | 14 + .../libraries/libMissingMetaJson/haxelib.json | 13 + extra/haxelib_src/test/tests/TestData.hx | 21 + extra/images/Readme.png | Bin 8453 -> 14857 bytes extra/installer.nsi | 2 +- .../mac-installer/scripts/neko-postinstall.sh | 4 +- extra/release-checklist.txt | 2 +- libs/Makefile | 4 +- libs/extc/dune | 10 +- libs/javalib/jReader.ml | 8 +- libs/mbedtls/dune | 8 +- libs/objsize/{alloc.c => alloc.h} | 0 libs/objsize/{bitarray.c => bitarray.h} | 2 +- libs/objsize/c_objsize.c | 2 +- libs/objsize/dune | 7 +- libs/pcre/Makefile | 28 - libs/pcre/dune | 7 - libs/pcre/pcre_stubs.c | 736 ---------- libs/pcre2/Makefile | 28 + libs/pcre2/dune | 9 + libs/{pcre/pcre.ml => pcre2/pcre2.ml} | 503 ++++--- libs/pcre2/pcre2_stubs.c | 791 +++++++++++ libs/swflib/as3hlparse.ml | 3 +- opam | 13 +- src-json/define.json | 245 ++-- src-json/meta.json | 36 +- src-json/warning.json | 107 ++ src-prebuild/prebuild.ml | 108 +- src/codegen/codegen.ml | 49 +- src/codegen/dotnet.ml | 35 +- src/codegen/gencommon/castDetect.ml | 61 +- src/codegen/gencommon/closuresToClass.ml | 34 +- src/codegen/gencommon/dynamicOperators.ml | 2 +- src/codegen/gencommon/enumToClass.ml | 10 +- src/codegen/gencommon/fixOverrides.ml | 16 +- src/codegen/gencommon/gencommon.ml | 57 +- src/codegen/gencommon/hardNullableSynf.ml | 2 +- src/codegen/gencommon/initFunction.ml | 23 +- .../gencommon/interfaceVarsDeleteModf.ml | 2 +- .../gencommon/overloadingConstructor.ml | 40 +- src/codegen/gencommon/realTypeParams.ml | 48 +- src/codegen/gencommon/reflectionCFs.ml | 30 +- src/codegen/gencommon/renameTypeParameters.ml | 10 +- .../unreachableCodeEliminationSynf.ml | 6 +- src/codegen/genxml.ml | 15 +- src/codegen/java.ml | 42 +- src/codegen/javaModern.ml | 76 +- src/codegen/overloads.ml | 80 +- src/codegen/swfLoader.ml | 8 +- src/compiler/args.ml | 370 +++++ .../compilationCache.ml} | 29 +- src/compiler/compilationContext.ml | 75 + src/compiler/compiler.ml | 626 +++++++++ src/compiler/displayOutput.ml | 343 ++--- src/compiler/displayProcessing.ml | 357 +++++ src/compiler/displayProcessingGlobals.ml | 3 + src/compiler/generate.ml | 101 ++ src/compiler/haxe.ml | 1214 +--------------- src/compiler/helper.ml | 57 + src/compiler/process_helper.ml | 151 ++ src/compiler/retyper.ml | 279 ++++ src/compiler/server.ml | 1213 +++++++++------- src/compiler/serverCompilationContext.ml | 74 + src/compiler/serverMessage.ml | 30 +- src/compiler/tasks.ml | 67 + src/context/abstractCast.ml | 118 +- src/context/common.ml | 487 ++++--- src/context/commonCache.ml | 88 +- src/context/display/deprecationCheck.ml | 17 +- src/context/display/diagnostics.ml | 51 +- src/context/display/diagnosticsPrinter.ml | 118 +- src/context/display/display.ml | 2 + src/context/display/displayEmitter.ml | 10 +- src/context/display/displayException.ml | 27 +- src/context/display/displayFields.ml | 74 +- src/context/display/displayJson.ml | 73 +- src/context/display/displayPath.ml | 2 +- src/context/display/displayTexpr.ml | 104 +- src/context/display/displayToplevel.ml | 178 +-- src/context/display/findReferences.ml | 47 +- src/context/display/importHandling.ml | 239 +++- src/context/display/statistics.ml | 3 +- src/context/display/syntaxExplorer.ml | 7 +- src/context/memory.ml | 101 +- src/context/purityState.ml | 4 +- src/context/typecore.ml | 252 +++- src/core/abstract.ml | 10 +- src/core/ast.ml | 76 +- src/core/define.ml | 101 +- src/core/display/completionItem.ml | 22 +- src/core/displayTypes.ml | 77 +- src/core/dune | 6 + src/core/error.ml | 72 +- src/core/globals.ml | 117 +- src/core/inheritDoc.ml | 6 +- src/core/json/genjson.ml | 28 +- src/core/meta.ml | 67 +- src/core/numeric.ml | 29 +- src/core/path.ml | 5 + src/core/tFunctions.ml | 113 +- src/core/tOther.ml | 100 +- src/core/tPrinting.ml | 50 +- src/core/tType.ml | 48 +- src/core/tUnification.ml | 102 +- src/core/texpr.ml | 125 +- src/core/warning.ml | 95 ++ src/dune | 4 +- src/filters/defaultArguments.ml | 2 + src/filters/exceptions.ml | 432 ++++-- src/filters/filters.ml | 531 ++++--- src/filters/filtersCommon.ml | 17 +- src/filters/renameVars.ml | 12 +- src/generators/gencpp.ml | 388 ++++-- src/generators/gencs.ml | 52 +- src/generators/genhl.ml | 72 +- src/generators/genhxold.ml | 26 +- src/generators/genjava.ml | 50 +- src/generators/genjs.ml | 20 +- src/generators/genjvm.ml | 440 ++++-- src/generators/genlua.ml | 126 +- src/generators/genneko.ml | 68 +- src/generators/genphp7.ml | 106 +- src/generators/genpy.ml | 23 +- src/generators/genshared.ml | 27 +- src/generators/genswf.ml | 8 +- src/generators/genswf9.ml | 20 +- src/generators/hl2c.ml | 88 +- src/generators/hlcode.ml | 14 +- src/generators/hlinterp.ml | 9 +- src/generators/hlopt.ml | 19 + src/generators/jvm/jvmAttribute.ml | 9 +- src/generators/jvm/jvmBuilder.ml | 65 +- src/generators/jvm/jvmClass.ml | 11 + src/generators/jvm/jvmFunctions.ml | 93 +- src/generators/jvm/jvmMethod.ml | 53 +- src/macro/eval/evalContext.ml | 25 +- src/macro/eval/evalDebugMisc.ml | 42 +- src/macro/eval/evalDebugSocket.ml | 2 + src/macro/eval/evalEmitter.ml | 37 +- src/macro/eval/evalEncode.ml | 10 +- src/macro/eval/evalExceptions.ml | 42 +- src/macro/eval/evalField.ml | 7 +- src/macro/eval/evalHash.ml | 9 +- src/macro/eval/evalIntegers.ml | 4 +- src/macro/eval/evalJit.ml | 16 +- src/macro/eval/evalLuv.ml | 27 +- src/macro/eval/evalMain.ml | 68 +- src/macro/eval/evalMisc.ml | 61 +- src/macro/eval/evalPrinting.ml | 67 +- src/macro/eval/evalPrototype.ml | 23 +- src/macro/eval/evalStdLib.ml | 111 +- src/macro/eval/evalValue.ml | 10 +- src/macro/macroApi.ml | 530 +++++-- src/optimization/analyzer.ml | 136 +- src/optimization/analyzerConfig.ml | 26 +- src/optimization/analyzerTexpr.ml | 501 ++++--- src/optimization/analyzerTexprTransformer.ml | 93 +- src/optimization/analyzerTypes.ml | 25 +- src/optimization/dce.ml | 15 +- src/optimization/inline.ml | 55 +- src/optimization/inlineConstructors.ml | 24 +- src/optimization/optimizer.ml | 71 +- src/optimization/optimizerTexpr.ml | 14 +- src/syntax/grammar.mly | 253 +++- src/syntax/lexer.ml | 88 +- src/syntax/parser.ml | 24 +- src/syntax/parserEntry.ml | 25 +- src/syntax/reification.ml | 47 +- src/typing/callUnification.ml | 217 ++- src/typing/calls.ml | 175 ++- src/typing/fieldAccess.ml | 51 +- src/typing/fields.ml | 97 +- src/typing/finalization.ml | 22 +- src/typing/forLoop.ml | 36 +- src/typing/functionArguments.ml | 43 +- src/typing/generic.ml | 177 +-- src/typing/instanceBuilder.ml | 18 +- src/typing/macroContext.ml | 257 ++-- src/typing/magicTypes.ml | 16 +- src/typing/matcher.ml | 278 ++-- src/typing/nullSafety.ml | 101 +- src/typing/operators.ml | 210 ++- src/typing/overloadResolution.ml | 2 +- src/typing/strictMeta.ml | 154 ++ src/typing/typeload.ml | 311 ++--- src/typing/typeloadCheck.ml | 248 ++-- src/typing/typeloadFields.ml | 707 ++++++---- src/typing/typeloadFunction.ml | 24 +- src/typing/typeloadModule.ml | 1241 +++++++---------- src/typing/typeloadParse.ml | 23 +- src/typing/typer.ml | 816 +++++++---- src/typing/typerBase.ml | 166 ++- src/typing/typerDisplay.ml | 131 +- src/typing/typerDotPath.ml | 26 +- std/Any.hx | 5 +- std/Math.hx | 4 +- std/Std.hx | 28 +- std/StringTools.hx | 6 +- std/Sys.hx | 16 +- std/UnicodeString.hx | 9 +- std/cpp/Int64.hx | 16 +- std/cpp/Int64Map.hx | 154 ++ std/cpp/Lib.hx | 2 +- std/cpp/NativeProcess.hx | 2 +- std/cpp/NativeSys.hx | 2 +- std/cpp/NativeXml.hx | 2 +- std/cpp/UInt64.hx | 17 +- std/cpp/_std/Sys.hx | 2 +- std/cpp/_std/haxe/Int64.hx | 96 +- std/cpp/_std/haxe/atomic/AtomicInt.hx | 46 + std/cpp/_std/haxe/ds/IntMap.hx | 10 + std/cpp/_std/haxe/ds/Map.hx | 209 +++ std/cpp/_std/haxe/ds/ObjectMap.hx | 11 +- std/cpp/_std/haxe/ds/StringMap.hx | 10 + std/cpp/_std/haxe/ds/WeakMap.hx | 1 + std/cpp/_std/sys/io/Process.hx | 4 +- std/cpp/_std/sys/thread/Condition.hx | 33 + std/cpp/_std/sys/thread/Semaphore.hx | 22 + std/cpp/cppia/HostClasses.hx | 4 + std/cs/_std/Std.hx | 102 +- std/cs/_std/Sys.hx | 20 +- std/cs/_std/haxe/Rest.hx | 14 +- std/cs/_std/haxe/atomic/AtomicInt.hx | 61 + std/cs/_std/haxe/atomic/AtomicObject.hx | 33 + std/cs/_std/haxe/ds/IntMap.hx | 4 +- std/cs/_std/haxe/ds/ObjectMap.hx | 4 +- std/cs/_std/haxe/ds/StringMap.hx | 4 +- std/cs/_std/sys/thread/Condition.hx | 37 + std/cs/_std/sys/thread/Semaphore.hx | 22 + std/cs/internal/HxObject.hx | 4 +- std/eval/_std/Sys.hx | 2 +- std/eval/_std/sys/net/Host.hx | 8 +- std/eval/_std/sys/thread/Condition.hx | 41 + std/eval/_std/sys/thread/EventLoop.hx | 100 +- std/eval/_std/sys/thread/Semaphore.hx | 36 + std/eval/integers/UInt64.hx | 4 - std/eval/luv/Dir.hx | 8 +- std/eval/luv/Env.hx | 5 + std/eval/luv/Resource.hx | 2 +- std/eval/luv/UVError.hx | 4 + std/flash/Lib.hx | 6 + std/flash/_std/Std.hx | 13 +- std/flash/_std/haxe/ds/IntMap.hx | 4 +- std/flash/_std/haxe/ds/ObjectMap.hx | 4 +- std/flash/_std/haxe/ds/StringMap.hx | 4 +- std/flash/_std/haxe/ds/UnsafeStringMap.hx | 4 +- std/flash/_std/haxe/ds/WeakMap.hx | 4 +- std/flash/globalization/DateTimeFormatter.hx | 2 +- std/haxe/EntryPoint.hx | 17 + std/haxe/EnumFlags.hx | 24 + std/haxe/EnumTools.hx | 7 +- std/haxe/Json.hx | 2 +- std/haxe/MainLoop.hx | 27 +- std/haxe/Rest.hx | 2 +- std/haxe/SysTools.hx | 4 +- std/haxe/Timer.hx | 5 + std/haxe/atomic/AtomicBool.hx | 55 + std/haxe/atomic/AtomicInt.hx | 67 + std/haxe/atomic/AtomicObject.hx | 44 + std/haxe/display/Display.hx | 1 + std/haxe/display/JsonModuleTypes.hx | 1 + std/haxe/display/Server.hx | 9 +- std/haxe/ds/BalancedTree.hx | 4 +- std/haxe/ds/EnumValueMap.hx | 14 + std/haxe/ds/Map.hx | 2 - std/haxe/ds/Vector.hx | 49 +- std/haxe/format/JsonPrinter.hx | 4 +- std/haxe/http/HttpJs.hx | 17 + std/haxe/http/HttpNodeJs.hx | 11 + std/haxe/io/Mime.hx | 1 + std/haxe/iterators/ArrayKeyValueIterator.hx | 9 +- std/haxe/macro/CompilationServer.hx | 4 + std/haxe/macro/Compiler.hx | 278 +++- std/haxe/macro/ComplexTypeTools.hx | 2 +- std/haxe/macro/Context.hx | 265 +++- std/haxe/macro/DisplayMode.hx | 15 + std/haxe/macro/ExampleJSGenerator.hx | 8 +- std/haxe/macro/Expr.hx | 38 +- std/haxe/macro/ExprTools.hx | 4 +- std/haxe/macro/MacroStringTools.hx | 2 +- std/haxe/macro/PlatformConfig.hx | 206 +++ std/haxe/macro/PositionTools.hx | 2 +- std/haxe/macro/Printer.hx | 19 +- std/haxe/macro/Type.hx | 5 + std/haxe/macro/TypeTools.hx | 40 +- std/haxe/macro/TypedExprTools.hx | 2 +- std/hl/Api.hx | 5 + std/hl/Atomics.hx | 19 + std/hl/CArray.hx | 36 + std/hl/I64.hx | 44 +- std/hl/UI.hx | 2 +- std/hl/_std/Sys.hx | 2 +- std/hl/_std/Type.hx | 16 +- std/hl/_std/haxe/Exception.hx | 2 +- std/hl/_std/haxe/Int64.hx | 608 ++++++++ std/hl/_std/haxe/NativeStackTrace.hx | 58 +- std/hl/_std/haxe/atomic/AtomicInt.hx | 49 + std/hl/_std/haxe/atomic/AtomicObject.hx | 31 + std/hl/_std/haxe/ds/IntMap.hx | 4 +- std/hl/_std/haxe/ds/ObjectMap.hx | 4 +- std/hl/_std/haxe/ds/StringMap.hx | 4 +- std/hl/_std/haxe/ds/Vector.hx | 13 +- std/hl/_std/haxe/io/FPHelper.hx | 4 + std/hl/_std/sys/thread/Condition.hx | 50 + std/hl/_std/sys/thread/Semaphore.hx | 32 + std/hl/_std/sys/thread/Thread.hx | 58 +- std/hl/hl_version | 2 +- std/hl/types/ArrayBytes.hx | 28 +- std/hl/types/ArrayDyn.hx | 20 +- std/hl/types/ArrayObj.hx | 22 +- std/hl/types/Int64Map.hx | 74 + std/hl/uv/Fs.hx | 51 + std/java/_std/Std.hx | 102 +- std/java/_std/Sys.hx | 18 +- std/java/_std/Type.hx | 4 +- std/java/_std/haxe/Rest.hx | 94 +- std/java/_std/haxe/atomic/AtomicBool.hx | 34 + std/java/_std/haxe/atomic/AtomicInt.hx | 64 + std/java/_std/haxe/atomic/AtomicObject.hx | 34 + std/java/_std/haxe/ds/IntMap.hx | 4 +- std/java/_std/haxe/ds/ObjectMap.hx | 4 +- std/java/_std/haxe/ds/StringMap.hx | 4 +- std/java/_std/haxe/ds/WeakMap.hx | 4 +- std/java/_std/sys/FileSystem.hx | 48 +- std/java/_std/sys/io/Process.hx | 2 +- std/java/_std/sys/thread/Condition.hx | 45 + std/java/_std/sys/thread/Semaphore.hx | 25 + std/java/internal/HxObject.hx | 4 +- std/js/Cookie.hx | 8 +- std/js/Lib.hx | 10 +- std/js/_std/Reflect.hx | 8 +- std/js/_std/Std.hx | 27 +- std/js/_std/haxe/NativeStackTrace.hx | 2 +- std/js/_std/haxe/atomic/AtomicInt.hx | 50 + std/js/_std/haxe/ds/IntMap.hx | 4 +- std/js/_std/haxe/ds/ObjectMap.hx | 11 +- std/js/_std/haxe/ds/StringMap.hx | 8 +- std/js/html/BeforeInstallPromptEvent.hx | 34 + std/js/html/DOMElement.hx | 4 +- std/js/html/DialogElement.hx | 60 + std/js/html/Document.hx | 9 +- std/js/html/FullscreenOptions.hx | 40 + std/js/html/HTMLDocument.hx | 5 +- std/js/html/Window.hx | 7 +- std/js/html/WorkerGlobalScope.hx | 8 +- std/js/lib/Atomics.hx | 99 ++ std/js/lib/BufferSource.hx | 12 +- std/js/lib/Promise.hx | 13 +- std/js/lib/SharedArrayBuffer.hx | 16 + std/js/lib/WeakRef.hx | 38 + std/js/lib/intl/DisplayNames.hx | 133 ++ std/js/lib/intl/ListFormat.hx | 121 ++ std/jvm/Closure.hx | 2 +- std/jvm/Jvm.hx | 2 +- std/jvm/_std/Std.hx | 103 +- std/jvm/_std/Type.hx | 31 +- std/jvm/_std/haxe/Rest.hx | 61 + std/jvm/_std/haxe/ds/StringMap.hx | 4 +- std/lua/FileHandle.hx | 1 + std/lua/_lua/_hx_anon.lua | 4 +- std/lua/_lua/_hx_bit.lua | 31 +- std/lua/_lua/_hx_handle_error.lua | 8 + std/lua/_lua/_hx_luv.lua | 8 + std/lua/_lua/_hx_tostring.lua | 13 - std/lua/_std/EReg.hx | 4 +- std/lua/_std/Reflect.hx | 10 +- std/lua/_std/Std.hx | 28 +- std/lua/_std/String.hx | 14 +- std/lua/_std/Sys.hx | 6 +- std/lua/_std/haxe/Exception.hx | 1 + std/lua/_std/haxe/ds/IntMap.hx | 4 +- std/lua/_std/haxe/ds/ObjectMap.hx | 4 +- std/lua/_std/haxe/ds/StringMap.hx | 6 +- std/lua/_std/haxe/format/JsonParser.hx | 4 + std/lua/_std/sys/net/Socket.hx | 74 +- std/lua/_std/sys/net/SocketInput.hx | 69 + std/lua/_std/sys/net/SocketOutput.hx | 61 + std/lua/_std/sys/ssl/Socket.hx | 69 + std/lua/lib/lrexlib/Rex.hx | 2 +- std/lua/lib/luasec/Ssl.hx | 44 + std/lua/lib/luasec/SslTcpClient.hx | 29 + std/lua/lib/luv/Check.hx | 2 +- std/lua/lib/luv/Loop.hx | 5 + std/lua/lib/luv/fs/FileSystem.hx | 57 +- std/neko/Boot.hx | 19 +- std/neko/_std/Sys.hx | 2 +- std/neko/_std/haxe/ds/IntMap.hx | 4 +- std/neko/_std/haxe/ds/ObjectMap.hx | 4 +- std/neko/_std/haxe/ds/StringMap.hx | 4 +- std/php/Boot.hx | 13 +- std/php/Const.hx | 58 + std/php/DirectoryIterator.hx | 40 + std/php/FilesystemIterator.hx | 51 + std/php/Global.hx | 292 +++- std/php/Lib.hx | 2 +- std/php/Phar.hx | 104 ++ std/php/PharData.hx | 59 + std/php/PharException.hx | 30 + std/php/PharFileInfo.hx | 46 + std/php/RecursiveDirectoryIterator.hx | 39 + std/php/RecursiveIterator.hx | 33 + std/php/ResourceBundle.hx | 42 + std/php/SplFileInfo.hx | 73 + std/php/SplFileObject.hx | 71 + std/php/_polyfills.php | 8 + std/php/_std/Array.hx | 11 +- std/php/_std/Std.hx | 33 +- std/php/_std/Sys.hx | 51 +- std/php/_std/haxe/Rest.hx | 5 + std/php/_std/haxe/crypto/Base64.hx | 22 +- std/php/_std/haxe/ds/IntMap.hx | 2 +- std/php/_std/haxe/ds/ObjectMap.hx | 4 +- std/php/_std/haxe/ds/StringMap.hx | 2 +- std/php/_std/haxe/ds/Vector.hx | 21 +- std/php/db/PDO.hx | 2 +- std/python/_std/Std.hx | 103 +- std/python/_std/Sys.hx | 36 +- std/python/_std/haxe/ds/IntMap.hx | 4 +- std/python/_std/haxe/ds/ObjectMap.hx | 4 +- std/python/_std/haxe/ds/StringMap.hx | 4 +- std/python/_std/sys/net/Socket.hx | 5 + std/python/_std/sys/thread/Condition.hx | 34 + std/python/_std/sys/thread/Deque.hx | 20 +- std/python/_std/sys/thread/Lock.hx | 14 +- std/python/_std/sys/thread/Mutex.hx | 13 +- std/python/_std/sys/thread/Semaphore.hx | 24 + std/python/_std/sys/thread/Thread.hx | 29 +- std/python/lib/Os.hx | 6 + std/python/lib/Subprocess.hx | 9 + std/python/lib/socket/Socket.hx | 17 + std/python/lib/threading/Condition.hx | 12 + std/python/lib/threading/Semaphore.hx | 9 + std/python/net/SslSocket.hx | 39 +- std/sys/Http.hx | 47 +- std/sys/thread/Condition.hx | 58 + std/sys/thread/EventLoop.hx | 30 +- std/sys/thread/Semaphore.hx | 33 + tests/PHP.ini | 5 + tests/README.md | 7 +- tests/RunCi.hx | 27 +- tests/display/src/Diagnostic.hx | 58 +- tests/display/src/cases/Abstract.hx | 35 + tests/display/src/cases/DocumentSymbols.hx | 11 +- tests/display/src/cases/Issue10004.hx | 49 + tests/display/src/cases/Issue10194.hx | 23 + tests/display/src/cases/Issue10412.hx | 78 ++ tests/display/src/cases/Issue10429.hx | 29 + tests/display/src/cases/Issue10633.hx | 31 + tests/display/src/cases/Issue10634.hx | 14 + tests/display/src/cases/Issue10691.hx | 63 + tests/display/src/cases/Issue10704.hx | 17 + tests/display/src/cases/Issue10713.hx | 22 + .../{Issue5172.hx.disabled => Issue5172.hx} | 0 tests/display/src/cases/Issue5306.hx | 16 +- tests/display/src/cases/Issue7328.hx | 4 +- tests/display/src/cases/Issue7777.hx | 1 + tests/display/src/cases/Issue7932.hx | 1 + tests/display/src/cases/Issue7935.hx | 1 + tests/display/src/cases/Issue7938.hx | 1 + tests/display/src/cases/Issue7939.hx | 1 + tests/display/src/cases/Issue7940.hx | 1 + tests/display/src/cases/Issue7943.hx | 13 +- tests/display/src/cases/Issue7944.hx | 1 + tests/display/src/cases/Issue7945.hx | 2 + tests/display/src/cases/Issue7946.hx | 2 + tests/display/src/cases/Issue7947.hx | 2 + tests/display/src/cases/Issue7948.hx | 4 +- tests/display/src/cases/Metadata.hx | 81 ++ tests/display/src/cases/Signature.hx | 50 + .../display/src/cases/StructureCompletion.hx | 16 +- tests/display/src/misc/issue10704/Statics.hx | 10 + tests/docgen/dox.hxml | 8 + tests/echoServer/www/crossdomain.xml | 8 +- tests/misc/compile.hxml | 2 +- .../projects/Issue10184/CheckOptionsTxt.hx | 28 + .../cpp/projects/Issue10184/options-txt.hxml | 5 + tests/misc/cpp/run.hxml | 2 + tests/misc/eventLoop/Main.hx | 5 +- tests/misc/eventLoop/build-cpp.hxml | 3 + .../eventLoop/build-cpp.hxml.stdout.disabled | 23 + tests/misc/eventLoop/build-hl.hxml | 3 + .../eventLoop/build-hl.hxml.stdout.disabled | 23 + .../misc/hl/projects/Issue10184/CheckJson.hx | 28 + .../misc/hl/projects/Issue10184/hlc-json.hxml | 5 + .../projects/Issue10376/CheckSourceHeader.hx | 56 + .../hl/projects/Issue10376/custom-header.hxml | 5 + .../projects/Issue10376/default-header.hxml | 5 + .../projects/Issue10376/no-header-dash.hxml | 5 + tests/misc/hl/run.hxml | 2 + tests/misc/java/projects/Issue10280/jvm.jar | Bin 88000 -> 0 bytes tests/misc/java/projects/Issue10517/Main.hx | 6 + .../projects/Issue10517/compile-fail.hxml | 3 + .../Issue10517/compile-fail.hxml.stderr | 1 + tests/misc/java/projects/Issue11014/Main.hx | 34 + .../java/projects/Issue11014/compile.hxml | 3 + .../projects/Issue11014/compile.hxml.stdout | 4 + tests/misc/java/projects/Issue11054/Main.hx | 9 + tests/misc/java/projects/Issue11054/Setup.hx | 4 + .../java/projects/Issue11054/compile.hxml | 12 + .../projects/Issue11054/compile.hxml.stdout | 1 + .../Issue11054/pack/ItemGroupEvents.java | 7 + .../projects/Issue10184/CheckSourceHeader.hx | 35 + tests/misc/js/projects/Issue10184/Main.hx | 3 + tests/misc/js/projects/Issue10184/Tools.hx | 12 + .../Issue10184/custom-header-dash.hxml | 4 + .../Issue10184/custom-header-underscore.hxml | 4 + .../projects/Issue10184/default-header.hxml | 4 + .../projects/Issue10184/no-header-dash.hxml | 4 + .../Issue10184/no-header-underscore.hxml | 4 + tests/misc/js/run.hxml | 2 + .../lua/projects/Issue10979/HaxeException.hx | 5 + .../lua/projects/Issue10979/NativeError.hx | 6 + .../misc/lua/projects/Issue10979/RunScript.hx | 38 + .../projects/Issue10979/embedded-native.hxml | 4 + .../Issue10979/embedded-native.hxml.stdout | 3 + .../lua/projects/Issue10979/embedded.hxml | 4 + .../projects/Issue10979/embedded.hxml.stdout | 3 + tests/misc/lua/projects/Issue10979/runner.lua | 10 + .../Issue10979/standalone-native.hxml | 7 + .../Issue10979/standalone-native.hxml.stdout | 3 + .../lua/projects/Issue10979/standalone.hxml | 7 + .../Issue10979/standalone.hxml.stdout | 3 + tests/misc/lua/projects/Issue9530/Main.hx | 21 + .../misc/lua/projects/Issue9530/compile.hxml | 4 + .../projects/Issue9530/compile.hxml.stdout | 6 + tests/misc/neko/projects/Issue10937/Main.hx | 70 + .../neko/projects/Issue10937/aaa-setup.hxml | 1 + .../neko/projects/Issue10937/compile.hxml | 4 + .../projects/Issue10937/compile.hxml.stdout | 1 + .../Issue10937/dummy_ndll/ndll/.exists | 0 tests/misc/neko/run.hxml | 2 + .../misc/php/projects/Issue10736/compile.hxml | 18 + .../misc/php/projects/Issue10736/src/App1.hx | 5 + .../misc/php/projects/Issue10736/src/App2.hx | 5 + tests/misc/php/projects/Issue10820/Foo.hx | 8 + tests/misc/php/projects/Issue10820/Main.hx | 8 + .../misc/php/projects/Issue10820/compile.hxml | 4 + .../misc/php/projects/Issue10820/pack/Foo.hx | 5 + .../Issue10005/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue10034/Main.hx | 13 + .../compile-fail.hxml | 0 .../Issue10034/compile-fail.hxml.stderr | 2 + .../Issue10039/compile-fail.hxml.stderr | 2 +- .../Issue10052/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue10107/Main.hx | 7 + tests/misc/projects/Issue10107/compile.hxml | 3 + tests/misc/projects/Issue10139/Main.hx | 11 + .../projects/Issue10139/compile-fail.hxml | 2 + .../Issue10139/compile-fail.hxml.stderr | 4 + tests/misc/projects/Issue10147/Main.hx | 28 + .../projects/Issue10147/compile-fail.hxml | 2 + .../Issue10147/compile-fail.hxml.stderr | 3 + tests/misc/projects/Issue10152/Main.hx | 9 + .../projects/Issue10152/compile-defined.hxml | 3 + .../Issue10152/compile-defined.hxml.stdout | 1 + .../compile-undefined.hxml} | 0 .../Issue10152/compile-undefined.hxml.stdout | 1 + tests/misc/projects/Issue10170/Main.hx | 7 + .../projects/Issue10170/compile-fail.hxml | 3 + .../Issue10170/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10171/Main.hx | 14 + tests/misc/projects/Issue10171/compile.hxml | 4 + .../projects/Issue10171/compile.hxml.stderr | 1 + tests/misc/projects/Issue10184/BuiltIn.hx | 12 + tests/misc/projects/Issue10184/Deprecated.hx | 4 + tests/misc/projects/Issue10184/Flags.hx | 27 + tests/misc/projects/Issue10184/GetDefines.hx | 27 + tests/misc/projects/Issue10184/IfBlocks.hx | 25 + tests/misc/projects/Issue10184/Repeated.hx | 25 + tests/misc/projects/Issue10184/builtin.hxml | 1 + .../projects/Issue10184/check-warning.hxml | 2 + .../Issue10184/check-warning.hxml.stderr | 1 + tests/misc/projects/Issue10184/cli.hxml | 7 + .../misc/projects/Issue10184/get-defines.hxml | 18 + tests/misc/projects/Issue10184/if-blocks.hxml | 18 + tests/misc/projects/Issue10184/macro.hxml | 7 + .../Issue10184/no-warning-underscore.hxml | 3 + .../no-warning-underscore.hxml.stderr | 0 .../misc/projects/Issue10184/no-warning.hxml | 3 + .../Issue10184/no-warning.hxml.stderr | 0 tests/misc/projects/Issue10184/repeated.hxml | 18 + tests/misc/projects/Issue10186/Main.hx | 6 + .../projects/Issue10186/compile-fail.hxml | 1 + .../Issue10186/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10189/Main.hx | 17 + .../projects/Issue10189/compile-fail.hxml | 1 + .../Issue10189/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10198/Main.hx | 7 + tests/misc/projects/Issue10198/Main2.hx | 31 + tests/misc/projects/Issue10198/Main3.hx | 12 + .../projects/Issue10198/compile-fail.hxml | 1 + .../Issue10198/compile-fail.hxml.stderr | 2 + .../projects/Issue10198/compile2-fail.hxml | 1 + .../Issue10198/compile2-fail.hxml.stderr | 3 + .../projects/Issue10198/compile3-fail.hxml | 1 + .../Issue10198/compile3-fail.hxml.stderr | 3 + tests/misc/projects/Issue10205/Main.hx | 8 - .../Issue10205/compile-fail.hxml.stderr | 3 - tests/misc/projects/Issue10219/Main.hx | 9 + .../projects/Issue10219/compile-fail.hxml | 1 + .../Issue10219/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10229/Main.hx | 25 + .../projects/Issue10229/compile-fail.hxml | 1 + .../Issue10229/compile-fail.hxml.stderr | 6 + tests/misc/projects/Issue10245/Main.hx | 2 + .../projects/Issue10245/compile-fail.hxml | 8 + .../Issue10245/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10264/Main.hx | 10 + .../projects/Issue10264/compile-fail.hxml | 1 + .../Issue10264/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10291/Main.hx | 15 + .../projects/Issue10291/compile-fail.hxml | 1 + .../Issue10291/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10358/Main.hx | 5 + tests/misc/projects/Issue10358/Main2.hx | 5 + tests/misc/projects/Issue10358/Main3.hx | 5 + .../projects/Issue10358/compile-fail.hxml | 1 + .../Issue10358/compile-fail.hxml.stderr | 1 + .../projects/Issue10358/compile2-fail.hxml | 1 + .../Issue10358/compile2-fail.hxml.stderr | 1 + .../projects/Issue10358/compile3-fail.hxml | 1 + .../Issue10358/compile3-fail.hxml.stderr | 1 + tests/misc/projects/Issue10426/Main.hx | 7 + .../projects/Issue10426/compile-fail.hxml | 1 + .../Issue10426/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10482/Main.hx | 7 + .../projects/Issue10482/compile-fail.hxml | 2 + .../Issue10482/compile-fail.hxml.stderr | 3 + tests/misc/projects/Issue10504/Main.hx | 12 + .../projects/Issue10504/compile-fail.hxml | 1 + .../Issue10504/compile-fail.hxml.stderr | 5 + tests/misc/projects/Issue10521/Main.hx | 22 + .../projects/Issue10521/compile-fail.hxml | 2 + .../Issue10521/compile-fail.hxml.stderr | 2 + tests/misc/projects/Issue10587/Macro.hx | 3 + tests/misc/projects/Issue10587/MainImport.hx | 7 + tests/misc/projects/Issue10587/MainUsing.hx | 7 + .../Issue10587/compile-import-fail.hxml | 2 + .../compile-import-fail.hxml.stderr | 1 + .../Issue10587/compile-using-fail.hxml | 2 + .../Issue10587/compile-using-fail.hxml.stderr | 1 + tests/misc/projects/Issue10591/Main.hx | 27 + tests/misc/projects/Issue10591/compile.hxml | 8 + .../projects/Issue10591/compile.hxml.stderr | 14 + tests/misc/projects/Issue10623/Main.hx | 7 + .../projects/Issue10623/compile-fail.hxml | 2 + .../Issue10623/compile-fail.hxml.stderr | 2 + .../misc/projects/Issue10623/indent-fail.hxml | 2 + .../Issue10623/indent-fail.hxml.stderr | 2 + .../misc/projects/Issue10623/pretty-fail.hxml | 3 + .../Issue10623/pretty-fail.hxml.stderr | 10 + tests/misc/projects/Issue10687/Main.hx | 24 + .../projects/Issue10687/compile-fail.hxml | 2 + .../Issue10687/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10711/Main.hx | 5 + tests/misc/projects/Issue10711/compile.hxml | 7 + .../projects/Issue10711/compile.hxml.stderr | 0 tests/misc/projects/Issue10711/lib1/X.hx | 1 + tests/misc/projects/Issue10711/lib2/X.hx | 1 + tests/misc/projects/Issue10729/Main.hx | 15 + .../projects/Issue10729/compile-fail.hxml | 2 + .../Issue10729/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10739/Main.hx | 50 + tests/misc/projects/Issue10739/compile.hxml | 3 + .../projects/Issue10739/compile.hxml.stdout | 1 + tests/misc/projects/Issue10793/Main.hx | 25 + tests/misc/projects/Issue10793/compile.hxml | 3 + .../projects/Issue10793/compile.hxml.stderr | 1 + tests/misc/projects/Issue10804/Main.hx | 3 + tests/misc/projects/Issue10804/Main2.hx | 7 + .../projects/Issue10804/compile-fail.hxml | 2 + .../Issue10804/compile-fail.hxml.stderr | 1 + .../projects/Issue10804/compile2-fail.hxml | 2 + .../Issue10804/compile2-fail.hxml.stderr | 1 + tests/misc/projects/Issue10819/Main.hx | 10 + tests/misc/projects/Issue10819/compile.hxml | 3 + .../projects/Issue10819/compile.hxml.stderr | 0 tests/misc/projects/Issue10825/Main.hx | 14 + .../projects/Issue10825/compile-fail.hxml | 2 + .../Issue10825/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10832/Main.hx | 21 + tests/misc/projects/Issue10832/compile.hxml | 2 + .../projects/Issue10832/compile.hxml.stdout | 3 + tests/misc/projects/Issue10844/Fail.hx | 0 tests/misc/projects/Issue10844/aaa-setup.hxml | 2 + tests/misc/projects/Issue10844/base-fail.hxml | 2 + .../Issue10844/custom-define-nofail.hxml | 2 + .../custom-define-nofail.hxml.stdout | 2 + .../projects/Issue10844/custom-define.hxml | 2 + .../Issue10844/custom-define.hxml.stdout | 2 + .../Issue10844/custom-meta-nofail.hxml | 2 + .../Issue10844/custom-meta-nofail.hxml.stdout | 5 + .../misc/projects/Issue10844/custom-meta.hxml | 2 + .../Issue10844/custom-meta.hxml.stdout | 5 + tests/misc/projects/Issue10844/define.json | 6 + .../Issue10844/dummy_doc/doc/define.json | 6 + .../Issue10844/dummy_doc/doc/meta.json | 17 + .../Issue10844/dummy_doc/haxelib.json | 16 + .../Issue10844/dummy_doc_dep/doc/define.json | 7 + .../Issue10844/dummy_doc_dep/doc/meta.json | 6 + .../Issue10844/dummy_doc_dep/haxelib.json | 13 + tests/misc/projects/Issue10844/meta.json | 6 + .../Issue10844/user-defined-define-fail.hxml | 2 + .../user-defined-define-fail.hxml.stderr | 2 + .../user-defined-define-json-fail.hxml | 2 + .../user-defined-define-json-fail.hxml.stderr | 3 + .../Issue10844/user-defined-defines.hxml | 4 + .../user-defined-defines.hxml.stdout | 4 + .../Issue10844/user-defined-meta-fail.hxml | 2 + .../user-defined-meta-fail.hxml.stderr | 2 + .../user-defined-meta-indent-fail.hxml | 2 + .../user-defined-meta-indent-fail.hxml.stderr | 2 + .../user-defined-meta-json-fail.hxml | 2 + .../user-defined-meta-json-fail.hxml.stderr | 3 + .../user-defined-meta-json-indent-fail.hxml | 2 + ...-defined-meta-json-indent-fail.hxml.stderr | 3 + .../user-defined-meta-json-pretty-fail.hxml | 4 + ...-defined-meta-json-pretty-fail.hxml.stderr | 12 + .../user-defined-meta-pretty-fail.hxml | 4 + .../user-defined-meta-pretty-fail.hxml.stderr | 5 + .../Issue10844/user-defined-metas.hxml | 4 + .../Issue10844/user-defined-metas.hxml.stdout | 8 + .../user-defines-did-something.hxml | 1 + .../user-defines-did-something.hxml.stdout | 0 .../Issue10844/user-metas-did-something.hxml | 1 + .../user-metas-did-something.hxml.stdout | 0 tests/misc/projects/Issue10845/Main.hx | 47 + .../projects/Issue10845/compile-fail.hxml | 2 + .../Issue10845/compile-fail.hxml.stderr | 12 + tests/misc/projects/Issue10847/bad/Macro.hx | 9 + tests/misc/projects/Issue10847/bad/Main.hx | 10 + .../projects/Issue10847/bad/compile-fail.hxml | 2 + .../Issue10847/bad/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10847/good/Macro.hx | 9 + tests/misc/projects/Issue10847/good/Main.hx | 10 + .../projects/Issue10847/good/compile.hxml | 2 + tests/misc/projects/Issue10852/Main.hx | 6 + .../projects/Issue10852/compile-fail.hxml | 2 + .../Issue10852/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10863/Main.js.hx | 5 + tests/misc/projects/Issue10863/Main.macro.hx | 8 + tests/misc/projects/Issue10863/compile.hxml | 5 + .../projects/Issue10863/compile.hxml.stderr | 12 + .../projects/Issue10863/logmode-fail.hxml | 3 + .../Issue10863/logmode-fail.hxml.stderr | 1 + .../projects/Issue10863/reporting-fail.hxml | 2 + .../Issue10863/reporting-fail.hxml.stderr | 1 + tests/misc/projects/Issue10865/Main.hx | 9 + .../projects/Issue10865/compile-fail.hxml | 2 + .../Issue10865/compile-fail.hxml.stderr | 1 + .../misc/projects/Issue10871/Compiler/Main.hx | 32 + .../projects/Issue10871/Compiler/MainFail.hx | 5 + .../Issue10871/Compiler/compile-fail.hxml | 2 + .../Compiler/compile-fail.hxml.stderr | 1 + .../Issue10871/Compiler/compile1.hxml | 3 + .../Issue10871/Compiler/compile1.hxml.stdout | 18 + .../Issue10871/Compiler/compile2.hxml | 5 + .../Issue10871/Compiler/compile2.hxml.stdout | 19 + .../Issue10871/Compiler/compile3.hxml | 5 + .../Issue10871/Compiler/compile3.hxml.stdout | 18 + .../misc/projects/Issue10871/Context/Main.hx | 43 + .../projects/Issue10871/Context/compile.hxml | 3 + .../Issue10871/Context/compile.hxml.stdout | 5 + .../projects/Issue10871/TypeTools/Main.hx | 118 ++ .../Issue10871/TypeTools/compile.hxml | 3 + .../Issue10871/TypeTools/compile.hxml.stdout | 0 tests/misc/projects/Issue10880/Main.hx | 5 + .../projects/Issue10880/compile-fail.hxml | 2 + .../Issue10880/compile-fail.hxml.stderr | 2 + tests/misc/projects/Issue10881/Main.hx | 7 + .../projects/Issue10881/compile-fail.hxml | 2 + .../Issue10881/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue10883/Main.hx | 15 + .../projects/Issue10883/compile-fail.hxml | 2 + .../Issue10883/compile-fail.hxml.stderr | 2 + tests/misc/projects/Issue10890/Main.hx | 5 + tests/misc/projects/Issue10890/build.hxml | 1 + .../projects/Issue10897/AbstractFields.hx | 3 + tests/misc/projects/Issue10897/ClassFields.hx | 3 + tests/misc/projects/Issue10897/EnumFields.hx | 3 + .../Issue10897/ImportAbstractField.hx | 3 + .../projects/Issue10897/ImportClassField.hx | 3 + .../projects/Issue10897/ImportEnumField.hx | 3 + .../projects/Issue10897/ImportModuleField.hx | 3 + .../misc/projects/Issue10897/ImportSubtype.hx | 3 + .../misc/projects/Issue10897/ModuleFields.hx | 1 + tests/misc/projects/Issue10897/Types.hx | 1 + .../import-abstract-field-fail.hxml | 2 + .../import-abstract-field-fail.hxml.stderr | 1 + .../Issue10897/import-class-field-fail.hxml | 2 + .../import-class-field-fail.hxml.stderr | 1 + .../Issue10897/import-enum-field-fail.hxml | 2 + .../import-enum-field-fail.hxml.stderr | 1 + .../Issue10897/import-module-field-fail.hxml | 2 + .../import-module-field-fail.hxml.stderr | 1 + .../Issue10897/import-subtype-fail.hxml | 2 + .../import-subtype-fail.hxml.stderr | 1 + tests/misc/projects/Issue10935/Main.hx | 6 + tests/misc/projects/Issue10935/build.hxml | 1 + tests/misc/projects/Issue10961/Main.hx | 37 + .../projects/Issue10961/compile-fail.hxml | 2 + .../Issue10961/compile-fail.hxml.stderr | 3 + tests/misc/projects/Issue10980/Main.hx | 18 + tests/misc/projects/Issue10980/compile.hxml | 3 + .../projects/Issue10980/compile.hxml.stderr | 1 + tests/misc/projects/Issue11004/Bar.hx | 49 + tests/misc/projects/Issue11004/Foo.hx | 13 + tests/misc/projects/Issue11004/Main.hx | 8 + tests/misc/projects/Issue11004/build.hxml | 1 + .../projects/Issue11004/build.hxml.stdout | 1 + tests/misc/projects/Issue11020/Main.hx | 17 + tests/misc/projects/Issue11020/compile.hxml | 4 + .../projects/Issue11020/compile.hxml.stderr | 1 + tests/misc/projects/Issue11055/Main.hx | 14 + .../projects/Issue11055/compile-fail.hxml | 1 + .../Issue11055/compile-fail.hxml.stderr | 3 + .../Issue11055/compile-pretty-fail.hxml | 3 + .../compile-pretty-fail.hxml.stderr | 14 + .../projects/Issue2508/compile.hxml.stderr | 20 +- tests/misc/projects/Issue2996/A.hx | 1 - tests/misc/projects/Issue2996/Main.hx | 5 - tests/misc/projects/Issue2996/compile1.hxml | 1 - .../projects/Issue2996/compile1.hxml.stderr | 3 - tests/misc/projects/Issue2996/compile2.hxml | 1 - .../projects/Issue2996/compile2.hxml.stderr | 3 - tests/misc/projects/Issue2996/pack/B.hx | 3 - .../projects/Issue4720/compile.hxml.stderr | 48 +- tests/misc/projects/Issue4803/Main2.hx | 8 + .../projects/Issue4803/compile2-fail.hxml | 3 + .../Issue4803/compile2-fail.hxml.stderr | 2 + .../Issue4907/compile-fail.hxml.stderr | 2 +- .../Issue5644/compile-fail.hxml.stderr | 3 +- .../misc/projects/Issue5644/pretty-fail.hxml | 3 + .../Issue5644/pretty-fail.hxml.stderr | 6 + tests/misc/projects/Issue5854/NamedLocal.hx | 10 + tests/misc/projects/Issue5854/UnnamedLocal.hx | 8 + .../misc/projects/Issue5854/compile-fail.hxml | 1 + .../Issue5854/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue5854/compile.hxml | 1 + .../projects/Issue5854/compile.hxml.stderr | 0 .../Issue5880/compile5-fail.hxml.stderr | 3 +- .../misc/projects/Issue5949/indent-fail.hxml | 2 + .../Issue5949/indent-fail.hxml.stderr | 2 + .../misc/projects/Issue5949/pretty-fail.hxml | 3 + .../Issue5949/pretty-fail.hxml.stderr | 10 + .../misc/projects/Issue6065/indent-fail.hxml | 2 + .../Issue6065/indent-fail.hxml.stderr | 14 + .../misc/projects/Issue6065/pretty-fail.hxml | 3 + .../Issue6065/pretty-fail.hxml.stderr | 40 + .../projects/Issue6226/compile.hxml.stderr | 2 +- .../Issue6584/compile1-fail.hxml.stderr | 2 +- .../Issue6584/compile2-fail.hxml.stderr | 2 +- .../Issue6584/compile3-fail.hxml.stderr | 2 +- .../Issue6584/compile4-fail.hxml.stderr | 3 +- .../misc/projects/Issue6584/pretty-fail.hxml | 3 + .../Issue6584/pretty-fail.hxml.stderr | 14 + .../misc/projects/Issue6790/pretty-fail.hxml | 3 + .../Issue6790/pretty-fail.hxml.stderr | 7 + tests/misc/projects/Issue6794/Main.hx | 17 + tests/misc/projects/Issue6794/compile.hxml | 2 + .../projects/Issue6794/compile.hxml.stderr | 1 + .../misc/projects/Issue6796/pretty-fail.hxml | 3 + .../Issue6796/pretty-fail.hxml.stderr | 7 + .../Issue6810/compile-fail.hxml.stderr | 2 - .../misc/projects/Issue6810/indent-fail.hxml | 2 + .../Issue6810/indent-fail.hxml.stderr | 6 + .../projects/Issue6810/logfile-01-fail.hxml | 2 + .../projects/Issue6810/logfile-02-fail.hxml | 4 + .../projects/Issue6810/logfile-03-fail.hxml | 4 + .../projects/Issue6810/logfile-04-fail.hxml | 1 + .../misc/projects/Issue6810/pretty-fail.hxml | 3 + .../Issue6810/pretty-fail.hxml.stderr | 16 + .../Issue6903/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue6938/Main1.hx | 25 + .../projects/Issue6938/compile1-fail.hxml | 2 + .../Issue6938/compile1-fail.hxml.stderr | 4 + .../projects/Issue7096/compile.hxml.stderr | 2 +- tests/misc/projects/Issue7447/Main.hx | 1 + .../projects/Issue7447/compile.hxml.stderr | 4 +- .../Issue7614/compile-fail.hxml.stderr | 1 - .../Issue7638/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue7669/Main.hx | 12 + .../misc/projects/Issue7669/compile-fail.hxml | 2 + .../Issue7669/compile-fail.hxml.stderr | 5 + tests/misc/projects/Issue7871/Macro.hx | 19 + .../misc/projects/Issue7871/compile-fail.hxml | 1 + .../Issue7871/compile-fail.hxml.stderr | 5 + .../projects/Issue7871/compile-next-fail.hxml | 2 + .../Issue7871/compile-next-fail.hxml.stderr | 7 + .../Issue7935/compile3-fail.hxml.stderr | 2 +- .../misc/projects/Issue7968/pretty-fail.hxml | 3 + .../Issue7968/pretty-fail.hxml.stderr | 10 + .../Issue8173/compile-fail.hxml.stderr | 2 +- .../misc/projects/Issue8303/indent-fail.hxml | 2 + .../Issue8303/indent-fail.hxml.stderr | 21 + .../misc/projects/Issue8303/pretty-fail.hxml | 3 + .../Issue8303/pretty-fail.hxml.stderr | 54 + .../projects/Issue8471/compile2-pretty.hxml | 3 + .../Issue8471/compile2-pretty.hxml.stderr | 22 + .../projects/Issue8471/compile2.hxml.stderr | 2 +- .../Issue8517/compile-fail.hxml.stderr | 1 - .../Issue8551/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue8634/Main.hx | 6 + .../misc/projects/Issue8634/compile-fail.hxml | 2 + .../Issue8634/compile-fail.hxml.stderr | 1 + .../Issue8828/compile1-fail.hxml.stderr | 2 +- .../Issue9014/compile-fail.hxml.stderr | 4 +- .../Issue9017/compile-fail.hxml.stderr | 1 - tests/misc/projects/Issue9163/Main.hx | 12 + .../misc/projects/Issue9163/compile-fail.hxml | 2 + .../Issue9163/compile-fail.hxml.stderr | 1 + tests/misc/projects/Issue9171/Main.hx | 11 + .../misc/projects/Issue9171/compile-fail.hxml | 3 + .../Issue9171/compile-fail.hxml.stderr | 1 + .../projects/Issue9192/compile.hxml.stderr | 2 +- .../projects/Issue9204/compile.hxml.stderr | 2 +- .../Issue9378/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue9381/FinalDisplay.hx | 2 + .../projects/Issue9381/FinalDisplayBuilder.hx | 15 + tests/misc/projects/Issue9381/Main.hx | 4 + tests/misc/projects/Issue9381/compile.hxml | 1 + .../projects/Issue9381/compile.hxml.stderr | 2 + .../Issue9391/compile-fail.hxml.stderr | 2 +- .../Issue9425/compile-success.hxml.stderr | 2 +- tests/misc/projects/Issue9584/Main.hx | 8 + .../misc/projects/Issue9584/compile-fail.hxml | 1 + .../Issue9584/compile-fail.hxml.stderr | 3 + .../abstract-dynamic/compile-fail.hxml.stderr | 2 +- .../compile-fail.hxml.stderr | 2 +- .../abstract-final/compile-fail.hxml.stderr | 2 +- .../abstract-inline/compile-fail.hxml.stderr | 2 +- .../abstract-static/compile-fail.hxml.stderr | 2 +- .../Issue9619/abstract-structInit/Main.hx | 9 - .../compile-fail.hxml.stderr | 2 - .../Issue9640/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue9676/MainExpr.hx | 3 + tests/misc/projects/Issue9676/MainField.hx | 5 + tests/misc/projects/Issue9676/MainMacro.hx | 3 + .../projects/Issue9676/compile1-fail.hxml | 1 + .../Issue9676/compile1-fail.hxml.stderr | 1 + tests/misc/projects/Issue9676/compile1D.hxml | 2 + .../projects/Issue9676/compile1D.hxml.stderr | 1 + .../projects/Issue9676/compile2-fail.hxml | 1 + .../Issue9676/compile2-fail.hxml.stderr | 1 + tests/misc/projects/Issue9676/compile2D.hxml | 2 + .../projects/Issue9676/compile2D.hxml.stderr | 1 + .../projects/Issue9676/compile3-fail.hxml | 1 + .../Issue9676/compile3-fail.hxml.stderr | 1 + tests/misc/projects/Issue9676/compile3D.hxml | 2 + .../projects/Issue9676/compile3D.hxml.stderr | 1 + .../Issue9785/compile-fail.hxml.stderr | 2 +- tests/misc/projects/Issue9919/Main.hx | 7 + tests/misc/projects/Issue9919/compile.hxml | 3 + .../projects/Issue9919/compile.hxml.stderr | 0 .../misc/projects/default_type_params/Ctor.hx | 3 + .../projects/default_type_params/EnumCtor.hx | 3 + .../projects/default_type_params/Method.hx | 1 + .../compile-ctor-fail.hxml | 1 + .../compile-ctor-fail.hxml.stderr | 1 + .../compile-enum-ctor-fail.hxml | 1 + .../compile-enum-ctor-fail.hxml.stderr | 1 + .../compile-method-fail.hxml | 1 + .../compile-method-fail.hxml.stderr | 1 + tests/misc/projects/eventLoop/Main.hx | 54 + tests/misc/projects/eventLoop/compile.hxml | 3 + .../eventLoop/compile.hxml.stdout.disabled | 23 + .../not-not-extern/compile-fail.hxml.stderr | 2 +- .../compile-fail.hxml.stderr | 2 +- .../issue5002/compile-fail.hxml.stderr | 4 +- .../issue5002/compile2-fail.hxml.stderr | 25 +- .../issue5002/compile4-fail.hxml.stderr | 2 +- .../misc/projects/local-static/Break/Main.hx | 7 + .../local-static/Break/compile-fail.hxml | 2 + .../Break/compile-fail.hxml.stderr | 1 + .../projects/local-static/Continue/Main.hx | 7 + .../local-static/Continue/compile-fail.hxml | 2 + .../Continue/compile-fail.hxml.stderr | 1 + .../projects/local-static/Function/Main.hx | 6 + .../local-static/Function/compile-fail.hxml | 2 + .../Function/compile-fail.hxml.stderr | 1 + .../misc/projects/local-static/Local/Main.hx | 6 + .../local-static/Local/compile-fail.hxml | 2 + .../Local/compile-fail.hxml.stderr | 1 + .../local-static/LocalVsLocal/Main.hx | 6 + .../LocalVsLocal/compile-fail.hxml | 2 + .../LocalVsLocal/compile-fail.hxml.stderr | 2 + .../local-static/LocalVsStatic/Main.hx | 6 + .../LocalVsStatic/compile-fail.hxml | 2 + .../LocalVsStatic/compile-fail.hxml.stderr | 2 + .../misc/projects/local-static/Return/Main.hx | 5 + .../local-static/Return/compile-fail.hxml | 2 + .../Return/compile-fail.hxml.stderr | 1 + tests/misc/projects/local-static/This/Main.hx | 11 + .../local-static/This/compile-fail.hxml | 2 + .../This/compile-fail.hxml.stderr | 1 + .../misc/projects/recursive-callbacks/Main.hx | 30 + .../projects/recursive-callbacks/compile.hxml | 2 + .../recursive-callbacks/compile.hxml.stderr | 7 + tests/misc/projects/sys-exit/SysExit0.hx | 6 + tests/misc/projects/sys-exit/SysExit1.hx | 6 + tests/misc/projects/sys-exit/compile00.hxml | 7 + .../projects/sys-exit/compile00.hxml.stderr | 2 + .../projects/sys-exit/compile01-fail.hxml | 7 + .../sys-exit/compile01-fail.hxml.stderr | 2 + .../projects/sys-exit/compile10-fail.hxml | 7 + .../sys-exit/compile10-fail.hxml.stderr | 1 + .../projects/sys-exit/compile11-fail.hxml | 7 + .../sys-exit/compile11-fail.hxml.stderr | 1 + tests/misc/projects/trailing-commas/Main.hx | 43 + tests/misc/projects/trailing-commas/Main2.hx | 6 + tests/misc/projects/trailing-commas/Main3.hx | 6 + .../projects/trailing-commas/compile1.hxml | 2 + .../trailing-commas/compile1.hxml.stdout | 1 + .../trailing-commas/compile2-fail.hxml | 2 + .../trailing-commas/compile2-fail.hxml.stderr | 1 + .../trailing-commas/compile3-fail.hxml | 2 + .../trailing-commas/compile3-fail.hxml.stderr | 1 + tests/misc/python/projects/Issue11060/Main.hx | 3 + .../python/projects/Issue11060/build.hxml | 3 + .../projects/Issue11060/build.hxml.stdout | 1 + tests/misc/src/Main.hx | 69 +- tests/misc/src/diff/Analyze.hx | 132 ++ tests/misc/src/diff/Change.hx | 15 + tests/misc/src/diff/Context.hx | 8 + tests/misc/src/diff/Diffseq.hx | 176 +++ tests/misc/src/diff/FileData.hx | 73 + tests/misc/src/diff/IndexVector.hx | 42 + tests/misc/src/diff/Io.hx | 159 +++ tests/misc/src/diff/Printer.hx | 122 ++ tests/nullsafety/src/cases/TestStrict.hx | 33 +- tests/optimization/run.hxml | 1 + tests/optimization/src/TestJs.hx | 124 +- tests/optimization/src/TestNadako.hx | 18 + tests/optimization/src/issues/Issue10188.hx | 64 + tests/optimization/src/issues/Issue10765.hx | 52 + tests/optimization/src/issues/Issue7237.hx | 26 + .../src/issues/Issue7765.hx.disabled | 47 + tests/optimization/src/issues/Issue9943.hx | 55 + tests/runci/Config.hx | 27 +- tests/runci/Linux.hx | 31 +- tests/runci/System.hx | 205 ++- tests/runci/TestTarget.hx | 3 +- tests/runci/targets/Cpp.hx | 50 +- tests/runci/targets/Cs.hx | 21 +- tests/runci/targets/Flash.hx | 337 +++-- tests/runci/targets/Hl.hx | 167 ++- tests/runci/targets/Java.hx | 33 +- tests/runci/targets/Js.hx | 51 +- tests/runci/targets/Jvm.hx | 9 +- tests/runci/targets/Lua.hx | 45 +- tests/runci/targets/Macro.hx | 25 +- tests/runci/targets/Neko.hx | 8 +- tests/runci/targets/Php.hx | 76 +- tests/runci/targets/Python.hx | 28 +- tests/server/.vscode/settings.json | 5 +- tests/server/.vscode/tasks.json | 2 +- tests/server/build.hxml | 2 +- tests/server/src/SkipReason.hx | 8 + tests/server/src/TestCase.hx | 64 +- tests/server/src/cases/RetyperTests.hx | 203 +++ tests/server/src/cases/ServerTests.hx | 130 +- .../src/cases/display/issues/Issue10414.hx | 138 +- .../src/cases/display/issues/Issue10635.hx | 83 ++ .../src/cases/display/issues/Issue10638.hx | 21 + .../src/cases/display/issues/Issue10678.hx | 17 + .../src/cases/display/issues/Issue10781.hx | 48 + .../src/cases/display/issues/Issue9140.hx | 61 + .../src/cases/display/issues/Issue9318.hx | 39 + .../src/cases/display/issues/Issue9423.hx | 12 +- tests/server/src/cases/issues/Issue10646.hx | 21 + tests/server/src/cases/issues/Issue10653.hx | 16 + tests/server/src/cases/issues/Issue10677.hx | 15 + tests/server/src/cases/issues/Issue8748.hx | 6 +- tests/server/src/cases/issues/Issue9636.hx | 15 + .../src/utils/macro/TestBuilder.macro.hx | 24 +- tests/server/test/templates/Dependency.hx | 2 + .../test/templates/GenericInstanceMethod.hx | 5 + tests/server/test/templates/GenericMethod.hx | 3 + .../templates/issues/Issue10635/MainAfter.hx | 6 + .../templates/issues/Issue10635/MainBefore.hx | 5 + .../issues/Issue10635/MainInstanceAfter.hx | 6 + .../issues/Issue10635/MainInstanceBefore.hx | 5 + .../templates/issues/Issue10635/Something.hx | 9 + .../templates/issues/Issue10653/MainAfter.hx | 8 + .../templates/issues/Issue10653/MainBefore.hx | 7 + .../test/templates/issues/Issue10653/Test.hx | 5 + .../test/templates/issues/Issue10677/Main.hx | 6 + .../templates/issues/Issue10677/Polygon.hx | 3 + .../issues/Issue10677/PolygonCollider.hx | 3 + .../templates/issues/Issue10677/Polygons.hx | 4 + .../test/templates/issues/Issue10678/Macro.hx | 14 + .../test/templates/issues/Issue10986/Main.hx | 7 + .../templates/issues/Issue10986/Vector.hx | 43 + .../templates/issues/Issue10986/build.hxml | 3 + .../test/templates/issues/Issue8368/Main2.hx | 11 + .../issues/Issue8368/MyMacro2.macro.hx | 16 + .../test/templates/issues/Issue9636/Foo.hx | 6 + .../test/templates/issues/Issue9636/Main.hx | 6 + .../retyper/AbstractWithDependency.hx | 5 + .../AbstractWithSignatureDependency.hx | 3 + .../test/templates/retyper/DependentEnum.hx | 3 + .../templates/retyper/DependentTypedef.hx | 1 + .../test/templates/retyper/IndependentEnum.hx | 9 + .../templates/retyper/IndependentTypedef.hx | 7 + .../templates/retyper/InterfaceDependency.hx | 1 + .../templates/retyper/MutualDependency.hx | 5 + .../retyper/WithInterfaceDependency.hx | 1 + .../templates/retyper/WithMutualDependency.hx | 7 + .../templates/retyper/WithParentDependency.hx | 1 + .../retyper/WithSignatureDependency.hx | 3 + .../WithSignatureDependencyInferredArg.hx | 5 + ...WithSignatureDependencyInferredProperty.hx | 3 + .../WithSignatureDependencyInferredRet.hx | 5 + ...WithSignatureDependencyInferredVariable.hx | 3 + .../WithSignatureDependencyProperty.hx | 3 + .../WithSignatureDependencyVariable.hx | 3 + tests/sys/compile-js.hxml | 22 + tests/sys/compile.hxml | 2 + tests/sys/genTestRes.py | 98 -- tests/sys/gen_test_res.py | 101 ++ tests/sys/run.hxml | 43 +- tests/sys/src/ExitCode.hx | 2 + tests/sys/src/Main.hx | 22 +- tests/sys/src/TestArguments.hx | 2 + tests/sys/src/TestCommandBase.hx | 4 + tests/sys/src/TestFileSystem.hx | 5 +- tests/sys/src/TestSys.hx | 105 +- tests/sys/src/TestUnicode.hx | 43 +- tests/sys/src/UtilityProcess.hx | 72 +- tests/sys/src/net/TestSocket.hx | 4 +- tests/threads/build.hxml | 4 +- tests/threads/src/cases/TestAtomics.hx | 23 + tests/threads/src/cases/TestCondition.hx | 23 + tests/threads/src/cases/TestEvents.hx | 6 +- tests/threads/src/cases/TestSemaphore.hx | 21 + tests/unit/.vscode/settings.json | 3 +- tests/unit/compile-each.hxml | 5 +- ...compile-flash9.hxml => compile-flash.hxml} | 5 +- tests/unit/compile-hl.hxml | 2 +- tests/unit/compile-python.hxml | 11 +- tests/unit/compile.hxml | 4 +- tests/unit/native_java/hxjava_build.txt | 4 + .../src/haxe/test/MyDefaultInterface.java | 12 + .../src/haxe/test/MyDefaultSubInterface.java | 7 + tests/unit/src/unit/HelperMacros.hx | 7 +- tests/unit/src/unit/TestArrowFunctions.hx | 2 +- tests/unit/src/unit/TestBasetypes.hx | 12 + .../src/unit/TestDefaultTypeParameters.hx | 44 + tests/unit/src/unit/TestExceptions.hx | 4 + tests/unit/src/unit/TestGADT.hx | 34 +- tests/unit/src/unit/TestGeneric.hx | 19 +- tests/unit/src/unit/TestHttp.hx | 2 +- tests/unit/src/unit/TestInt64.hx | 521 ++++--- tests/unit/src/unit/TestJava.hx | 227 ++- tests/unit/src/unit/TestLocalStatic.hx | 20 + tests/unit/src/unit/TestMacro.hx | 1 + tests/unit/src/unit/TestMain.hx | 43 +- tests/unit/src/unit/TestMatch.hx | 6 +- tests/unit/src/unit/TestMisc.hx | 1 + tests/unit/src/unit/TestNullCoalescing.hx | 124 ++ tests/unit/src/unit/TestNumericSeparator.hx | 75 + tests/unit/src/unit/TestNumericSuffixes.hx | 29 + tests/unit/src/unit/TestRest.hx | 34 +- tests/unit/src/unit/UnitBuilder.hx | 108 +- tests/unit/src/unit/issues/Issue10066.hx | 24 + tests/unit/src/unit/issues/Issue10073.hx | 51 +- tests/unit/src/unit/issues/Issue10124.hx | 4 +- tests/unit/src/unit/issues/Issue10127.hx | 15 + tests/unit/src/unit/issues/Issue10135.hx | 18 + tests/unit/src/unit/issues/Issue10139.hx | 19 + tests/unit/src/unit/issues/Issue10148.hx | 17 + tests/unit/src/unit/issues/Issue10205.hx | 16 + tests/unit/src/unit/issues/Issue10209.hx | 13 + tests/unit/src/unit/issues/Issue10291.hx | 25 + tests/unit/src/unit/issues/Issue10304.hx | 35 + tests/unit/src/unit/issues/Issue10336.hx | 54 + tests/unit/src/unit/issues/Issue10383.hx | 15 + tests/unit/src/unit/issues/Issue10397.hx | 159 +++ tests/unit/src/unit/issues/Issue10410.hx | 12 + tests/unit/src/unit/issues/Issue10455.hx | 8 + tests/unit/src/unit/issues/Issue10468.hx | 10 + tests/unit/src/unit/issues/Issue10482.hx | 86 ++ tests/unit/src/unit/issues/Issue10508.hx | 10 + tests/unit/src/unit/issues/Issue10535.hx | 13 + tests/unit/src/unit/issues/Issue10537.hx | 40 + tests/unit/src/unit/issues/Issue10541.hx | 14 + tests/unit/src/unit/issues/Issue10550.hx | 32 + tests/unit/src/unit/issues/Issue10561.hx | 11 + tests/unit/src/unit/issues/Issue10565.hx | 55 + tests/unit/src/unit/issues/Issue10568.hx | 56 + tests/unit/src/unit/issues/Issue10571.hx | 24 + tests/unit/src/unit/issues/Issue10600.hx | 12 + tests/unit/src/unit/issues/Issue10604.hx | 18 + tests/unit/src/unit/issues/Issue10618.hx | 20 + tests/unit/src/unit/issues/Issue10624.hx | 17 + tests/unit/src/unit/issues/Issue10626.hx | 17 + tests/unit/src/unit/issues/Issue10641.hx | 40 + tests/unit/src/unit/issues/Issue10684.hx | 11 + tests/unit/src/unit/issues/Issue10685.hx | 19 + tests/unit/src/unit/issues/Issue10709.hx | 11 + tests/unit/src/unit/issues/Issue10712.hx | 47 + tests/unit/src/unit/issues/Issue10716.hx | 21 + tests/unit/src/unit/issues/Issue10720.hx | 19 + tests/unit/src/unit/issues/Issue10728.hx | 21 + tests/unit/src/unit/issues/Issue10730.hx | 18 + tests/unit/src/unit/issues/Issue10735.hx | 24 + tests/unit/src/unit/issues/Issue10738.hx | 19 + tests/unit/src/unit/issues/Issue10740.hx | 77 + tests/unit/src/unit/issues/Issue10743.hx | 14 + tests/unit/src/unit/issues/Issue10744.hx | 179 +++ tests/unit/src/unit/issues/Issue10746.hx | 13 + tests/unit/src/unit/issues/Issue10748.hx | 21 + tests/unit/src/unit/issues/Issue10752.hx | 16 + tests/unit/src/unit/issues/Issue10754.hx | 9 + tests/unit/src/unit/issues/Issue10761.hx | 17 + tests/unit/src/unit/issues/Issue10776.hx | 35 + tests/unit/src/unit/issues/Issue10799.hx | 79 ++ .../src/unit/issues/Issue10821.hx.disabled | 15 + tests/unit/src/unit/issues/Issue10830.hx | 103 ++ tests/unit/src/unit/issues/Issue10831.hx | 25 + tests/unit/src/unit/issues/Issue10834.hx | 12 + tests/unit/src/unit/issues/Issue10836.hx | 21 + tests/unit/src/unit/issues/Issue10845.hx | 47 + tests/unit/src/unit/issues/Issue10867.hx | 19 + tests/unit/src/unit/issues/Issue10876.hx | 41 + tests/unit/src/unit/issues/Issue10906.hx | 20 + tests/unit/src/unit/issues/Issue10960.hx | 12 + tests/unit/src/unit/issues/Issue10965.hx | 39 + tests/unit/src/unit/issues/Issue10993.hx | 17 + tests/unit/src/unit/issues/Issue11007.hx | 8 + tests/unit/src/unit/issues/Issue11040.hx | 50 + tests/unit/src/unit/issues/Issue2776.hx | 13 +- tests/unit/src/unit/issues/Issue2778.hx | 4 +- tests/unit/src/unit/issues/Issue3396.hx | 10 +- tests/unit/src/unit/issues/Issue3861.hx | 3 +- tests/unit/src/unit/issues/Issue4327.hx | 1 + tests/unit/src/unit/issues/Issue4578.hx | 24 +- tests/unit/src/unit/issues/Issue5078.hx | 4 +- tests/unit/src/unit/issues/Issue5890.hx | 53 + tests/unit/src/unit/issues/Issue6017.hx | 70 + tests/unit/src/unit/issues/Issue6063.hx | 35 + tests/unit/src/unit/issues/Issue6457.hx | 8 +- tests/unit/src/unit/issues/Issue6561.hx | 10 +- tests/unit/src/unit/issues/Issue7736.hx | 21 + tests/unit/src/unit/issues/Issue8218.hx | 11 - tests/unit/src/unit/issues/Issue8401.hx | 50 + tests/unit/src/unit/issues/Issue8869.hx | 6 +- tests/unit/src/unit/issues/Issue9034.hx | 2 +- tests/unit/src/unit/issues/Issue9198.hx | 9 + tests/unit/src/unit/issues/Issue9543.hx | 36 + tests/unit/src/unit/issues/Issue9563.hx | 17 + tests/unit/src/unitstd/EReg.unit.hx | 2 +- tests/unit/src/unitstd/Std.unit.hx | 115 +- tests/unit/src/unitstd/haxe/EnumFlags.unit.hx | 10 + .../unitstd/haxe/atomic/AtomicBool.unit.hx | 18 + .../src/unitstd/haxe/atomic/AtomicInt.unit.hx | 33 + .../unitstd/haxe/atomic/AtomicObject.unit.hx | 15 + .../src/unitstd/haxe/crypto/Base64.unit.hx | 45 + tests/unit/src/unitstd/haxe/ds/Vector.unit.hx | 14 + .../src/unitstd/haxe/macro/ExprTools.unit.hx | 46 +- tests/unit/src/unitstd/sys/net/Socket.unit.hx | 6 - tests/unit/unit.html | 10 +- 1307 files changed, 33675 insertions(+), 11685 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100644 .devcontainer/library-scripts/common-debian.sh create mode 100644 .devcontainer/library-scripts/docker-debian.sh create mode 100644 .earthlyignore create mode 100644 .github/workflows/cancel.yml create mode 100644 Earthfile delete mode 100644 extra/github-actions/build-linux.yml create mode 100644 extra/github-actions/cache-opam.yml rename extra/github-actions/{install-neko.yml => install-neko-unix.yml} (51%) create mode 100644 extra/github-actions/install-neko-windows.yml create mode 100644 extra/github-actions/install-nsis.yml create mode 100644 extra/github-actions/install-ocaml-libs-windows.yml create mode 100644 extra/github-actions/install-ocaml-windows.yml create mode 100644 extra/github-actions/install-ocaml-windows64.yml delete mode 100644 extra/github-actions/test-linux.yml create mode 100644 extra/haxelib_src/test/libraries/libBadDefineJson/doc/defines.json create mode 100644 extra/haxelib_src/test/libraries/libBadDefineJson/haxelib.json create mode 100644 extra/haxelib_src/test/libraries/libBadMetaJson/doc/meta.json create mode 100644 extra/haxelib_src/test/libraries/libBadMetaJson/haxelib.json create mode 100644 extra/haxelib_src/test/libraries/libBadMetaJson2/doc/meta.json create mode 100644 extra/haxelib_src/test/libraries/libBadMetaJson2/haxelib.json create mode 100644 extra/haxelib_src/test/libraries/libDocumentationFiles/doc/defines.json create mode 100644 extra/haxelib_src/test/libraries/libDocumentationFiles/doc/meta.json create mode 100644 extra/haxelib_src/test/libraries/libDocumentationFiles/haxelib.json create mode 100644 extra/haxelib_src/test/libraries/libMissingMetaJson/haxelib.json rename libs/objsize/{alloc.c => alloc.h} (100%) rename libs/objsize/{bitarray.c => bitarray.h} (98%) delete mode 100644 libs/pcre/Makefile delete mode 100644 libs/pcre/dune delete mode 100644 libs/pcre/pcre_stubs.c create mode 100644 libs/pcre2/Makefile create mode 100644 libs/pcre2/dune rename libs/{pcre/pcre.ml => pcre2/pcre2.ml} (70%) create mode 100644 libs/pcre2/pcre2_stubs.c create mode 100644 src-json/warning.json create mode 100644 src/compiler/args.ml rename src/{context/compilationServer.ml => compiler/compilationCache.ml} (95%) create mode 100644 src/compiler/compilationContext.ml create mode 100644 src/compiler/compiler.ml create mode 100644 src/compiler/displayProcessing.ml create mode 100644 src/compiler/displayProcessingGlobals.ml create mode 100644 src/compiler/generate.ml create mode 100644 src/compiler/helper.ml create mode 100644 src/compiler/process_helper.ml create mode 100644 src/compiler/retyper.ml create mode 100644 src/compiler/serverCompilationContext.ml create mode 100644 src/compiler/tasks.ml create mode 100644 src/core/warning.ml create mode 100644 src/typing/strictMeta.ml create mode 100644 std/cpp/Int64Map.hx create mode 100644 std/cpp/_std/haxe/atomic/AtomicInt.hx create mode 100644 std/cpp/_std/haxe/ds/Map.hx create mode 100644 std/cpp/_std/sys/thread/Condition.hx create mode 100644 std/cpp/_std/sys/thread/Semaphore.hx create mode 100644 std/cs/_std/haxe/atomic/AtomicInt.hx create mode 100644 std/cs/_std/haxe/atomic/AtomicObject.hx create mode 100644 std/cs/_std/sys/thread/Condition.hx create mode 100644 std/cs/_std/sys/thread/Semaphore.hx create mode 100644 std/eval/_std/sys/thread/Condition.hx create mode 100644 std/eval/_std/sys/thread/Semaphore.hx create mode 100644 std/haxe/atomic/AtomicBool.hx create mode 100644 std/haxe/atomic/AtomicInt.hx create mode 100644 std/haxe/atomic/AtomicObject.hx create mode 100644 std/haxe/macro/DisplayMode.hx create mode 100644 std/haxe/macro/PlatformConfig.hx create mode 100644 std/hl/Atomics.hx create mode 100644 std/hl/CArray.hx create mode 100644 std/hl/_std/haxe/Int64.hx create mode 100644 std/hl/_std/haxe/atomic/AtomicInt.hx create mode 100644 std/hl/_std/haxe/atomic/AtomicObject.hx create mode 100644 std/hl/_std/sys/thread/Condition.hx create mode 100644 std/hl/_std/sys/thread/Semaphore.hx create mode 100644 std/hl/types/Int64Map.hx create mode 100644 std/hl/uv/Fs.hx create mode 100644 std/java/_std/haxe/atomic/AtomicBool.hx create mode 100644 std/java/_std/haxe/atomic/AtomicInt.hx create mode 100644 std/java/_std/haxe/atomic/AtomicObject.hx create mode 100644 std/java/_std/sys/thread/Condition.hx create mode 100644 std/java/_std/sys/thread/Semaphore.hx create mode 100644 std/js/_std/haxe/atomic/AtomicInt.hx create mode 100644 std/js/html/BeforeInstallPromptEvent.hx create mode 100644 std/js/html/DialogElement.hx create mode 100644 std/js/html/FullscreenOptions.hx create mode 100644 std/js/lib/Atomics.hx create mode 100644 std/js/lib/SharedArrayBuffer.hx create mode 100644 std/js/lib/WeakRef.hx create mode 100644 std/js/lib/intl/DisplayNames.hx create mode 100644 std/js/lib/intl/ListFormat.hx create mode 100644 std/jvm/_std/haxe/Rest.hx create mode 100644 std/lua/_lua/_hx_handle_error.lua create mode 100644 std/lua/_lua/_hx_luv.lua create mode 100644 std/lua/_std/sys/net/SocketInput.hx create mode 100644 std/lua/_std/sys/net/SocketOutput.hx create mode 100644 std/lua/_std/sys/ssl/Socket.hx create mode 100644 std/lua/lib/luasec/Ssl.hx create mode 100644 std/lua/lib/luasec/SslTcpClient.hx create mode 100644 std/php/DirectoryIterator.hx create mode 100644 std/php/FilesystemIterator.hx create mode 100644 std/php/Phar.hx create mode 100644 std/php/PharData.hx create mode 100644 std/php/PharException.hx create mode 100644 std/php/PharFileInfo.hx create mode 100644 std/php/RecursiveDirectoryIterator.hx create mode 100644 std/php/RecursiveIterator.hx create mode 100644 std/php/ResourceBundle.hx create mode 100644 std/php/SplFileInfo.hx create mode 100644 std/php/SplFileObject.hx create mode 100644 std/python/_std/sys/thread/Condition.hx create mode 100644 std/python/_std/sys/thread/Semaphore.hx create mode 100644 std/python/lib/threading/Condition.hx create mode 100644 std/python/lib/threading/Semaphore.hx create mode 100644 std/sys/thread/Condition.hx create mode 100644 std/sys/thread/Semaphore.hx create mode 100644 tests/PHP.ini create mode 100644 tests/display/src/cases/Issue10004.hx create mode 100644 tests/display/src/cases/Issue10194.hx create mode 100644 tests/display/src/cases/Issue10412.hx create mode 100644 tests/display/src/cases/Issue10429.hx create mode 100644 tests/display/src/cases/Issue10633.hx create mode 100644 tests/display/src/cases/Issue10634.hx create mode 100644 tests/display/src/cases/Issue10691.hx create mode 100644 tests/display/src/cases/Issue10704.hx create mode 100644 tests/display/src/cases/Issue10713.hx rename tests/display/src/cases/{Issue5172.hx.disabled => Issue5172.hx} (100%) create mode 100644 tests/display/src/misc/issue10704/Statics.hx create mode 100644 tests/docgen/dox.hxml create mode 100644 tests/misc/cpp/projects/Issue10184/CheckOptionsTxt.hx create mode 100644 tests/misc/cpp/projects/Issue10184/options-txt.hxml create mode 100644 tests/misc/cpp/run.hxml create mode 100644 tests/misc/eventLoop/build-cpp.hxml create mode 100644 tests/misc/eventLoop/build-cpp.hxml.stdout.disabled create mode 100644 tests/misc/eventLoop/build-hl.hxml create mode 100644 tests/misc/eventLoop/build-hl.hxml.stdout.disabled create mode 100644 tests/misc/hl/projects/Issue10184/CheckJson.hx create mode 100644 tests/misc/hl/projects/Issue10184/hlc-json.hxml create mode 100644 tests/misc/hl/projects/Issue10376/CheckSourceHeader.hx create mode 100644 tests/misc/hl/projects/Issue10376/custom-header.hxml create mode 100644 tests/misc/hl/projects/Issue10376/default-header.hxml create mode 100644 tests/misc/hl/projects/Issue10376/no-header-dash.hxml create mode 100644 tests/misc/hl/run.hxml delete mode 100644 tests/misc/java/projects/Issue10280/jvm.jar create mode 100644 tests/misc/java/projects/Issue10517/Main.hx create mode 100644 tests/misc/java/projects/Issue10517/compile-fail.hxml create mode 100644 tests/misc/java/projects/Issue10517/compile-fail.hxml.stderr create mode 100644 tests/misc/java/projects/Issue11014/Main.hx create mode 100644 tests/misc/java/projects/Issue11014/compile.hxml create mode 100644 tests/misc/java/projects/Issue11014/compile.hxml.stdout create mode 100644 tests/misc/java/projects/Issue11054/Main.hx create mode 100644 tests/misc/java/projects/Issue11054/Setup.hx create mode 100644 tests/misc/java/projects/Issue11054/compile.hxml create mode 100644 tests/misc/java/projects/Issue11054/compile.hxml.stdout create mode 100644 tests/misc/java/projects/Issue11054/pack/ItemGroupEvents.java create mode 100644 tests/misc/js/projects/Issue10184/CheckSourceHeader.hx create mode 100644 tests/misc/js/projects/Issue10184/Main.hx create mode 100644 tests/misc/js/projects/Issue10184/Tools.hx create mode 100644 tests/misc/js/projects/Issue10184/custom-header-dash.hxml create mode 100644 tests/misc/js/projects/Issue10184/custom-header-underscore.hxml create mode 100644 tests/misc/js/projects/Issue10184/default-header.hxml create mode 100644 tests/misc/js/projects/Issue10184/no-header-dash.hxml create mode 100644 tests/misc/js/projects/Issue10184/no-header-underscore.hxml create mode 100644 tests/misc/js/run.hxml create mode 100644 tests/misc/lua/projects/Issue10979/HaxeException.hx create mode 100644 tests/misc/lua/projects/Issue10979/NativeError.hx create mode 100644 tests/misc/lua/projects/Issue10979/RunScript.hx create mode 100644 tests/misc/lua/projects/Issue10979/embedded-native.hxml create mode 100644 tests/misc/lua/projects/Issue10979/embedded-native.hxml.stdout create mode 100644 tests/misc/lua/projects/Issue10979/embedded.hxml create mode 100644 tests/misc/lua/projects/Issue10979/embedded.hxml.stdout create mode 100644 tests/misc/lua/projects/Issue10979/runner.lua create mode 100644 tests/misc/lua/projects/Issue10979/standalone-native.hxml create mode 100644 tests/misc/lua/projects/Issue10979/standalone-native.hxml.stdout create mode 100644 tests/misc/lua/projects/Issue10979/standalone.hxml create mode 100644 tests/misc/lua/projects/Issue10979/standalone.hxml.stdout create mode 100644 tests/misc/lua/projects/Issue9530/Main.hx create mode 100644 tests/misc/lua/projects/Issue9530/compile.hxml create mode 100644 tests/misc/lua/projects/Issue9530/compile.hxml.stdout create mode 100644 tests/misc/neko/projects/Issue10937/Main.hx create mode 100644 tests/misc/neko/projects/Issue10937/aaa-setup.hxml create mode 100644 tests/misc/neko/projects/Issue10937/compile.hxml create mode 100644 tests/misc/neko/projects/Issue10937/compile.hxml.stdout create mode 100644 tests/misc/neko/projects/Issue10937/dummy_ndll/ndll/.exists create mode 100644 tests/misc/neko/run.hxml create mode 100644 tests/misc/php/projects/Issue10736/compile.hxml create mode 100644 tests/misc/php/projects/Issue10736/src/App1.hx create mode 100644 tests/misc/php/projects/Issue10736/src/App2.hx create mode 100644 tests/misc/php/projects/Issue10820/Foo.hx create mode 100644 tests/misc/php/projects/Issue10820/Main.hx create mode 100644 tests/misc/php/projects/Issue10820/compile.hxml create mode 100644 tests/misc/php/projects/Issue10820/pack/Foo.hx create mode 100644 tests/misc/projects/Issue10034/Main.hx rename tests/misc/projects/{Issue10205 => Issue10034}/compile-fail.hxml (100%) create mode 100644 tests/misc/projects/Issue10034/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10107/Main.hx create mode 100644 tests/misc/projects/Issue10107/compile.hxml create mode 100644 tests/misc/projects/Issue10139/Main.hx create mode 100644 tests/misc/projects/Issue10139/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10139/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10147/Main.hx create mode 100644 tests/misc/projects/Issue10147/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10147/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10152/Main.hx create mode 100644 tests/misc/projects/Issue10152/compile-defined.hxml create mode 100644 tests/misc/projects/Issue10152/compile-defined.hxml.stdout rename tests/misc/projects/{Issue9619/abstract-structInit/compile-fail.hxml => Issue10152/compile-undefined.hxml} (100%) create mode 100644 tests/misc/projects/Issue10152/compile-undefined.hxml.stdout create mode 100644 tests/misc/projects/Issue10170/Main.hx create mode 100644 tests/misc/projects/Issue10170/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10170/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10171/Main.hx create mode 100644 tests/misc/projects/Issue10171/compile.hxml create mode 100644 tests/misc/projects/Issue10171/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10184/BuiltIn.hx create mode 100644 tests/misc/projects/Issue10184/Deprecated.hx create mode 100644 tests/misc/projects/Issue10184/Flags.hx create mode 100644 tests/misc/projects/Issue10184/GetDefines.hx create mode 100644 tests/misc/projects/Issue10184/IfBlocks.hx create mode 100644 tests/misc/projects/Issue10184/Repeated.hx create mode 100644 tests/misc/projects/Issue10184/builtin.hxml create mode 100644 tests/misc/projects/Issue10184/check-warning.hxml create mode 100644 tests/misc/projects/Issue10184/check-warning.hxml.stderr create mode 100644 tests/misc/projects/Issue10184/cli.hxml create mode 100644 tests/misc/projects/Issue10184/get-defines.hxml create mode 100644 tests/misc/projects/Issue10184/if-blocks.hxml create mode 100644 tests/misc/projects/Issue10184/macro.hxml create mode 100644 tests/misc/projects/Issue10184/no-warning-underscore.hxml create mode 100644 tests/misc/projects/Issue10184/no-warning-underscore.hxml.stderr create mode 100644 tests/misc/projects/Issue10184/no-warning.hxml create mode 100644 tests/misc/projects/Issue10184/no-warning.hxml.stderr create mode 100644 tests/misc/projects/Issue10184/repeated.hxml create mode 100644 tests/misc/projects/Issue10186/Main.hx create mode 100644 tests/misc/projects/Issue10186/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10186/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10189/Main.hx create mode 100644 tests/misc/projects/Issue10189/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10189/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10198/Main.hx create mode 100644 tests/misc/projects/Issue10198/Main2.hx create mode 100644 tests/misc/projects/Issue10198/Main3.hx create mode 100644 tests/misc/projects/Issue10198/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10198/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10198/compile2-fail.hxml create mode 100644 tests/misc/projects/Issue10198/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10198/compile3-fail.hxml create mode 100644 tests/misc/projects/Issue10198/compile3-fail.hxml.stderr delete mode 100644 tests/misc/projects/Issue10205/Main.hx delete mode 100644 tests/misc/projects/Issue10205/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10219/Main.hx create mode 100644 tests/misc/projects/Issue10219/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10219/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10229/Main.hx create mode 100644 tests/misc/projects/Issue10229/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10229/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10245/Main.hx create mode 100644 tests/misc/projects/Issue10245/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10245/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10264/Main.hx create mode 100644 tests/misc/projects/Issue10264/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10264/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10291/Main.hx create mode 100644 tests/misc/projects/Issue10291/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10291/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10358/Main.hx create mode 100644 tests/misc/projects/Issue10358/Main2.hx create mode 100644 tests/misc/projects/Issue10358/Main3.hx create mode 100644 tests/misc/projects/Issue10358/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10358/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10358/compile2-fail.hxml create mode 100644 tests/misc/projects/Issue10358/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10358/compile3-fail.hxml create mode 100644 tests/misc/projects/Issue10358/compile3-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10426/Main.hx create mode 100644 tests/misc/projects/Issue10426/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10426/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10482/Main.hx create mode 100644 tests/misc/projects/Issue10482/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10482/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10504/Main.hx create mode 100644 tests/misc/projects/Issue10504/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10504/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10521/Main.hx create mode 100644 tests/misc/projects/Issue10521/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10521/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10587/Macro.hx create mode 100644 tests/misc/projects/Issue10587/MainImport.hx create mode 100644 tests/misc/projects/Issue10587/MainUsing.hx create mode 100644 tests/misc/projects/Issue10587/compile-import-fail.hxml create mode 100644 tests/misc/projects/Issue10587/compile-import-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10587/compile-using-fail.hxml create mode 100644 tests/misc/projects/Issue10587/compile-using-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10591/Main.hx create mode 100644 tests/misc/projects/Issue10591/compile.hxml create mode 100644 tests/misc/projects/Issue10591/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10623/Main.hx create mode 100644 tests/misc/projects/Issue10623/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10623/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10623/indent-fail.hxml create mode 100644 tests/misc/projects/Issue10623/indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10623/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue10623/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10687/Main.hx create mode 100644 tests/misc/projects/Issue10687/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10687/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10711/Main.hx create mode 100644 tests/misc/projects/Issue10711/compile.hxml create mode 100644 tests/misc/projects/Issue10711/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10711/lib1/X.hx create mode 100644 tests/misc/projects/Issue10711/lib2/X.hx create mode 100644 tests/misc/projects/Issue10729/Main.hx create mode 100644 tests/misc/projects/Issue10729/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10729/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10739/Main.hx create mode 100644 tests/misc/projects/Issue10739/compile.hxml create mode 100644 tests/misc/projects/Issue10739/compile.hxml.stdout create mode 100644 tests/misc/projects/Issue10793/Main.hx create mode 100644 tests/misc/projects/Issue10793/compile.hxml create mode 100644 tests/misc/projects/Issue10793/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10804/Main.hx create mode 100644 tests/misc/projects/Issue10804/Main2.hx create mode 100644 tests/misc/projects/Issue10804/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10804/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10804/compile2-fail.hxml create mode 100644 tests/misc/projects/Issue10804/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10819/Main.hx create mode 100644 tests/misc/projects/Issue10819/compile.hxml create mode 100644 tests/misc/projects/Issue10819/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10825/Main.hx create mode 100644 tests/misc/projects/Issue10825/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10825/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10832/Main.hx create mode 100644 tests/misc/projects/Issue10832/compile.hxml create mode 100644 tests/misc/projects/Issue10832/compile.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/Fail.hx create mode 100644 tests/misc/projects/Issue10844/aaa-setup.hxml create mode 100644 tests/misc/projects/Issue10844/base-fail.hxml create mode 100644 tests/misc/projects/Issue10844/custom-define-nofail.hxml create mode 100644 tests/misc/projects/Issue10844/custom-define-nofail.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/custom-define.hxml create mode 100644 tests/misc/projects/Issue10844/custom-define.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/custom-meta-nofail.hxml create mode 100644 tests/misc/projects/Issue10844/custom-meta-nofail.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/custom-meta.hxml create mode 100644 tests/misc/projects/Issue10844/custom-meta.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/define.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc/doc/define.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc/doc/meta.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc/haxelib.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc_dep/doc/define.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc_dep/doc/meta.json create mode 100644 tests/misc/projects/Issue10844/dummy_doc_dep/haxelib.json create mode 100644 tests/misc/projects/Issue10844/meta.json create mode 100644 tests/misc/projects/Issue10844/user-defined-define-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-define-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-defines.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-defines.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10844/user-defined-metas.hxml create mode 100644 tests/misc/projects/Issue10844/user-defined-metas.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/user-defines-did-something.hxml create mode 100644 tests/misc/projects/Issue10844/user-defines-did-something.hxml.stdout create mode 100644 tests/misc/projects/Issue10844/user-metas-did-something.hxml create mode 100644 tests/misc/projects/Issue10844/user-metas-did-something.hxml.stdout create mode 100644 tests/misc/projects/Issue10845/Main.hx create mode 100644 tests/misc/projects/Issue10845/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10845/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10847/bad/Macro.hx create mode 100644 tests/misc/projects/Issue10847/bad/Main.hx create mode 100644 tests/misc/projects/Issue10847/bad/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10847/bad/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10847/good/Macro.hx create mode 100644 tests/misc/projects/Issue10847/good/Main.hx create mode 100644 tests/misc/projects/Issue10847/good/compile.hxml create mode 100644 tests/misc/projects/Issue10852/Main.hx create mode 100644 tests/misc/projects/Issue10852/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10852/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10863/Main.js.hx create mode 100644 tests/misc/projects/Issue10863/Main.macro.hx create mode 100644 tests/misc/projects/Issue10863/compile.hxml create mode 100644 tests/misc/projects/Issue10863/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue10863/logmode-fail.hxml create mode 100644 tests/misc/projects/Issue10863/logmode-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10863/reporting-fail.hxml create mode 100644 tests/misc/projects/Issue10863/reporting-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10865/Main.hx create mode 100644 tests/misc/projects/Issue10865/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10865/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10871/Compiler/Main.hx create mode 100644 tests/misc/projects/Issue10871/Compiler/MainFail.hx create mode 100644 tests/misc/projects/Issue10871/Compiler/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10871/Compiler/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10871/Compiler/compile1.hxml create mode 100644 tests/misc/projects/Issue10871/Compiler/compile1.hxml.stdout create mode 100644 tests/misc/projects/Issue10871/Compiler/compile2.hxml create mode 100644 tests/misc/projects/Issue10871/Compiler/compile2.hxml.stdout create mode 100644 tests/misc/projects/Issue10871/Compiler/compile3.hxml create mode 100644 tests/misc/projects/Issue10871/Compiler/compile3.hxml.stdout create mode 100644 tests/misc/projects/Issue10871/Context/Main.hx create mode 100644 tests/misc/projects/Issue10871/Context/compile.hxml create mode 100644 tests/misc/projects/Issue10871/Context/compile.hxml.stdout create mode 100644 tests/misc/projects/Issue10871/TypeTools/Main.hx create mode 100644 tests/misc/projects/Issue10871/TypeTools/compile.hxml create mode 100644 tests/misc/projects/Issue10871/TypeTools/compile.hxml.stdout create mode 100644 tests/misc/projects/Issue10880/Main.hx create mode 100644 tests/misc/projects/Issue10880/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10880/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10881/Main.hx create mode 100644 tests/misc/projects/Issue10881/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10881/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10883/Main.hx create mode 100644 tests/misc/projects/Issue10883/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10883/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10890/Main.hx create mode 100644 tests/misc/projects/Issue10890/build.hxml create mode 100644 tests/misc/projects/Issue10897/AbstractFields.hx create mode 100644 tests/misc/projects/Issue10897/ClassFields.hx create mode 100644 tests/misc/projects/Issue10897/EnumFields.hx create mode 100644 tests/misc/projects/Issue10897/ImportAbstractField.hx create mode 100644 tests/misc/projects/Issue10897/ImportClassField.hx create mode 100644 tests/misc/projects/Issue10897/ImportEnumField.hx create mode 100644 tests/misc/projects/Issue10897/ImportModuleField.hx create mode 100644 tests/misc/projects/Issue10897/ImportSubtype.hx create mode 100644 tests/misc/projects/Issue10897/ModuleFields.hx create mode 100644 tests/misc/projects/Issue10897/Types.hx create mode 100644 tests/misc/projects/Issue10897/import-abstract-field-fail.hxml create mode 100644 tests/misc/projects/Issue10897/import-abstract-field-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10897/import-class-field-fail.hxml create mode 100644 tests/misc/projects/Issue10897/import-class-field-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10897/import-enum-field-fail.hxml create mode 100644 tests/misc/projects/Issue10897/import-enum-field-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10897/import-module-field-fail.hxml create mode 100644 tests/misc/projects/Issue10897/import-module-field-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10897/import-subtype-fail.hxml create mode 100644 tests/misc/projects/Issue10897/import-subtype-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10935/Main.hx create mode 100644 tests/misc/projects/Issue10935/build.hxml create mode 100644 tests/misc/projects/Issue10961/Main.hx create mode 100644 tests/misc/projects/Issue10961/compile-fail.hxml create mode 100644 tests/misc/projects/Issue10961/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue10980/Main.hx create mode 100644 tests/misc/projects/Issue10980/compile.hxml create mode 100644 tests/misc/projects/Issue10980/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue11004/Bar.hx create mode 100644 tests/misc/projects/Issue11004/Foo.hx create mode 100644 tests/misc/projects/Issue11004/Main.hx create mode 100644 tests/misc/projects/Issue11004/build.hxml create mode 100644 tests/misc/projects/Issue11004/build.hxml.stdout create mode 100644 tests/misc/projects/Issue11020/Main.hx create mode 100644 tests/misc/projects/Issue11020/compile.hxml create mode 100644 tests/misc/projects/Issue11020/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue11055/Main.hx create mode 100644 tests/misc/projects/Issue11055/compile-fail.hxml create mode 100644 tests/misc/projects/Issue11055/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue11055/compile-pretty-fail.hxml create mode 100644 tests/misc/projects/Issue11055/compile-pretty-fail.hxml.stderr delete mode 100644 tests/misc/projects/Issue2996/A.hx delete mode 100644 tests/misc/projects/Issue2996/Main.hx delete mode 100644 tests/misc/projects/Issue2996/compile1.hxml delete mode 100644 tests/misc/projects/Issue2996/compile1.hxml.stderr delete mode 100644 tests/misc/projects/Issue2996/compile2.hxml delete mode 100644 tests/misc/projects/Issue2996/compile2.hxml.stderr delete mode 100644 tests/misc/projects/Issue2996/pack/B.hx create mode 100644 tests/misc/projects/Issue4803/Main2.hx create mode 100644 tests/misc/projects/Issue4803/compile2-fail.hxml create mode 100644 tests/misc/projects/Issue4803/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue5644/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue5644/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue5854/NamedLocal.hx create mode 100644 tests/misc/projects/Issue5854/UnnamedLocal.hx create mode 100644 tests/misc/projects/Issue5854/compile-fail.hxml create mode 100644 tests/misc/projects/Issue5854/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue5854/compile.hxml create mode 100644 tests/misc/projects/Issue5854/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue5949/indent-fail.hxml create mode 100644 tests/misc/projects/Issue5949/indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue5949/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue5949/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6065/indent-fail.hxml create mode 100644 tests/misc/projects/Issue6065/indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6065/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue6065/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6584/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue6584/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6790/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue6790/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6794/Main.hx create mode 100644 tests/misc/projects/Issue6794/compile.hxml create mode 100644 tests/misc/projects/Issue6794/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue6796/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue6796/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6810/indent-fail.hxml create mode 100644 tests/misc/projects/Issue6810/indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6810/logfile-01-fail.hxml create mode 100644 tests/misc/projects/Issue6810/logfile-02-fail.hxml create mode 100644 tests/misc/projects/Issue6810/logfile-03-fail.hxml create mode 100644 tests/misc/projects/Issue6810/logfile-04-fail.hxml create mode 100644 tests/misc/projects/Issue6810/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue6810/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue6938/Main1.hx create mode 100644 tests/misc/projects/Issue6938/compile1-fail.hxml create mode 100644 tests/misc/projects/Issue6938/compile1-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue7669/Main.hx create mode 100644 tests/misc/projects/Issue7669/compile-fail.hxml create mode 100644 tests/misc/projects/Issue7669/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue7871/Macro.hx create mode 100644 tests/misc/projects/Issue7871/compile-fail.hxml create mode 100644 tests/misc/projects/Issue7871/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue7871/compile-next-fail.hxml create mode 100644 tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue7968/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue7968/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue8303/indent-fail.hxml create mode 100644 tests/misc/projects/Issue8303/indent-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue8303/pretty-fail.hxml create mode 100644 tests/misc/projects/Issue8303/pretty-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue8471/compile2-pretty.hxml create mode 100644 tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr create mode 100644 tests/misc/projects/Issue8634/Main.hx create mode 100644 tests/misc/projects/Issue8634/compile-fail.hxml create mode 100644 tests/misc/projects/Issue8634/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9163/Main.hx create mode 100644 tests/misc/projects/Issue9163/compile-fail.hxml create mode 100644 tests/misc/projects/Issue9163/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9171/Main.hx create mode 100644 tests/misc/projects/Issue9171/compile-fail.hxml create mode 100644 tests/misc/projects/Issue9171/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9381/FinalDisplay.hx create mode 100644 tests/misc/projects/Issue9381/FinalDisplayBuilder.hx create mode 100644 tests/misc/projects/Issue9381/Main.hx create mode 100644 tests/misc/projects/Issue9381/compile.hxml create mode 100644 tests/misc/projects/Issue9381/compile.hxml.stderr create mode 100644 tests/misc/projects/Issue9584/Main.hx create mode 100644 tests/misc/projects/Issue9584/compile-fail.hxml create mode 100644 tests/misc/projects/Issue9584/compile-fail.hxml.stderr delete mode 100644 tests/misc/projects/Issue9619/abstract-structInit/Main.hx delete mode 100644 tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/MainExpr.hx create mode 100644 tests/misc/projects/Issue9676/MainField.hx create mode 100644 tests/misc/projects/Issue9676/MainMacro.hx create mode 100644 tests/misc/projects/Issue9676/compile1-fail.hxml create mode 100644 tests/misc/projects/Issue9676/compile1-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/compile1D.hxml create mode 100644 tests/misc/projects/Issue9676/compile1D.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/compile2-fail.hxml create mode 100644 tests/misc/projects/Issue9676/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/compile2D.hxml create mode 100644 tests/misc/projects/Issue9676/compile2D.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/compile3-fail.hxml create mode 100644 tests/misc/projects/Issue9676/compile3-fail.hxml.stderr create mode 100644 tests/misc/projects/Issue9676/compile3D.hxml create mode 100644 tests/misc/projects/Issue9676/compile3D.hxml.stderr create mode 100644 tests/misc/projects/Issue9919/Main.hx create mode 100644 tests/misc/projects/Issue9919/compile.hxml create mode 100644 tests/misc/projects/Issue9919/compile.hxml.stderr create mode 100644 tests/misc/projects/default_type_params/Ctor.hx create mode 100644 tests/misc/projects/default_type_params/EnumCtor.hx create mode 100644 tests/misc/projects/default_type_params/Method.hx create mode 100644 tests/misc/projects/default_type_params/compile-ctor-fail.hxml create mode 100644 tests/misc/projects/default_type_params/compile-ctor-fail.hxml.stderr create mode 100644 tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml create mode 100644 tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml.stderr create mode 100644 tests/misc/projects/default_type_params/compile-method-fail.hxml create mode 100644 tests/misc/projects/default_type_params/compile-method-fail.hxml.stderr create mode 100644 tests/misc/projects/eventLoop/Main.hx create mode 100644 tests/misc/projects/eventLoop/compile.hxml create mode 100644 tests/misc/projects/eventLoop/compile.hxml.stdout.disabled create mode 100644 tests/misc/projects/local-static/Break/Main.hx create mode 100644 tests/misc/projects/local-static/Break/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/Break/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/Continue/Main.hx create mode 100644 tests/misc/projects/local-static/Continue/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/Function/Main.hx create mode 100644 tests/misc/projects/local-static/Function/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/Function/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/Local/Main.hx create mode 100644 tests/misc/projects/local-static/Local/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/Local/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/LocalVsLocal/Main.hx create mode 100644 tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/LocalVsStatic/Main.hx create mode 100644 tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/Return/Main.hx create mode 100644 tests/misc/projects/local-static/Return/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/Return/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/local-static/This/Main.hx create mode 100644 tests/misc/projects/local-static/This/compile-fail.hxml create mode 100644 tests/misc/projects/local-static/This/compile-fail.hxml.stderr create mode 100644 tests/misc/projects/recursive-callbacks/Main.hx create mode 100644 tests/misc/projects/recursive-callbacks/compile.hxml create mode 100644 tests/misc/projects/recursive-callbacks/compile.hxml.stderr create mode 100644 tests/misc/projects/sys-exit/SysExit0.hx create mode 100644 tests/misc/projects/sys-exit/SysExit1.hx create mode 100644 tests/misc/projects/sys-exit/compile00.hxml create mode 100644 tests/misc/projects/sys-exit/compile00.hxml.stderr create mode 100644 tests/misc/projects/sys-exit/compile01-fail.hxml create mode 100644 tests/misc/projects/sys-exit/compile01-fail.hxml.stderr create mode 100644 tests/misc/projects/sys-exit/compile10-fail.hxml create mode 100644 tests/misc/projects/sys-exit/compile10-fail.hxml.stderr create mode 100644 tests/misc/projects/sys-exit/compile11-fail.hxml create mode 100644 tests/misc/projects/sys-exit/compile11-fail.hxml.stderr create mode 100644 tests/misc/projects/trailing-commas/Main.hx create mode 100644 tests/misc/projects/trailing-commas/Main2.hx create mode 100644 tests/misc/projects/trailing-commas/Main3.hx create mode 100644 tests/misc/projects/trailing-commas/compile1.hxml create mode 100644 tests/misc/projects/trailing-commas/compile1.hxml.stdout create mode 100644 tests/misc/projects/trailing-commas/compile2-fail.hxml create mode 100644 tests/misc/projects/trailing-commas/compile2-fail.hxml.stderr create mode 100644 tests/misc/projects/trailing-commas/compile3-fail.hxml create mode 100644 tests/misc/projects/trailing-commas/compile3-fail.hxml.stderr create mode 100644 tests/misc/python/projects/Issue11060/Main.hx create mode 100644 tests/misc/python/projects/Issue11060/build.hxml create mode 100644 tests/misc/python/projects/Issue11060/build.hxml.stdout create mode 100644 tests/misc/src/diff/Analyze.hx create mode 100644 tests/misc/src/diff/Change.hx create mode 100644 tests/misc/src/diff/Context.hx create mode 100644 tests/misc/src/diff/Diffseq.hx create mode 100644 tests/misc/src/diff/FileData.hx create mode 100644 tests/misc/src/diff/IndexVector.hx create mode 100644 tests/misc/src/diff/Io.hx create mode 100644 tests/misc/src/diff/Printer.hx create mode 100644 tests/optimization/src/TestNadako.hx create mode 100644 tests/optimization/src/issues/Issue10188.hx create mode 100644 tests/optimization/src/issues/Issue10765.hx create mode 100644 tests/optimization/src/issues/Issue7237.hx create mode 100644 tests/optimization/src/issues/Issue7765.hx.disabled create mode 100644 tests/optimization/src/issues/Issue9943.hx create mode 100644 tests/server/src/SkipReason.hx create mode 100644 tests/server/src/cases/RetyperTests.hx create mode 100644 tests/server/src/cases/display/issues/Issue10635.hx create mode 100644 tests/server/src/cases/display/issues/Issue10638.hx create mode 100644 tests/server/src/cases/display/issues/Issue10678.hx create mode 100644 tests/server/src/cases/display/issues/Issue10781.hx create mode 100644 tests/server/src/cases/display/issues/Issue9140.hx create mode 100644 tests/server/src/cases/display/issues/Issue9318.hx create mode 100644 tests/server/src/cases/issues/Issue10646.hx create mode 100644 tests/server/src/cases/issues/Issue10653.hx create mode 100644 tests/server/src/cases/issues/Issue10677.hx create mode 100644 tests/server/src/cases/issues/Issue9636.hx create mode 100644 tests/server/test/templates/GenericInstanceMethod.hx create mode 100644 tests/server/test/templates/GenericMethod.hx create mode 100644 tests/server/test/templates/issues/Issue10635/MainAfter.hx create mode 100644 tests/server/test/templates/issues/Issue10635/MainBefore.hx create mode 100644 tests/server/test/templates/issues/Issue10635/MainInstanceAfter.hx create mode 100644 tests/server/test/templates/issues/Issue10635/MainInstanceBefore.hx create mode 100644 tests/server/test/templates/issues/Issue10635/Something.hx create mode 100644 tests/server/test/templates/issues/Issue10653/MainAfter.hx create mode 100644 tests/server/test/templates/issues/Issue10653/MainBefore.hx create mode 100644 tests/server/test/templates/issues/Issue10653/Test.hx create mode 100644 tests/server/test/templates/issues/Issue10677/Main.hx create mode 100644 tests/server/test/templates/issues/Issue10677/Polygon.hx create mode 100644 tests/server/test/templates/issues/Issue10677/PolygonCollider.hx create mode 100644 tests/server/test/templates/issues/Issue10677/Polygons.hx create mode 100644 tests/server/test/templates/issues/Issue10678/Macro.hx create mode 100644 tests/server/test/templates/issues/Issue10986/Main.hx create mode 100644 tests/server/test/templates/issues/Issue10986/Vector.hx create mode 100644 tests/server/test/templates/issues/Issue10986/build.hxml create mode 100644 tests/server/test/templates/issues/Issue8368/Main2.hx create mode 100644 tests/server/test/templates/issues/Issue8368/MyMacro2.macro.hx create mode 100644 tests/server/test/templates/issues/Issue9636/Foo.hx create mode 100644 tests/server/test/templates/issues/Issue9636/Main.hx create mode 100644 tests/server/test/templates/retyper/AbstractWithDependency.hx create mode 100644 tests/server/test/templates/retyper/AbstractWithSignatureDependency.hx create mode 100644 tests/server/test/templates/retyper/DependentEnum.hx create mode 100644 tests/server/test/templates/retyper/DependentTypedef.hx create mode 100644 tests/server/test/templates/retyper/IndependentEnum.hx create mode 100644 tests/server/test/templates/retyper/IndependentTypedef.hx create mode 100644 tests/server/test/templates/retyper/InterfaceDependency.hx create mode 100644 tests/server/test/templates/retyper/MutualDependency.hx create mode 100644 tests/server/test/templates/retyper/WithInterfaceDependency.hx create mode 100644 tests/server/test/templates/retyper/WithMutualDependency.hx create mode 100644 tests/server/test/templates/retyper/WithParentDependency.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependency.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyInferredArg.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyInferredProperty.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyInferredRet.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyInferredVariable.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyProperty.hx create mode 100644 tests/server/test/templates/retyper/WithSignatureDependencyVariable.hx create mode 100644 tests/sys/compile-js.hxml delete mode 100755 tests/sys/genTestRes.py create mode 100755 tests/sys/gen_test_res.py create mode 100644 tests/threads/src/cases/TestAtomics.hx create mode 100644 tests/threads/src/cases/TestCondition.hx create mode 100644 tests/threads/src/cases/TestSemaphore.hx rename tests/unit/{compile-flash9.hxml => compile-flash.hxml} (72%) create mode 100644 tests/unit/native_java/src/haxe/test/MyDefaultInterface.java create mode 100644 tests/unit/native_java/src/haxe/test/MyDefaultSubInterface.java create mode 100644 tests/unit/src/unit/TestDefaultTypeParameters.hx create mode 100644 tests/unit/src/unit/TestLocalStatic.hx create mode 100644 tests/unit/src/unit/TestNullCoalescing.hx create mode 100644 tests/unit/src/unit/TestNumericSeparator.hx create mode 100644 tests/unit/src/unit/TestNumericSuffixes.hx create mode 100644 tests/unit/src/unit/issues/Issue10066.hx create mode 100644 tests/unit/src/unit/issues/Issue10127.hx create mode 100644 tests/unit/src/unit/issues/Issue10135.hx create mode 100644 tests/unit/src/unit/issues/Issue10139.hx create mode 100644 tests/unit/src/unit/issues/Issue10148.hx create mode 100644 tests/unit/src/unit/issues/Issue10205.hx create mode 100644 tests/unit/src/unit/issues/Issue10209.hx create mode 100644 tests/unit/src/unit/issues/Issue10291.hx create mode 100644 tests/unit/src/unit/issues/Issue10304.hx create mode 100644 tests/unit/src/unit/issues/Issue10336.hx create mode 100644 tests/unit/src/unit/issues/Issue10383.hx create mode 100644 tests/unit/src/unit/issues/Issue10397.hx create mode 100644 tests/unit/src/unit/issues/Issue10410.hx create mode 100644 tests/unit/src/unit/issues/Issue10455.hx create mode 100644 tests/unit/src/unit/issues/Issue10468.hx create mode 100644 tests/unit/src/unit/issues/Issue10482.hx create mode 100644 tests/unit/src/unit/issues/Issue10508.hx create mode 100644 tests/unit/src/unit/issues/Issue10535.hx create mode 100644 tests/unit/src/unit/issues/Issue10537.hx create mode 100644 tests/unit/src/unit/issues/Issue10541.hx create mode 100644 tests/unit/src/unit/issues/Issue10550.hx create mode 100644 tests/unit/src/unit/issues/Issue10561.hx create mode 100644 tests/unit/src/unit/issues/Issue10565.hx create mode 100644 tests/unit/src/unit/issues/Issue10568.hx create mode 100644 tests/unit/src/unit/issues/Issue10571.hx create mode 100644 tests/unit/src/unit/issues/Issue10600.hx create mode 100644 tests/unit/src/unit/issues/Issue10604.hx create mode 100644 tests/unit/src/unit/issues/Issue10618.hx create mode 100644 tests/unit/src/unit/issues/Issue10624.hx create mode 100644 tests/unit/src/unit/issues/Issue10626.hx create mode 100644 tests/unit/src/unit/issues/Issue10641.hx create mode 100644 tests/unit/src/unit/issues/Issue10684.hx create mode 100644 tests/unit/src/unit/issues/Issue10685.hx create mode 100644 tests/unit/src/unit/issues/Issue10709.hx create mode 100644 tests/unit/src/unit/issues/Issue10712.hx create mode 100644 tests/unit/src/unit/issues/Issue10716.hx create mode 100644 tests/unit/src/unit/issues/Issue10720.hx create mode 100644 tests/unit/src/unit/issues/Issue10728.hx create mode 100644 tests/unit/src/unit/issues/Issue10730.hx create mode 100644 tests/unit/src/unit/issues/Issue10735.hx create mode 100644 tests/unit/src/unit/issues/Issue10738.hx create mode 100644 tests/unit/src/unit/issues/Issue10740.hx create mode 100644 tests/unit/src/unit/issues/Issue10743.hx create mode 100644 tests/unit/src/unit/issues/Issue10744.hx create mode 100644 tests/unit/src/unit/issues/Issue10746.hx create mode 100644 tests/unit/src/unit/issues/Issue10748.hx create mode 100644 tests/unit/src/unit/issues/Issue10752.hx create mode 100644 tests/unit/src/unit/issues/Issue10754.hx create mode 100644 tests/unit/src/unit/issues/Issue10761.hx create mode 100644 tests/unit/src/unit/issues/Issue10776.hx create mode 100644 tests/unit/src/unit/issues/Issue10799.hx create mode 100644 tests/unit/src/unit/issues/Issue10821.hx.disabled create mode 100644 tests/unit/src/unit/issues/Issue10830.hx create mode 100644 tests/unit/src/unit/issues/Issue10831.hx create mode 100644 tests/unit/src/unit/issues/Issue10834.hx create mode 100644 tests/unit/src/unit/issues/Issue10836.hx create mode 100644 tests/unit/src/unit/issues/Issue10845.hx create mode 100644 tests/unit/src/unit/issues/Issue10867.hx create mode 100644 tests/unit/src/unit/issues/Issue10876.hx create mode 100644 tests/unit/src/unit/issues/Issue10906.hx create mode 100644 tests/unit/src/unit/issues/Issue10960.hx create mode 100644 tests/unit/src/unit/issues/Issue10965.hx create mode 100644 tests/unit/src/unit/issues/Issue10993.hx create mode 100644 tests/unit/src/unit/issues/Issue11007.hx create mode 100644 tests/unit/src/unit/issues/Issue11040.hx create mode 100644 tests/unit/src/unit/issues/Issue5890.hx create mode 100644 tests/unit/src/unit/issues/Issue6017.hx create mode 100644 tests/unit/src/unit/issues/Issue6063.hx create mode 100644 tests/unit/src/unit/issues/Issue7736.hx create mode 100644 tests/unit/src/unit/issues/Issue8401.hx create mode 100644 tests/unit/src/unit/issues/Issue9198.hx create mode 100644 tests/unit/src/unit/issues/Issue9543.hx create mode 100644 tests/unit/src/unit/issues/Issue9563.hx create mode 100644 tests/unit/src/unitstd/haxe/atomic/AtomicBool.unit.hx create mode 100644 tests/unit/src/unitstd/haxe/atomic/AtomicInt.unit.hx create mode 100644 tests/unit/src/unitstd/haxe/atomic/AtomicObject.unit.hx create mode 100644 tests/unit/src/unitstd/haxe/crypto/Base64.unit.hx diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..3cea1091 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.202.5/containers/docker-from-docker-compose +{ + "name": "haxe", + "dockerComposeFile": "docker-compose.yml", + "service": "workspace", + "workspaceFolder": "/workspace", + + // Use this environment variable if you need to bind mount your local source code into a new container. + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + "extensions": [ + "nadako.vshaxe", + "ms-azuretools.vscode-docker", + "earthly.earthfile-syntax-highlighting", + ], + + "remoteUser": "vscode" +} \ No newline at end of file diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..230decf5 --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,28 @@ +version: '3' +services: + workspace: + image: ghcr.io/haxefoundation/haxe_devcontainer:development + init: true + volumes: + - /var/run/docker.sock:/var/run/docker-host.sock + - ..:/workspace:cached + environment: + - EARTHLY_BUILDKIT_HOST=tcp://earthly:8372 + - EARTHLY_USE_INLINE_CACHE=true + - EARTHLY_SAVE_INLINE_CACHE=true + user: vscode + entrypoint: /usr/local/share/docker-init.sh + command: sleep infinity + earthly: + image: earthly/buildkitd:v0.6.13 + privileged: true + environment: + - BUILDKIT_TCP_TRANSPORT_ENABLED=true + expose: + - 8372 + volumes: + # https://docs.earthly.dev/docs/guides/using-the-earthly-docker-images/buildkit-standalone#earthly_tmp_dir + - earthly-tmp:/tmp/earthly:rw + +volumes: + earthly-tmp: diff --git a/.devcontainer/library-scripts/common-debian.sh b/.devcontainer/library-scripts/common-debian.sh new file mode 100644 index 00000000..f453a6b1 --- /dev/null +++ b/.devcontainer/library-scripts/common-debian.sh @@ -0,0 +1,454 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages] + +set -e + +INSTALL_ZSH=${1:-"true"} +USERNAME=${2:-"automatic"} +USER_UID=${3:-"automatic"} +USER_GID=${4:-"automatic"} +UPGRADE_PACKAGES=${5:-"true"} +INSTALL_OH_MYS=${6:-"true"} +ADD_NON_FREE_PACKAGES=${7:-"false"} +SCRIPT_DIR="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)" +MARKER_FILE="/usr/local/etc/vscode-dev-containers/common" + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Ensure that login shells get the correct path if the user updated the PATH using ENV. +rm -f /etc/profile.d/00-restore-env.sh +echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh +chmod +x /etc/profile.d/00-restore-env.sh + +# If in automatic mode, determine if a user already exists, if not use vscode +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=vscode + fi +elif [ "${USERNAME}" = "none" ]; then + USERNAME=root + USER_UID=0 + USER_GID=0 +fi + +# Load markers to see which steps have already run +if [ -f "${MARKER_FILE}" ]; then + echo "Marker file found:" + cat "${MARKER_FILE}" + source "${MARKER_FILE}" +fi + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Function to call apt-get if needed +apt_get_update_if_needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies +if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then + + package_list="apt-utils \ + openssh-client \ + gnupg2 \ + dirmngr \ + iproute2 \ + procps \ + lsof \ + htop \ + net-tools \ + psmisc \ + curl \ + wget \ + rsync \ + ca-certificates \ + unzip \ + zip \ + nano \ + vim-tiny \ + less \ + jq \ + lsb-release \ + apt-transport-https \ + dialog \ + libc6 \ + libgcc1 \ + libkrb5-3 \ + libgssapi-krb5-2 \ + libicu[0-9][0-9] \ + liblttng-ust0 \ + libstdc++6 \ + zlib1g \ + locales \ + sudo \ + ncdu \ + man-db \ + strace \ + manpages \ + manpages-dev \ + init-system-helpers" + + # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian + if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then + # Bring in variables from /etc/os-release like VERSION_CODENAME + . /etc/os-release + sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list + # Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html + sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list + sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list + echo "Running apt-get update..." + apt-get update + package_list="${package_list} manpages-posix manpages-posix-dev" + else + apt_get_update_if_needed + fi + + # Install libssl1.1 if available + if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then + package_list="${package_list} libssl1.1" + fi + + # Install appropriate version of libssl1.0.x if available + libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '') + if [ "$(echo "$LIlibssl_packageBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then + if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then + # Debian 9 + package_list="${package_list} libssl1.0.2" + elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then + # Ubuntu 18.04, 16.04, earlier + package_list="${package_list} libssl1.0.0" + fi + fi + + echo "Packages to verify are installed: ${package_list}" + apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 ) + + # Install git if not already installed (may be more recent than distro version) + if ! type git > /dev/null 2>&1; then + apt-get -y install --no-install-recommends git + fi + + PACKAGES_ALREADY_INSTALLED="true" +fi + +# Get to latest versions of all packages +if [ "${UPGRADE_PACKAGES}" = "true" ]; then + apt_get_update_if_needed + apt-get -y upgrade --no-install-recommends + apt-get autoremove -y +fi + +# Ensure at least the en_US.UTF-8 UTF-8 locale is available. +# Common need for both applications and things like the agnoster ZSH theme. +if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen + locale-gen + LOCALE_ALREADY_SET="true" +fi + +# Create or update a non-root user to match UID/GID. +group_name="${USERNAME}" +if id -u ${USERNAME} > /dev/null 2>&1; then + # User exists, update if needed + if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then + group_name="$(id -gn $USERNAME)" + groupmod --gid $USER_GID ${group_name} + usermod --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then + usermod --uid $USER_UID $USERNAME + fi +else + # Create user + if [ "${USER_GID}" = "automatic" ]; then + groupadd $USERNAME + else + groupadd --gid $USER_GID $USERNAME + fi + if [ "${USER_UID}" = "automatic" ]; then + useradd -s /bin/bash --gid $USERNAME -m $USERNAME + else + useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME + fi +fi + +# Add add sudo support for non-root user +if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then + echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME + chmod 0440 /etc/sudoers.d/$USERNAME + EXISTING_NON_ROOT_USER="${USERNAME}" +fi + +# ** Shell customization section ** +if [ "${USERNAME}" = "root" ]; then + user_rc_path="/root" +else + user_rc_path="/home/${USERNAME}" +fi + +# Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty +if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then + cp /etc/skel/.bashrc "${user_rc_path}/.bashrc" +fi + +# Restore user .profile defaults from skeleton file if it doesn't exist or is empty +if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then + cp /etc/skel/.profile "${user_rc_path}/.profile" +fi + +# .bashrc/.zshrc snippet +rc_snippet="$(cat << 'EOF' + +if [ -z "${USER}" ]; then export USER=$(whoami); fi +if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi + +# Display optional first run image specific notice if configured and terminal is interactive +if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then + if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then + cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" + elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then + cat "/workspaces/.codespaces/shared/first-run-notice.txt" + fi + mkdir -p "$HOME/.config/vscode-dev-containers" + # Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it + ((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &) +fi + +# Set the default git editor if not already set +if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then + if [ "${TERM_PROGRAM}" = "vscode" ]; then + if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then + export GIT_EDITOR="code-insiders --wait" + else + export GIT_EDITOR="code --wait" + fi + fi +fi + +EOF +)" + +# code shim, it fallbacks to code-insiders if code is not available +cat << 'EOF' > /usr/local/bin/code +#!/bin/sh + +get_in_path_except_current() { + which -a "$1" | grep -A1 "$0" | grep -v "$0" +} + +code="$(get_in_path_except_current code)" + +if [ -n "$code" ]; then + exec "$code" "$@" +elif [ "$(command -v code-insiders)" ]; then + exec code-insiders "$@" +else + echo "code or code-insiders is not installed" >&2 + exit 127 +fi +EOF +chmod +x /usr/local/bin/code + +# systemctl shim - tells people to use 'service' if systemd is not running +cat << 'EOF' > /usr/local/bin/systemctl +#!/bin/sh +set -e +if [ -d "/run/systemd/system" ]; then + exec /bin/systemctl/systemctl "$@" +else + echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services intead. e.g.: \n\nservice --status-all' +fi +EOF +chmod +x /usr/local/bin/systemctl + +# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme +codespaces_bash="$(cat \ +<<'EOF' + +# Codespaces bash prompt theme +__bash_prompt() { + local userpart='`export XIT=$? \ + && [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \ + && [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`' + local gitbranch='`\ + if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \ + export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \ + if [ "${BRANCH}" != "" ]; then \ + echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \ + && if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \ + echo -n " \[\033[1;33m\]✗"; \ + fi \ + && echo -n "\[\033[0;36m\]) "; \ + fi; \ + fi`' + local lightblue='\[\033[1;34m\]' + local removecolor='\[\033[0m\]' + PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ " + unset -f __bash_prompt +} +__bash_prompt + +EOF +)" + +codespaces_zsh="$(cat \ +<<'EOF' +# Codespaces zsh prompt theme +__zsh_prompt() { + local prompt_username + if [ ! -z "${GITHUB_USER}" ]; then + prompt_username="@${GITHUB_USER}" + else + prompt_username="%n" + fi + PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow + PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd + PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status + PROMPT+='%{$fg[white]%}$ %{$reset_color%}' + unset -f __zsh_prompt +} +ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}" +ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} " +ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})" +ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})" +__zsh_prompt + +EOF +)" + +# Add RC snippet and custom bash prompt +if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then + echo "${rc_snippet}" >> /etc/bash.bashrc + echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc" + echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc" + if [ "${USERNAME}" != "root" ]; then + echo "${codespaces_bash}" >> "/root/.bashrc" + echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc" + fi + chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc" + RC_SNIPPET_ALREADY_ADDED="true" +fi + +# Optionally install and configure zsh and Oh My Zsh! +if [ "${INSTALL_ZSH}" = "true" ]; then + if ! type zsh > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get install -y zsh + fi + if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then + echo "${rc_snippet}" >> /etc/zsh/zshrc + ZSH_ALREADY_INSTALLED="true" + fi + + # Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme. + # See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script. + oh_my_install_dir="${user_rc_path}/.oh-my-zsh" + if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then + template_path="${oh_my_install_dir}/templates/zshrc.zsh-template" + user_rc_file="${user_rc_path}/.zshrc" + umask g-w,o-w + mkdir -p ${oh_my_install_dir} + git clone --depth=1 \ + -c core.eol=lf \ + -c core.autocrlf=false \ + -c fsck.zeroPaddedFilemode=ignore \ + -c fetch.fsck.zeroPaddedFilemode=ignore \ + -c receive.fsck.zeroPaddedFilemode=ignore \ + "https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1 + echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file} + sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file} + + mkdir -p ${oh_my_install_dir}/custom/themes + echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme" + # Shrink git while still enabling updates + cd "${oh_my_install_dir}" + git repack -a -d -f --depth=1 --window=1 + # Copy to non-root user if one is specified + if [ "${USERNAME}" != "root" ]; then + cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root + chown -R ${USERNAME}:${group_name} "${user_rc_path}" + fi + fi +fi + +# Persist image metadata info, script if meta.env found in same directory +meta_info_script="$(cat << 'EOF' +#!/bin/sh +. /usr/local/etc/vscode-dev-containers/meta.env + +# Minimal output +if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then + echo "${VERSION}" + exit 0 +elif [ "$1" = "release" ]; then + echo "${GIT_REPOSITORY_RELEASE}" + exit 0 +elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then + echo "${CONTENTS_URL}" + exit 0 +fi + +#Full output +echo +echo "Development container image information" +echo +if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi +if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi +if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi +if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi +if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi +if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi +if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi +echo +EOF +)" +if [ -f "${SCRIPT_DIR}/meta.env" ]; then + mkdir -p /usr/local/etc/vscode-dev-containers/ + cp -f "${SCRIPT_DIR}/meta.env" /usr/local/etc/vscode-dev-containers/meta.env + echo "${meta_info_script}" > /usr/local/bin/devcontainer-info + chmod +x /usr/local/bin/devcontainer-info +fi + +# Write marker file +mkdir -p "$(dirname "${MARKER_FILE}")" +echo -e "\ + PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\ + LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\ + EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\ + RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\ + ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}" + +echo "Done!" diff --git a/.devcontainer/library-scripts/docker-debian.sh b/.devcontainer/library-scripts/docker-debian.sh new file mode 100644 index 00000000..a60e49a6 --- /dev/null +++ b/.devcontainer/library-scripts/docker-debian.sh @@ -0,0 +1,309 @@ +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- +# +# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md +# Maintainer: The VS Code and Codespaces Teams +# +# Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby] [CLI version] + +ENABLE_NONROOT_DOCKER=${1:-"true"} +SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"} +TARGET_SOCKET=${3:-"/var/run/docker.sock"} +USERNAME=${4:-"automatic"} +USE_MOBY=${5:-"true"} +DOCKER_VERSION=${6:-"latest"} +MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" +DOCKER_DASH_COMPOSE_VERSION="1" + +set -e + +if [ "$(id -u)" -ne 0 ]; then + echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' + exit 1 +fi + +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in ${POSSIBLE_USERS[@]}; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi + +# Get central common setting +get_common_setting() { + if [ "${common_settings_file_loaded}" != "true" ]; then + curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping." + common_settings_file_loaded=true + fi + if [ -f "/tmp/vsdc-settings.env" ]; then + local multi_line="" + if [ "$2" = "true" ]; then multi_line="-z"; fi + local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')" + if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi + fi + echo "$1=${!1}" +} + +# Function to run apt-get if needed +apt_get_update_if_needed() +{ + if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update + else + echo "Skipping apt-get update." + fi +} + +# Checks if packages are installed and installs them if not +check_packages() { + if ! dpkg -s "$@" > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install --no-install-recommends "$@" + fi +} + +# Figure out correct version of a three part version number is not passed +find_version_from_git_tags() { + local variable_name=$1 + local requested_version=${!variable_name} + if [ "${requested_version}" = "none" ]; then return; fi + local repository=$2 + local prefix=${3:-"tags/v"} + local separator=${4:-"."} + local last_part_optional=${5:-"false"} + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then + local escaped_separator=${separator//./\\.} + local last_part + if [ "${last_part_optional}" = "true" ]; then + last_part="(${escaped_separator}[0-9]+)?" + else + last_part="${escaped_separator}[0-9]+" + fi + local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$" + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" + else + set +e + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" + set -e + fi + fi + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 + exit 1 + fi + echo "${variable_name}=${!variable_name}" +} + +# Ensure apt is in non-interactive to avoid prompts +export DEBIAN_FRONTEND=noninteractive + +# Install dependencies +check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr +if ! type git > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install git +fi + +# Source /etc/os-release to get OS info +. /etc/os-release +# Fetch host/container arch. +architecture="$(dpkg --print-architecture)" + +# Set up the necessary apt repos (either Microsoft's or Docker's) +if [ "${USE_MOBY}" = "true" ]; then + + cli_package_name="moby-cli" + + # Import key safely and import Microsoft apt repo + get_common_setting MICROSOFT_GPG_KEYS_URI + curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg + echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list +else + # Name of proprietary engine package + cli_package_name="docker-ce-cli" + + # Import key safely and import Docker apt repo + curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list +fi + +# Refresh apt lists +apt-get update + +# Soft version matching for CLI +if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then + # Empty, meaning grab whatever "latest" is in apt repo + cli_version_suffix="" +else + # Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...) + docker_version_dot_escaped="${DOCKER_VERSION//./\\.}" + docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}" + # Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/ + docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)" + set +e # Don't exit if finding version fails - will handle gracefully + cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")" + set -e + if [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ]; then + echo "(!) No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:" + apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+' + exit 1 + fi + echo "cli_version_suffix ${cli_version_suffix}" +fi + +# Install Docker / Moby CLI if not already installed +if type docker > /dev/null 2>&1; then + echo "Docker / Moby CLI already installed." +else + if [ "${USE_MOBY}" = "true" ]; then + apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx + apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping." + else + apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} + fi +fi + +# Install Docker Compose if not already installed and is on a supported architecture +if type docker-compose > /dev/null 2>&1; then + echo "Docker Compose already installed." +else + TARGET_COMPOSE_ARCH="$(uname -m)" + if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then + TARGET_COMPOSE_ARCH="x86_64" + fi + if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then + # Use pip to get a version that runns on this architecture + if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install python3-minimal python3-pip libffi-dev python3-venv + fi + export PIPX_HOME=/usr/local/pipx + mkdir -p ${PIPX_HOME} + export PIPX_BIN_DIR=/usr/local/bin + export PYTHONUSERBASE=/tmp/pip-tmp + export PIP_CACHE_DIR=/tmp/pip-tmp/cache + pipx_bin=pipx + if ! type pipx > /dev/null 2>&1; then + pip3 install --disable-pip-version-check --no-cache-dir --user pipx + pipx_bin=/tmp/pip-tmp/bin/pipx + fi + ${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose + rm -rf /tmp/pip-tmp + else + find_version_from_git_tags DOCKER_DASH_COMPOSE_VERSION "https://github.com/docker/compose" "tags/" + echo "(*) Installing docker-compose ${DOCKER_DASH_COMPOSE_VERSION}..." + curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_DASH_COMPOSE_VERSION}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + fi +fi + +# If init file already exists, exit +if [ -f "/usr/local/share/docker-init.sh" ]; then + exit 0 +fi +echo "docker-init doesnt exist, adding..." + +# By default, make the source and target sockets the same +if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then + touch "${SOURCE_SOCKET}" + ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}" +fi + +# Add a stub if not adding non-root user access, user is root +if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then + echo '/usr/bin/env bash -c "\$@"' > /usr/local/share/docker-init.sh + chmod +x /usr/local/share/docker-init.sh + exit 0 +fi + +# If enabling non-root access and specified user is found, setup socat and add script +chown -h "${USERNAME}":root "${TARGET_SOCKET}" +if ! dpkg -s socat > /dev/null 2>&1; then + apt_get_update_if_needed + apt-get -y install socat +fi +tee /usr/local/share/docker-init.sh > /dev/null \ +<< EOF +#!/usr/bin/env bash +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +set -e + +SOCAT_PATH_BASE=/tmp/vscr-docker-from-docker +SOCAT_LOG=\${SOCAT_PATH_BASE}.log +SOCAT_PID=\${SOCAT_PATH_BASE}.pid + +# Wrapper function to only use sudo if not already root +sudoIf() +{ + if [ "\$(id -u)" -ne 0 ]; then + sudo "\$@" + else + "\$@" + fi +} + +# Log messages +log() +{ + echo -e "[\$(date)] \$@" | sudoIf tee -a \${SOCAT_LOG} > /dev/null +} + +echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null +log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}" + +# If enabled, try to add a docker group with the right GID. If the group is root, +# fall back on using socat to forward the docker socket to another unix socket so +# that we can set permissions on it without affecting the host. +if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then + SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET}) + if [ "\${SOCKET_GID}" != "0" ]; then + log "Adding user to group with GID \${SOCKET_GID}." + if [ "\$(cat /etc/group | grep :\${SOCKET_GID}:)" = "" ]; then + sudoIf groupadd --gid \${SOCKET_GID} docker-host + fi + # Add user to group if not already in it + if [ "\$(id ${USERNAME} | grep -E "groups.*(=|,)\${SOCKET_GID}\(")" = "" ]; then + sudoIf usermod -aG \${SOCKET_GID} ${USERNAME} + fi + else + # Enable proxy if not already running + if [ ! -f "\${SOCAT_PID}" ] || ! ps -p \$(cat \${SOCAT_PID}) > /dev/null; then + log "Enabling socket proxy." + log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode" + sudoIf rm -rf ${TARGET_SOCKET} + (sudoIf socat UNIX-LISTEN:${TARGET_SOCKET},fork,mode=660,user=${USERNAME} UNIX-CONNECT:${SOURCE_SOCKET} 2>&1 | sudoIf tee -a \${SOCAT_LOG} > /dev/null & echo "\$!" | sudoIf tee \${SOCAT_PID} > /dev/null) + else + log "Socket proxy already running." + fi + fi + log "Success" +fi + +# Execute whatever commands were passed in (if any). This allows us +# to set this script to ENTRYPOINT while still executing the default CMD. +set +e +exec "\$@" +EOF +chmod +x /usr/local/share/docker-init.sh +chown ${USERNAME}:root /usr/local/share/docker-init.sh +echo "Done!" \ No newline at end of file diff --git a/.earthlyignore b/.earthlyignore new file mode 100644 index 00000000..5244d8f9 --- /dev/null +++ b/.earthlyignore @@ -0,0 +1,6 @@ +.github +.vscode +Earthfile +extra/doc +bin +out \ No newline at end of file diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml new file mode 100644 index 00000000..b7e572d0 --- /dev/null +++ b/.github/workflows/cancel.yml @@ -0,0 +1,14 @@ +name: Cancel previous jobs +on: + workflow_run: + workflows: ["CI"] + types: + - requested +jobs: + cancel: + runs-on: ubuntu-latest + steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.11.0 + with: + workflow_id: ${{ github.event.workflow.id }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 47ba17cf..5e39221e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,22 +5,13 @@ name: CI on: [push, pull_request] jobs: - cancel: - name: "Cancel previous runs" - runs-on: ubuntu-18.04 - timeout-minutes: 3 - steps: - - uses: styfle/cancel-workflow-action@0.3.1 - with: - workflow_id: 1447568 - access_token: ${{ github.token }} - windows64-build: runs-on: windows-latest env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true PLATFORM: windows64 OPAMYES: 1 + OPAMROOT: D:\.opam CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ ARCH: 64 MINGW_ARCH: x86_64 @@ -31,23 +22,19 @@ jobs: with: submodules: recursive - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} + - name: Use GNU Tar from msys run: | - set -ex - - curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz - tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP - NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` - sudo mkdir -p /usr/local/bin - sudo mkdir -p /usr/local/lib/neko - sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ - sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ - sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ - echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} + echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + rm C:\msys64\usr\bin\bash.exe + + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: D:\.opam + key: ${{ runner.os }}64-${{ hashFiles('./opam', './libs/') }} + + - name: Install Neko from S3 shell: pwsh run: | Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip @@ -55,10 +42,10 @@ jobs: $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* echo "$NEKOPATH" >> $env:GITHUB_PATH echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + - name: choco install nsis uses: nick-invision/retry@v1 with: @@ -71,13 +58,14 @@ jobs: - name: Prepend Chocolatey path shell: pwsh run: Write-Host "::add-path::C:\ProgramData\chocolatey\bin" - - - name: Install OCaml and OCaml libraries + + - name: Install OCaml shell: pwsh run: | Set-PSDebug -Trace 1 curl.exe -fsSL -o cygwin-setup.exe --retry 3 $($env:CYGWIN_SETUP) - Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre" -Wait + Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre2-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre2" -Wait -RedirectStandardOutput cygwin-setup-out.txt + Get-Content cygwin-setup-out.txt curl.exe -fsSL -o "opam.tar.xz" --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.2/opam$($env:ARCH).tar.xz curl.exe -fsSL -o "libmbedtls.tar.xz" --retry 3 https://github.com/Simn/mingw64-mbedtls/releases/download/2.16.3/mingw64-$($env:MINGW_ARCH)-mbedtls-2.16.3-1.tar.xz & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'curl -L https://cpanmin.us | perl - App::cpanminus') @@ -87,23 +75,35 @@ jobs: & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -C / -xvf libmbedtls.tar.xz') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -xf opam.tar.xz') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && bash opam${ARCH}/install.sh') + + - name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' + shell: pwsh + run: | + Set-PSDebug -Trace 1 & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam init mingw "https://github.com/fdopen/opam-repository-mingw.git#opam2" --comp 4.07.0+mingw${ARCH}c --switch 4.07.0+mingw${ARCH}c --auto-setup --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add ctypes 0.17.1 --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v') - + - name: Expose mingw dll files shell: pwsh run: Write-Host "::add-path::${env:CYG_ROOT}/usr/$($env:MINGW_ARCH)-w64-mingw32/sys-root/mingw/bin" - + + # required to be able to retrieve the revision + - name: Mark directory as safe + shell: pwsh + run: | + Set-PSDebug -Trace 1 + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'git config --global --add safe.directory "$OLDPWD"') + - name: Set ADD_REVISION=1 for non-release if: ${{ !startsWith(github.ref, 'refs/tags/') }} shell: pwsh run: echo "ADD_REVISION=1" >> $Env:GITHUB_ENV - + - name: Build Haxe shell: pwsh run: | @@ -115,13 +115,13 @@ jobs: & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxe.exe') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxelib.exe') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && ls ./out') - + - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: name: win${{env.ARCH}}Binaries path: out - + windows-build: runs-on: windows-latest @@ -129,7 +129,8 @@ jobs: ACTIONS_ALLOW_UNSECURE_COMMANDS: true PLATFORM: windows OPAMYES: 1 - CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ + OPAMROOT: D:\.opam + CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin-archive/20221123 ARCH: 32 MINGW_ARCH: i686 CYGWIN_SETUP: https://cygwin.com/setup-x86.exe @@ -139,23 +140,19 @@ jobs: with: submodules: recursive - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} + - name: Use GNU Tar from msys run: | - set -ex - - curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz - tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP - NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` - sudo mkdir -p /usr/local/bin - sudo mkdir -p /usr/local/lib/neko - sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ - sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ - sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ - echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} + echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + rm C:\msys64\usr\bin\bash.exe + + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: D:\.opam + key: ${{ runner.os }}32-${{ hashFiles('./opam', './libs/') }} + + - name: Install Neko from S3 shell: pwsh run: | Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip @@ -163,10 +160,10 @@ jobs: $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* echo "$NEKOPATH" >> $env:GITHUB_PATH echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + - name: choco install nsis uses: nick-invision/retry@v1 with: @@ -179,13 +176,15 @@ jobs: - name: Prepend Chocolatey path shell: pwsh run: Write-Host "::add-path::C:\ProgramData\chocolatey\bin" - - - name: Install OCaml and OCaml libraries + + - name: Install OCaml shell: pwsh run: | Set-PSDebug -Trace 1 curl.exe -fsSL -o cygwin-setup.exe --retry 3 $($env:CYGWIN_SETUP) - Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre" -Wait + Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre2-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre2 --allow-unsupported-windows" -Wait -RedirectStandardOutput cygwin-setup-out.txt + Get-Content cygwin-setup-out.txt + & "$($env:CYG_ROOT)/bin/bash.exe" @('--norc', '--noprofile', '/etc/postinstall/ca-certificates.sh') curl.exe -fsSL -o "opam.tar.xz" --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.2/opam$($env:ARCH).tar.xz curl.exe -fsSL -o "libmbedtls.tar.xz" --retry 3 https://github.com/Simn/mingw64-mbedtls/releases/download/2.16.3/mingw64-$($env:MINGW_ARCH)-mbedtls-2.16.3-1.tar.xz & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'curl -L https://cpanmin.us | perl - App::cpanminus') @@ -195,23 +194,35 @@ jobs: & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -C / -xvf libmbedtls.tar.xz') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -xf opam.tar.xz') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && bash opam${ARCH}/install.sh') + + - name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' + shell: pwsh + run: | + Set-PSDebug -Trace 1 & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam init mingw "https://github.com/fdopen/opam-repository-mingw.git#opam2" --comp 4.07.0+mingw${ARCH}c --switch 4.07.0+mingw${ARCH}c --auto-setup --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add ctypes 0.17.1 --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v') - + - name: Expose mingw dll files shell: pwsh run: Write-Host "::add-path::${env:CYG_ROOT}/usr/$($env:MINGW_ARCH)-w64-mingw32/sys-root/mingw/bin" - + + # required to be able to retrieve the revision + - name: Mark directory as safe + shell: pwsh + run: | + Set-PSDebug -Trace 1 + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'git config --global --add safe.directory "$OLDPWD"') + - name: Set ADD_REVISION=1 for non-release if: ${{ !startsWith(github.ref, 'refs/tags/') }} shell: pwsh run: echo "ADD_REVISION=1" >> $Env:GITHUB_ENV - + - name: Build Haxe shell: pwsh run: | @@ -223,16 +234,16 @@ jobs: & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxe.exe') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && cygcheck ./haxelib.exe') & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && ls ./out') - + - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: name: win${{env.ARCH}}Binaries path: out - + linux-build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 env: PLATFORM: linux64 OPAMYES: 1 @@ -241,11 +252,17 @@ jobs: with: submodules: recursive - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: ~/.opam/ + key: ${{ runner.os }}-${{ hashFiles('./opam', './libs/') }}-2 + + - name: Install Neko from S3 run: | set -ex - + curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` @@ -255,29 +272,21 @@ jobs: sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} - shell: pwsh - run: | - Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip - Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP - $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* - echo "$NEKOPATH" >> $env:GITHUB_PATH - echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + + - name: Install dependencies run: | set -ex sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2 sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls sudo apt-get update -qqy - sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre3-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build libstring-shellquote-perl - + sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build libstring-shellquote-perl libipc-system-simple-perl + - name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' run: | set -ex opam init # --disable-sandboxing @@ -286,11 +295,11 @@ jobs: opam install haxe --deps-only --assume-depexts opam list ocamlopt -v - + - name: Set ADD_REVISION=1 for non-release if: ${{ !startsWith(github.ref, 'refs/tags/') }} run: echo "ADD_REVISION=1" >> $GITHUB_ENV - + - name: Build Haxe run: | set -ex @@ -301,13 +310,13 @@ jobs: ls -l out ldd -v ./haxe ldd -v ./haxelib - + # https://stackoverflow.com/questions/58033366/how-to-get-current-branch-within-github-actions - name: Extract branch name id: extract_branch shell: bash run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - + - name: Build xmldoc run: | set -ex @@ -318,19 +327,234 @@ jobs: "branch": "${{ steps.extract_branch.outputs.branch }}" } EOL - + + - name: Upload artifact + uses: actions/upload-artifact@v1.0.0 + with: + name: linuxBinaries + path: out + - name: Upload xmldoc artifact uses: actions/upload-artifact@v1.0.0 with: name: xmldoc path: extra/doc - + + linux-test: + needs: linux-build + runs-on: ubuntu-20.04 + env: + PLATFORM: linux64 + TEST: ${{matrix.target}} + HXCPP_COMPILE_CACHE: ~/hxcache + HAXE_STD_PATH: /usr/local/share/haxe/std + strategy: + fail-fast: false + matrix: + target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, lua, flash, neko] + include: + - target: hl + APT_PACKAGES: cmake ninja-build libturbojpeg-dev + - target: cpp + APT_PACKAGES: gcc-multilib g++-multilib + - target: lua + APT_PACKAGES: ncurses-dev + - target: flash + APT_PACKAGES: libglib2.0-0 libgtk2.0-0 libfreetype6 xvfb + steps: + - uses: actions/checkout@main + with: + submodules: recursive + - uses: actions/download-artifact@v1 + with: + name: linuxBinaries + + - name: Install Neko from S3 + run: | + set -ex + + curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz + tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP + NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` + sudo mkdir -p /usr/local/bin + sudo mkdir -p /usr/local/lib/neko + sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ + sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ + sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ + echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV + + - name: Print Neko version + run: neko -version 2>&1 + + + - name: Setup Haxe + run: | + sudo apt install -qqy libmbedtls-dev + + set -ex + tar -xf linuxBinaries/*_bin.tar.gz -C linuxBinaries --strip-components=1 + sudo mkdir -p /usr/local/bin/ + sudo mkdir -p /usr/local/share/haxe/ + sudo ln -s `pwd`/linuxBinaries/haxe /usr/local/bin/haxe + sudo ln -s `pwd`/linuxBinaries/haxelib /usr/local/bin/haxelib + sudo ln -s `pwd`/linuxBinaries/std /usr/local/share/haxe/std + + - name: Print Haxe version + run: haxe -version + + - name: Setup haxelib + run: | + set -ex + mkdir ~/haxelib + haxelib setup ~/haxelib + + - name: Install apt packages + if: matrix.APT_PACKAGES + run: | + set -ex + sudo apt update -qqy + sudo apt install -qqy ${{matrix.APT_PACKAGES}} + + - name: Flash setup + if: matrix.target == 'flash' + run: export DISPLAY=:99.0 + + - name: Test + run: haxe RunCi.hxml + working-directory: ${{github.workspace}}/tests + + test-docgen: + needs: linux-build + runs-on: ubuntu-20.04 + env: + PLATFORM: linux64 + HXCPP_COMPILE_CACHE: ~/hxcache + steps: + - uses: actions/checkout@main + with: + submodules: recursive + + - uses: actions/download-artifact@v1 + with: + name: linuxBinaries + + - name: Download xmldoc artifact + uses: actions/download-artifact@v2 + with: + name: xmldoc + path: xmldoc + + - name: Install Neko from S3 + run: | + set -ex + + curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz + tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP + NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` + sudo mkdir -p /usr/local/bin + sudo mkdir -p /usr/local/lib/neko + sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ + sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ + sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ + echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV + + - name: Print Neko version + run: neko -version 2>&1 + + + - name: Setup Haxe + run: | + sudo apt install -qqy libmbedtls-dev + + set -ex + tar -xf linuxBinaries/*_bin.tar.gz -C linuxBinaries --strip-components=1 + sudo mkdir -p /usr/local/bin/ + sudo mkdir -p /usr/local/share/haxe/ + sudo ln -s `pwd`/linuxBinaries/haxe /usr/local/bin/haxe + sudo ln -s `pwd`/linuxBinaries/haxelib /usr/local/bin/haxelib + sudo ln -s `pwd`/linuxBinaries/std /usr/local/share/haxe/std + + - name: Print Haxe version + run: haxe -version + + - name: Setup haxelib + run: | + set -ex + mkdir ~/haxelib + haxelib setup ~/haxelib + + - name: Test documentation generation + run: | + set -ex + haxelib git dox https://github.com/HaxeFoundation/dox.git + haxelib git hxtemplo https://github.com/Simn/hxtemplo.git + haxelib git hxargs https://github.com/Simn/hxargs.git + haxelib git markdown https://github.com/dpeek/haxe-markdown.git + haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp.git + cd $(haxelib libpath hxcpp)/tools/hxcpp + haxe compile.hxml + cd - + haxe dox.hxml + mkdir resources + cp ../../src-json/* resources + cpp/Dox -i ../../xmldoc -ex microsoft -ex javax -ex cs.internal -theme $(haxelib libpath dox)/themes/default + working-directory: ${{github.workspace}}/tests/docgen + + linux-arm64: + runs-on: ubuntu-20.04 + permissions: + packages: write + env: + FORCE_COLOR: 1 + steps: + - name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Earthly + run: sudo /bin/sh -c 'wget https://github.com/earthly/earthly/releases/download/v0.6.13/earthly-linux-amd64 -O /usr/local/bin/earthly && chmod +x /usr/local/bin/earthly && /usr/local/bin/earthly bootstrap --with-autocomplete' + + - name: Set up QEMU + id: qemu + uses: docker/setup-qemu-action@v1 + with: + image: tonistiigi/binfmt:latest + platforms: all + + - uses: actions/checkout@main + with: + submodules: recursive + + - name: Set CONTAINER_ vars + run: | + echo "CONTAINER_REG=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV; + echo "CONTAINER_TAG=$(echo ${{ github.ref_name }} | sed -e 's/[^A-Za-z0-9\.]/-/g')" >> $GITHUB_ENV; + + - name: Build devcontainer + run: earthly --platform=linux/arm64 +devcontainer --IMAGE_NAME="ghcr.io/${CONTAINER_REG}_devcontainer" --IMAGE_TAG="${CONTAINER_TAG}-arm64" --IMAGE_CACHE="ghcr.io/haxefoundation/haxe_devcontainer:development-arm64" + env: + EARTHLY_PUSH: "${{ github.event_name == 'push' }}" + EARTHLY_USE_INLINE_CACHE: true + EARTHLY_SAVE_INLINE_CACHE: true + + - name: Set ADD_REVISION=1 for non-release + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + run: echo "ADD_REVISION=1" >> $GITHUB_ENV + + - name: Build + run: earthly --platform=linux/arm64 +build --ADD_REVISION="$ADD_REVISION" --SET_SAFE_DIRECTORY="true" + env: + EARTHLY_PUSH: "${{ github.event_name == 'push' }}" + EARTHLY_REMOTE_CACHE: "ghcr.io/${{env.CONTAINER_REG}}_cache:build-${{env.CONTAINER_TAG}}-arm64" + - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: - name: linuxBinaries - path: out - + name: linuxArm64Binaries + path: out/linux/arm64 mac-build: runs-on: macos-latest @@ -343,11 +567,17 @@ jobs: with: submodules: recursive - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: ~/.opam/ + key: ${{ runner.os }}-${{ hashFiles('./opam', './libs/') }}-2 + + - name: Install Neko from S3 run: | set -ex - + curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` @@ -357,25 +587,16 @@ jobs: sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} - shell: pwsh - run: | - Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip - Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP - $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* - echo "$NEKOPATH" >> $env:GITHUB_PATH - echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + - name: Install dependencies env: - ZLIB_VERSION: 1.2.11 + # For compatibility with macOS 10.13 + ZLIB_VERSION: 1.2.13 MBEDTLS_VERSION: 2.25.0 - PCRE_VERSION: 10.39 + PCRE2_VERSION: 10.42 run: | set -ex brew uninstall openssl@1.0.2t || echo @@ -385,6 +606,7 @@ jobs: brew update # brew unlink python@2 brew bundle --file=tests/Brewfile --no-upgrade || brew link --overwrite awscli + brew install libunistring brew install cpanminus cpanm IPC::System::Simple cpanm String::ShellQuote @@ -392,16 +614,20 @@ jobs: cd zlib-$ZLIB_VERSION ./configure make && make install + cd .. curl -L https://github.com/ARMmbed/mbedtls/archive/v$MBEDTLS_VERSION.tar.gz | tar xz cd mbedtls-$MBEDTLS_VERSION make && make install - curl -L https://github.com/PhilipHazel/pcre2/releases/download/pcre2-10.39/pcre2-$PCRE_VERSION.tar.gz | tar xz - cd pcre2-$PCRE_VERSION - ./configure --enable-utf8 --enable-pcre2-8 --enable-pcre2-16 --enable-pcre2-32 --enable-unicode-properties --enable-pcregrep-libz --enable-pcregrep-libbz2 --enable-jit + cd .. + curl -L https://github.com/PCRE2Project/pcre2/releases/download/pcre2-$PCRE2_VERSION/pcre2-$PCRE2_VERSION.tar.gz | tar xz + cd pcre2-$PCRE2_VERSION + ./configure --enable-unicode --enable-pcre2-8 --enable-pcre2-16 --enable-pcre2-32 --enable-unicode-properties --enable-pcre2grep-libz --enable-pcre2grep-libbz2 --enable-jit make && make install - - + cd .. + + - name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' run: | set -ex opam init # --disable-sandboxing @@ -414,28 +640,28 @@ jobs: opam install haxe --deps-only --assume-depexts opam list ocamlopt -v - + - name: Set ADD_REVISION=1 for non-release if: ${{ !startsWith(github.ref, 'refs/tags/') }} run: echo "ADD_REVISION=1" >> $GITHUB_ENV - + - name: Build Haxe run: | set -ex eval $(opam env) - opam config exec -- make -s -j`sysctl -n hw.ncpu` STATICLINK=1 "LIB_PARAMS=/usr/local/lib/libz.a /usr/local/lib/libpcre.a /usr/local/lib/libmbedtls.a /usr/local/lib/libmbedcrypto.a /usr/local/lib/libmbedx509.a -cclib '-framework Security -framework CoreFoundation'" haxe + opam config exec -- make -s -j`sysctl -n hw.ncpu` STATICLINK=1 "LIB_PARAMS=/usr/local/lib/libz.a /usr/local/lib/libpcre2-8.a /usr/local/lib/libmbedtls.a /usr/local/lib/libmbedcrypto.a /usr/local/lib/libmbedx509.a -cclib '-framework Security -framework CoreFoundation'" haxe opam config exec -- make -s haxelib make -s package_unix package_installer_mac ls -l out otool -L ./haxe otool -L ./haxelib - + - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: name: macBinaries path: out - + windows64-test: needs: windows64-build @@ -449,8 +675,8 @@ jobs: strategy: fail-fast: false matrix: - # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/5024 - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, neko] + # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/10919 + target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, flash, neko] steps: - uses: actions/checkout@main with: @@ -459,23 +685,7 @@ jobs: with: name: win${{env.ARCH}}Binaries - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} - run: | - set -ex - - curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz - tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP - NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` - sudo mkdir -p /usr/local/bin - sudo mkdir -p /usr/local/lib/neko - sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ - sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ - sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ - echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} + - name: Install Neko from S3 shell: pwsh run: | Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip @@ -483,17 +693,17 @@ jobs: $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* echo "$NEKOPATH" >> $env:GITHUB_PATH echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + # - name: Quick test # shell: pwsh # run: | # $DOWNLOADDIR="./win$($env:ARCH)Binaries" # new-item -Name $DOWNLOADDIR -ItemType directory # Invoke-WebRequest https://build.haxe.org/builds/haxe/$env:PLATFORM/haxe_latest.zip -OutFile $DOWNLOADDIR/haxe_bin.zip - + - name: Setup Haxe shell: pwsh run: | @@ -503,11 +713,11 @@ jobs: $HAXEPATH = Get-ChildItem $DOWNLOADDIR/haxe_*_* -Directory Write-Host "::add-path::$HAXEPATH" Write-Host "::set-env name=HAXELIB_ROOT::$HAXEPATH\lib" - + - name: Print Haxe version shell: pwsh run: haxe -version - + - name: "Make Python 3 be available as python3 in the cmdline" shell: pwsh run: | @@ -516,7 +726,7 @@ jobs: $py3path = $pypath.replace("python.exe","python3.exe") cmd /c mklink $py3path $pypath python3 -V - + - name: Install hererocks if: matrix.target == 'lua' shell: cmd @@ -524,18 +734,25 @@ jobs: pip install hererocks hererocks lua53 -l5.3 -rlatest call lua53/bin/activate - + + - name: Install wget + if: matrix.target == 'flash' + shell: cmd + run: | + choco install wget + wget --version + - name: Setup haxelib shell: pwsh run: | mkdir "$env:HAXELIB_ROOT" haxelib setup "$env:HAXELIB_ROOT" - + - name: Test shell: pwsh run: haxe RunCi.hxml working-directory: ${{github.workspace}}/tests - + windows-test: needs: windows-build @@ -550,8 +767,8 @@ jobs: fail-fast: false matrix: # TODO jvm: https://github.com/HaxeFoundation/haxe/issues/8601 - # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/5024 - target: [macro, js, hl, cpp, java, cs, php, python, neko] + # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/10919 + target: [macro, js, hl, cpp, java, cs, php, python, flash, neko] steps: - uses: actions/checkout@main with: @@ -560,23 +777,7 @@ jobs: with: name: win${{env.ARCH}}Binaries - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} - run: | - set -ex - - curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz - tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP - NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` - sudo mkdir -p /usr/local/bin - sudo mkdir -p /usr/local/lib/neko - sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ - sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ - sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ - echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} + - name: Install Neko from S3 shell: pwsh run: | Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip @@ -584,17 +785,17 @@ jobs: $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* echo "$NEKOPATH" >> $env:GITHUB_PATH echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + # - name: Quick test # shell: pwsh # run: | # $DOWNLOADDIR="./win$($env:ARCH)Binaries" # new-item -Name $DOWNLOADDIR -ItemType directory # Invoke-WebRequest https://build.haxe.org/builds/haxe/$env:PLATFORM/haxe_latest.zip -OutFile $DOWNLOADDIR/haxe_bin.zip - + - name: Setup Haxe shell: pwsh run: | @@ -604,11 +805,11 @@ jobs: $HAXEPATH = Get-ChildItem $DOWNLOADDIR/haxe_*_* -Directory Write-Host "::add-path::$HAXEPATH" Write-Host "::set-env name=HAXELIB_ROOT::$HAXEPATH\lib" - + - name: Print Haxe version shell: pwsh run: haxe -version - + - name: "Make Python 3 be available as python3 in the cmdline" shell: pwsh run: | @@ -617,7 +818,7 @@ jobs: $py3path = $pypath.replace("python.exe","python3.exe") cmd /c mklink $py3path $pypath python3 -V - + - name: Install hererocks if: matrix.target == 'lua' shell: cmd @@ -625,126 +826,25 @@ jobs: pip install hererocks hererocks lua53 -l5.3 -rlatest call lua53/bin/activate - + + - name: Install wget + if: matrix.target == 'flash' + shell: cmd + run: | + choco install wget + wget --version + - name: Setup haxelib shell: pwsh run: | mkdir "$env:HAXELIB_ROOT" haxelib setup "$env:HAXELIB_ROOT" - + - name: Test shell: pwsh run: haxe RunCi.hxml working-directory: ${{github.workspace}}/tests - - - linux-test: - needs: linux-build - runs-on: ubuntu-18.04 - env: - PLATFORM: linux64 - TEST: ${{matrix.target}} - HXCPP_COMPILE_CACHE: ~/hxcache - strategy: - fail-fast: false - matrix: - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, lua, neko] #flash9 - include: - - target: js - # SAUCE: 1 - # SAUCE_TUNNEL_ID: 'linux-test' - # SAUCE_BUILD: '${{env.GITHUB_RUN_NUMBER}}' # $(Build.BuildNumber) - - target: hl - APT_PACKAGES: cmake ninja-build libturbojpeg-dev - - target: cpp - APT_PACKAGES: gcc-multilib g++-multilib - - target: lua - APT_PACKAGES: ncurses-dev - # - target: flash9 - # APT_PACKAGES: libglib2.0-0 libfreetype6 xvfb - steps: - - uses: actions/checkout@main - with: - submodules: recursive - - uses: actions/download-artifact@v1 - with: - name: linuxBinaries - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} - run: | - set -ex - - curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz - tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP - NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` - sudo mkdir -p /usr/local/bin - sudo mkdir -p /usr/local/lib/neko - sudo ln -s $NEKOPATH/{neko,nekoc,nekoml,nekotools} /usr/local/bin/ - sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ - sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ - echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} - shell: pwsh - run: | - Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip - Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP - $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* - echo "$NEKOPATH" >> $env:GITHUB_PATH - echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - - - name: Print Neko version - run: neko -version 2>&1 - - - name: Setup Haxe - run: | - # mkdir ./linuxBinaries - # curl -sSL https://build.haxe.org/builds/haxe/linux64/haxe_latest.tar.gz -o ./linuxBinaries/haxe_bin.tar.gz - - sudo apt install -qqy libmbedtls-dev - - set -ex - tar -xf linuxBinaries/*_bin.tar.gz -C linuxBinaries --strip-components=1 - sudo mkdir -p /usr/local/bin/ - sudo mkdir -p /usr/local/share/haxe/ - sudo ln -s `pwd`/linuxBinaries/haxe /usr/local/bin/haxe - sudo ln -s `pwd`/linuxBinaries/haxelib /usr/local/bin/haxelib - sudo ln -s `pwd`/linuxBinaries/std /usr/local/share/haxe/std - - - name: Print Haxe version - run: haxe -version - - - name: Setup haxelib - run: | - set -ex - mkdir ~/haxelib - haxelib setup ~/haxelib - - - name: Install apt packages - if: matrix.APT_PACKAGES - run: | - set -ex - sudo apt update -qqy - sudo apt install -qqy ${{matrix.APT_PACKAGES}} - - # - name: Flash setup - # if: matrix.target == 'flash9' - # run: export DISPLAY=:99.0 - - - name: Test - if: success() && !(matrix.SAUCE && matrix.SAUCE_ACCESS_KEY) - run: haxe RunCi.hxml - working-directory: ${{github.workspace}}/tests - - - name: Test (with SauceLabs) - if: matrix.target == 'js' && success() && matrix.SAUCE && matrix.SAUCE_ACCESS_KEY - run: haxe RunCi.hxml - working-directory: ${{github.workspace}}/tests - env: - SAUCE_ACCESS_KEY: matrix.SAUCE_ACCESS_KEY - mac-test: needs: mac-build @@ -753,10 +853,11 @@ jobs: PLATFORM: mac TEST: ${{matrix.target}} HXCPP_COMPILE_CACHE: ~/hxcache + HAXE_STD_PATH: /usr/local/share/haxe/std strategy: fail-fast: false matrix: - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, lua, neko] #flash9 + target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, flash, neko] include: - target: hl BREW_PACKAGES: ninja @@ -768,11 +869,10 @@ jobs: with: name: macBinaries - - name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} + - name: Install Neko from S3 run: | set -ex - + curl -sSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o $RUNNER_TEMP/neko_latest.tar.gz tar -xf $RUNNER_TEMP/neko_latest.tar.gz -C $RUNNER_TEMP NEKOPATH=`echo $RUNNER_TEMP/neko-*-*` @@ -782,25 +882,15 @@ jobs: sudo ln -s $NEKOPATH/libneko.* /usr/local/lib/ sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV - - - name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} - shell: pwsh - run: | - Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip - Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP - $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* - echo "$NEKOPATH" >> $env:GITHUB_PATH - echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - + - name: Print Neko version run: neko -version 2>&1 - + - name: Setup Haxe run: | # mkdir ./macBinaries # curl -sSL https://build.haxe.org/builds/haxe/mac/haxe_latest.tar.gz -o ./macBinaries/haxe_bin.tar.gz - + set -ex tar -xf macBinaries/*_bin.tar.gz -C macBinaries --strip-components=1 sudo mkdir -p /usr/local/bin/ @@ -808,32 +898,32 @@ jobs: sudo ln -s `pwd`/macBinaries/haxe /usr/local/bin/haxe sudo ln -s `pwd`/macBinaries/haxelib /usr/local/bin/haxelib sudo ln -s `pwd`/macBinaries/std /usr/local/share/haxe/std - + - name: Print Haxe version run: haxe -version - + - name: Setup haxelib run: | set -ex mkdir ~/haxelib haxelib setup ~/haxelib - + - name: Install homebrew packages if: matrix.BREW_PACKAGES run: brew install ${{matrix.BREW_PACKAGES}} - + - name: Test run: | # disable invalid Unicode filenames on APFS echo "" > sys/compile-fs.hxml haxe RunCi.hxml working-directory: ${{github.workspace}}/tests - + deploy: - if: github.event_name != 'pull_request' - needs: [linux-test, mac-test, windows-test, windows64-test] - runs-on: ubuntu-18.04 + if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' + needs: [linux-test, linux-arm64, mac-test, windows-test, windows64-test] + runs-on: ubuntu-20.04 steps: # this is only needed for to get `COMMIT_DATE`... # maybe https://github.community/t/expose-commit-timestamp-in-the-github-context-data/16460/3 @@ -862,12 +952,14 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} HXBUILDS_S3ADDR: ${{ secrets.HXBUILDS_S3ADDR }} + AWS_EC2_METADATA_DISABLED: true run: | set -ex COMMIT_HASH_SHORT=${GITHUB_SHA:0:7} COMMIT_DATE=`TZ=UTC git show --quiet --date='format-local:%Y-%m-%d' --format="%cd"` FILE_NAME=haxe_${COMMIT_DATE}_${{ steps.extract_branch.outputs.branch }}_${COMMIT_HASH_SHORT} aws s3 cp linuxBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux64/${FILE_NAME}.tar.gz + aws s3 cp linuxArm64Binaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux-arm64/${FILE_NAME}.tar.gz aws s3 cp macBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac/${FILE_NAME}.tar.gz aws s3 cp macBinaries/*_installer.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac-installer/${FILE_NAME}.tar.gz aws s3 cp win64Binaries/*_bin.zip ${HXBUILDS_S3ADDR}/haxe/windows64/${FILE_NAME}.zip @@ -884,9 +976,11 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} HXBUILDS_S3ADDR: ${{ secrets.HXBUILDS_S3ADDR }} + AWS_EC2_METADATA_DISABLED: true run: | set -ex aws s3 cp linuxBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux64/haxe_latest.tar.gz + aws s3 cp linuxArm64Binaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux-arm64/haxe_latest.tar.gz aws s3 cp macBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac/haxe_latest.tar.gz aws s3 cp macBinaries/*_installer.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac-installer/haxe_latest.tar.gz aws s3 cp win64Binaries/*_bin.zip ${HXBUILDS_S3ADDR}/haxe/windows64/haxe_latest.zip @@ -903,10 +997,15 @@ jobs: aws s3 cp ${HXBUILDS_S3ADDR}/haxe/windows-choco/${FILE_NAME} ${HXBUILDS_S3ADDR}/haxe/windows-choco/haxe_latest.nupkg --acl public-read --website-redirect "${HXBUILDS_S3PATH}/haxe/windows-choco/${FILE_NAME}" deploy_apidoc: - if: github.event_name != 'pull_request' # TODO: also only when `GHP_REMOTE` is present - needs: [linux-test, mac-test, windows-test, windows64-test] - runs-on: ubuntu-18.04 + if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' + needs: [linux-test, linux-arm64, mac-test, windows-test, windows64-test] + runs-on: ubuntu-20.04 steps: + - name: Install dependencies + run: | + set -ex + sudo apt-get install -qqy libc6 + - name: Download Haxe uses: actions/download-artifact@v2 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f4c8a0c..a4d15fdd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,63 +10,13 @@ This repository is about the Haxe compiler itself and the Haxe standard library. Here's an overview of repositories that are part of the Haxe ecosystem: * The haxelib command line tool or lib.haxe.org: -* Something on try.haxe.org: +* Something on try.haxe.org: * Something under haxe.org/manual: * Something on api.haxe.org: For content this is probably the right repository. If it's about the representation, try instead. * Something else on haxe.org: -## Submitting a Pull-Request - -Thank you for your interest in contributing to Haxe! Haxe is a -community-driven project and your help is vital and appreciated! - -When preparing to submit a pull-request, please make your PR as easy -as possible for core devs to evaluate and merge. To that end: - - * In your PR comments, include: - - * the reason for your proposed changes (What problem are you fixing?) - * some possible solutions, and rationale for the one you chose - * a summary of the code changes in your PR - * any pros and cons to note about the solution you implemented - * links to any relevant GitHub issues, PR's, and/or forum - discussions - - * If you've found and fixed a bug, have you also included a - corresponding test for it? - * Does your code formatting match that of the rest of the project? - * If your changes require updates to the documentation, does your PR - include those as well? - -Please also bear the following in mind: - - * Evaluating PR's takes time and effort. Even taking a look at a PR - in order to request more info or clarification is not zero-cost. - * Most members of the core team are volunteers too, and at any given time - are typically already busy working on other areas of Haxe. - * It's no fun providing negative feedback to a PR. The better you - can craft and champion your PR, the more likely it is to be - speedily evaluated. - - -## Debugging Hints - -### Using a debugger - -To debug the Haxe compiler, you can use either a system debugger (`gdb`/`lldb`), or [ocamldebug](http://caml.inria.fr/pub/docs/manual-ocaml/debugger.html). `ocamldebug` provides a better debugging experience. To use it, uncomment `(modes byte)` from [src/dune](src/dune) and recompile. - -### Using printf - -To print information about a type, you can add the following before most lines: - -```ocaml -Printf.printf "%s\n" (s_type_kind t); -``` - -There are lots of other stringifying functions, search for "Printing" in `src/core/type.ml` and scroll down to find them. - ## Other remarks: - Sometimes people try to be particularly helpful by not only including broken parts in their code, but also "similar" code which is working. More often than not this is more distracting than helpful. If you want to highlight something like this, consider adding the working code commented out. - We do not require a classic "What do you see/what do you expect?" form, but in some cases it is hard to figure out where you think the actual problem is otherwise. -- We're keeping this page quite short so there's a higher chance that people actually read it. +- We're keeping this page quite short so there's a higher chance that people actually read it. \ No newline at end of file diff --git a/Earthfile b/Earthfile new file mode 100644 index 00000000..123f491b --- /dev/null +++ b/Earthfile @@ -0,0 +1,421 @@ +VERSION 0.6 +FROM mcr.microsoft.com/vscode/devcontainers/base:0-bionic +ARG DEVCONTAINER_IMAGE_NAME_DEFAULT=ghcr.io/haxefoundation/haxe_devcontainer + +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +ARG WORKDIR=/workspace +RUN mkdir -m 777 "$WORKDIR" +WORKDIR "$WORKDIR" + +ARG --required TARGETARCH + +devcontainer-library-scripts: + RUN curl -fsSLO https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/common-debian.sh + RUN curl -fsSLO https://raw.githubusercontent.com/microsoft/vscode-dev-containers/main/script-library/docker-debian.sh + SAVE ARTIFACT --keep-ts *.sh AS LOCAL .devcontainer/library-scripts/ + +devcontainer: + # Avoid warnings by switching to noninteractive + ENV DEBIAN_FRONTEND=noninteractive + + ARG INSTALL_ZSH="false" + ARG UPGRADE_PACKAGES="true" + ARG ENABLE_NONROOT_DOCKER="true" + ARG USE_MOBY="false" + COPY .devcontainer/library-scripts/common-debian.sh .devcontainer/library-scripts/docker-debian.sh /tmp/library-scripts/ + RUN apt-get update \ + && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \ + && /bin/bash /tmp/library-scripts/docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "/var/run/docker-host.sock" "/var/run/docker.sock" "${USERNAME}" "${USE_MOBY}" \ + # Clean up + && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts/ + + # Setting the ENTRYPOINT to docker-init.sh will configure non-root access + # to the Docker socket. The script will also execute CMD as needed. + ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] + CMD [ "sleep", "infinity" ] + + # Configure apt and install packages + RUN apt-get update \ + && apt-get install -qqy --no-install-recommends apt-utils dialog 2>&1 \ + && apt-get install -qqy --no-install-recommends \ + iproute2 \ + procps \ + sudo \ + bash-completion \ + build-essential \ + curl \ + wget \ + software-properties-common \ + direnv \ + tzdata \ + # install docker engine for using `WITH DOCKER` + docker-ce \ + # install node + && curl -sL https://deb.nodesource.com/setup_16.x | bash - \ + && apt-get install -qqy --no-install-recommends nodejs=16.* \ + # install ocaml and other haxe compiler deps + && add-apt-repository ppa:avsm/ppa \ + && add-apt-repository ppa:haxe/ocaml \ + && apt-get install -qqy --no-install-recommends \ + ocaml-nox \ + camlp5 \ + opam \ + libpcre2-dev \ + zlib1g-dev \ + libgtk2.0-dev \ + libmbedtls-dev \ + ninja-build \ + libstring-shellquote-perl \ + libipc-system-simple-perl \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + + # Switch back to dialog for any ad-hoc use of apt-get + ENV DEBIAN_FRONTEND= + + DO +INSTALL_NEKO + + COPY +earthly/earthly /usr/local/bin/ + RUN earthly bootstrap --no-buildkit --with-autocomplete + + USER $USERNAME + + # Do not show git branch in bash prompt because it's slow + # https://github.com/microsoft/vscode-dev-containers/issues/1196#issuecomment-988388658 + RUN git config --global codespaces-theme.hide-status 1 + + # Install OCaml libraries + COPY opam . + RUN opam init --disable-sandboxing + RUN opam install . --yes --deps-only --no-depexts + RUN opam list + RUN ocamlopt -v + + USER root + + ARG IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME_DEFAULT" + ARG IMAGE_TAG="development" + ARG IMAGE_CACHE="$IMAGE_NAME:$IMAGE_TAG" + SAVE IMAGE --cache-from="$IMAGE_CACHE" --push "$IMAGE_NAME:$IMAGE_TAG" + +devcontainer-multiarch-amd64: + ARG IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME_DEFAULT" + ARG IMAGE_TAG="development" + FROM --platform=linux/amd64 +devcontainer --IMAGE_NAME="$IMAGE_NAME" --IMAGE_TAG="$IMAGE_TAG-amd64" + SAVE IMAGE --push "$IMAGE_NAME:$IMAGE_TAG" + +devcontainer-multiarch-arm64: + ARG IMAGE_NAME="$DEVCONTAINER_IMAGE_NAME_DEFAULT" + ARG IMAGE_TAG="development" + FROM --platform=linux/arm64 +devcontainer --IMAGE_NAME="$IMAGE_NAME" --IMAGE_TAG="$IMAGE_TAG-arm64" + SAVE IMAGE --push "$IMAGE_NAME:$IMAGE_TAG" + +devcontainer-multiarch: + BUILD +devcontainer-multiarch-amd64 + BUILD +devcontainer-multiarch-arm64 + +# Usage: +# COPY +earthly/earthly /usr/local/bin/ +# RUN earthly bootstrap --no-buildkit --with-autocomplete +earthly: + ARG --required TARGETARCH + RUN curl -fsSL https://github.com/earthly/earthly/releases/download/v0.6.13/earthly-linux-${TARGETARCH} -o /usr/local/bin/earthly \ + && chmod +x /usr/local/bin/earthly + SAVE ARTIFACT /usr/local/bin/earthly + +INSTALL_PACKAGES: + COMMAND + ARG PACKAGES + RUN apt-get update -qqy && \ + apt-get install -qqy --no-install-recommends $PACKAGES && \ + apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* + +INSTALL_NEKO: + COMMAND + ARG NEKOPATH=/neko + COPY +neko/* "$NEKOPATH/" + ARG PREFIX=/usr/local + RUN bash -c "ln -s \"$NEKOPATH\"/{neko,nekoc,nekoml,nekotools} \"$PREFIX/bin/\"" + RUN bash -c "ln -s \"$NEKOPATH\"/libneko.* \"$PREFIX/lib/\"" + RUN mkdir -p "$PREFIX/lib/neko/" + RUN bash -c "ln -s \"$NEKOPATH\"/*.ndll \"$PREFIX/lib/neko/\"" + RUN ldconfig + +INSTALL_HAXE: + COMMAND + ARG PREFIX=/usr/local + COPY +build/haxe +build/haxelib "$PREFIX/bin/" + COPY std "$PREFIX/share/haxe/std" + +try-neko: + DO +INSTALL_NEKO + RUN neko -version + RUN nekotools + +try-haxe: + DO +INSTALL_NEKO + DO +INSTALL_HAXE + RUN haxe --version + RUN haxelib version + +neko: + RUN set -ex && \ + case "$TARGETARCH" in \ + amd64) PLATFORM=linux64;; \ + arm64) PLATFORM=linux-arm64;; \ + *) exit 1;; \ + esac && \ + curl -fsSL https://build.haxe.org/builds/neko/$PLATFORM/neko_latest.tar.gz -o neko_latest.tar.gz && \ + tar -xf neko_latest.tar.gz && \ + mv `echo neko-*-*` /tmp/neko-unpacked + SAVE ARTIFACT /tmp/neko-unpacked/* + SAVE IMAGE --cache-hint + +build: + FROM +devcontainer + + USER $USERNAME + + # Build Haxe + COPY --dir extra libs plugins src* std dune* Makefile* . + + # the Makefile calls git to get commit sha + COPY .git .git + ARG SET_SAFE_DIRECTORY="false" + IF [ "$SET_SAFE_DIRECTORY" = "true" ] + RUN git config --global --add safe.directory "$WORKDIR" + END + + ARG ADD_REVISION + ENV ADD_REVISION=$ADD_REVISION + RUN opam config exec -- make -s -j`nproc` STATICLINK=1 haxe && ldd -v ./haxe + RUN opam config exec -- make -s haxelib && ldd -v ./haxelib + RUN make -s package_unix && ls -l out + + ARG TARGETPLATFORM + SAVE ARTIFACT --keep-ts ./out/* AS LOCAL out/$TARGETPLATFORM/ + SAVE ARTIFACT --keep-ts ./haxe AS LOCAL out/$TARGETPLATFORM/ + SAVE ARTIFACT --keep-ts ./haxelib AS LOCAL out/$TARGETPLATFORM/ + SAVE IMAGE --cache-hint + +build-multiarch: + ARG ADD_REVISION + BUILD --platform=linux/amd64 --platform=linux/arm64 +build --ADD_REVISION=$ADD_REVISION + +xmldoc: + DO +INSTALL_NEKO + DO +INSTALL_HAXE + + COPY --dir extra . + + WORKDIR extra + RUN haxelib newrepo + RUN haxelib git hxcpp https://github.com/HaxeFoundation/hxcpp + RUN haxelib git hxjava https://github.com/HaxeFoundation/hxjava + RUN haxelib git hxcs https://github.com/HaxeFoundation/hxcs + RUN haxe doc.hxml + + ARG COMMIT + ARG BRANCH + RUN echo "{\"commit\":\"$COMMIT\",\"branch\":\"$BRANCH\"}" > doc/info.json + + SAVE ARTIFACT --keep-ts ./doc AS LOCAL extra/doc + +test-environment: + # we use a sightly newer ubuntu for easier installation of the target runtimes (e.g. php) + FROM ubuntu:focal + DO +INSTALL_NEKO + DO +INSTALL_HAXE + + ENV DEBIAN_FRONTEND=noninteractive + DO +INSTALL_PACKAGES --PACKAGES="ca-certificates curl wget git build-essential locales sqlite3" + + # Node.js is required as there are tests that use it (search "-cmd node") + RUN curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \ + apt-get install -qqy nodejs && \ + apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* + + # set locale + RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen + ENV LANG=en_US.UTF-8 + ENV LANGUAGE=en_US:en + ENV LC_ALL=en_US.UTF-8 + + SAVE IMAGE --cache-hint + +test-environment-java: + FROM +test-environment + DO +INSTALL_PACKAGES --PACKAGES="default-jdk" + SAVE IMAGE --cache-hint + +test-environment-js: + # somehow js tests require hxjava which in turns require javac + FROM +test-environment-java + +test-environment-python: + FROM +test-environment + DO +INSTALL_PACKAGES --PACKAGES="python3" + SAVE IMAGE --cache-hint + +test-environment-php: + FROM +test-environment + DO +INSTALL_PACKAGES --PACKAGES="php-cli php-mbstring php-sqlite3" + SAVE IMAGE --cache-hint + +test-environment-cs: + FROM +test-environment + DO +INSTALL_PACKAGES --PACKAGES="mono-devel mono-mcs" + SAVE IMAGE --cache-hint + +test-environment-hl: + FROM +test-environment + DO +INSTALL_PACKAGES --PACKAGES="cmake ninja-build libturbojpeg-dev libpng-dev zlib1g-dev libvorbis-dev libsqlite3-dev" + SAVE IMAGE --cache-hint + +test-environment-lua: + # hererocks uses pip + FROM +test-environment-python + DO +INSTALL_PACKAGES --PACKAGES="libssl-dev libreadline-dev python3-pip unzip libpcre2-dev cmake" + RUN ln -s /root/.local/bin/hererocks /bin/ + SAVE IMAGE --cache-hint + +test-environment-cpp: + FROM +test-environment + + ARG TARGETPLATFORM + + IF [ "$TARGETPLATFORM" = "linux/amd64" ] + DO +INSTALL_PACKAGES --PACKAGES="g++-multilib" + ELSE IF [ "$TARGETPLATFORM" = "linux/arm64" ] + DO +INSTALL_PACKAGES --PACKAGES="g++-multilib-arm-linux-gnueabi" + ELSE + RUN echo "Unsupported platform $TARGETPLATFORM" && exit 1 + END + + SAVE IMAGE --cache-hint + +test-environment-flash: + # apache flex requires java + FROM +test-environment-java + # requirements for running flash player + DO +INSTALL_PACKAGES --PACKAGES="libglib2.0-0 libfreetype6 xvfb libxcursor1 libnss3 libgtk2.0-0" + SAVE IMAGE --cache-hint + +RUN_CI: + COMMAND + COPY tests tests + RUN mkdir /haxelib && haxelib setup /haxelib + WORKDIR tests + ARG --required TEST + ENV TEST="$TEST" + RUN haxe RunCi.hxml + +test-macro: + FROM +test-environment + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=macro + +test-neko: + FROM +test-environment + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=neko + +test-js: + FROM +test-environment-js + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=js + +test-hl: + FROM +test-environment-hl + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=hl + +test-cpp: + FROM +test-environment-cpp + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=cpp + +test-java: + FROM +test-environment-java + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=java + +test-jvm: + FROM +test-environment-java + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=jvm + +test-cs: + FROM +test-environment-cs + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=cs + +test-php: + FROM +test-environment-php + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=php + +test-python: + FROM +test-environment-python + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=python + +test-lua: + FROM +test-environment-lua + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=lua + +test-flash: + FROM +test-environment-flash + ARG GITHUB_ACTIONS + ENV GITHUB_ACTIONS=$GITHUB_ACTIONS + DO +RUN_CI --TEST=flash + +test-all: + ARG TARGETPLATFORM + + BUILD +test-macro + BUILD +test-neko + BUILD +test-php + BUILD +test-python + BUILD +test-java + BUILD +test-jvm + BUILD +test-cs + BUILD +test-cpp + BUILD +test-lua + BUILD +test-js + BUILD +test-flash + + IF [ "$TARGETPLATFORM" = "linux/amd64" ] + BUILD +test-hl # FIXME: hl can't compile on arm64 (JIT issue?) + END + +github-actions: + DO +INSTALL_NEKO + DO +INSTALL_HAXE + RUN mkdir -p "$WORKDIR"/.github/workflows + COPY extra/github-actions extra/github-actions + WORKDIR extra/github-actions + RUN haxe build.hxml + SAVE ARTIFACT --keep-ts "$WORKDIR"/.github/workflows AS LOCAL .github/workflows + +ghcr-login: + LOCALLY + RUN echo "$GITHUB_CR_PAT" | docker login ghcr.io -u "$GITHUB_USERNAME" --password-stdin diff --git a/Makefile b/Makefile index 7f5268e9..a369415b 100644 --- a/Makefile +++ b/Makefile @@ -59,10 +59,14 @@ PACKAGE_FILE_NAME=haxe_$(COMMIT_DATE)_$(COMMIT_SHA) HAXE_VERSION=$(shell $(CURDIR)/$(HAXE_OUTPUT) -version 2>&1 | awk '{print $$1;}') HAXE_VERSION_SHORT=$(shell echo "$(HAXE_VERSION)" | grep -oE "^[0-9]+\.[0-9]+\.[0-9]+") +NEKO_VERSION=2.3.0 +NEKO_MAJOR_VERSION=$(shell echo "$(NEKO_VERSION)" | grep -oE "^[0-9]+") +NEKO_VERSION_TAG=v$(shell echo "$(NEKO_VERSION)" | sed "s/\./-/g") + ifneq ($(STATICLINK),0) - LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre -lz -lmbedtls -lmbedx509 -lmbedcrypto -Wl,-Bdynamic ' + LIB_PARAMS= -cclib '-Wl,-Bstatic -lpcre2-8 -lz -lmbedtls -lmbedx509 -lmbedcrypto -Wl,-Bdynamic ' else - LIB_PARAMS?= -cclib -lpcre -cclib -lz -cclib -lmbedtls -cclib -lmbedx509 -cclib -lmbedcrypto + LIB_PARAMS?= -cclib -lpcre2-8 -cclib -lz -cclib -lmbedtls -cclib -lmbedx509 -cclib -lmbedcrypto endif ifeq ($(SYSTEM_NAME),Mac) LIB_PARAMS+= -cclib '-framework Security -framework CoreFoundation' @@ -165,7 +169,7 @@ $(INSTALLER_TMP_DIR): mkdir -p $(INSTALLER_TMP_DIR) $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz: $(INSTALLER_TMP_DIR) - wget -nv https://github.com/HaxeFoundation/neko/releases/download/v2-3-0/neko-2.3.0-osx64.tar.gz -O installer/neko-osx64.tar.gz + wget -nv https://github.com/HaxeFoundation/neko/releases/download/$(NEKO_VERSION_TAG)/neko-$(NEKO_VERSION)-osx64.tar.gz -O installer/neko-osx64.tar.gz # Installer @@ -173,7 +177,6 @@ package_installer_mac: $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz package_unix $(eval OUTFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.tar.gz) $(eval PACKFILE := $(shell pwd)/$(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_bin.tar.gz) $(eval VERSION := $(shell $(CURDIR)/$(HAXE_OUTPUT) -version 2>&1)) - $(eval NEKOVER := $(shell neko -version 2>&1)) bash -c "rm -rf $(INSTALLER_TMP_DIR)/{resources,pkg,tgz,haxe.tar.gz}" mkdir $(INSTALLER_TMP_DIR)/resources # neko - unpack to change the dir name @@ -186,6 +189,8 @@ package_installer_mac: $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz package_unix cd $(INSTALLER_TMP_DIR)/resources && tar -zcvf haxe.tar.gz haxe # scripts cp -rf extra/mac-installer/* $(INSTALLER_TMP_DIR)/resources + sed -i '' 's/%%NEKO_VERSION%%/$(NEKO_VERSION)/g' $(INSTALLER_TMP_DIR)/resources/scripts/neko-postinstall.sh + sed -i '' 's/%%NEKO_MAJOR_VERSION%%/$(NEKO_MAJOR_VERSION)/g' $(INSTALLER_TMP_DIR)/resources/scripts/neko-postinstall.sh cd $(INSTALLER_TMP_DIR)/resources && tar -zcvf scripts.tar.gz scripts # installer structure mkdir -p $(INSTALLER_TMP_DIR)/pkg @@ -201,12 +206,12 @@ package_installer_mac: $(INSTALLER_TMP_DIR)/neko-osx64.tar.gz package_unix sed -i '' 's/%%VERSION%%/$(VERSION)/g' PackageInfo ;\ sed -i '' 's/%%VERSTRING%%/$(VERSION)/g' PackageInfo ;\ sed -i '' 's/%%VERLONG%%/$(VERSION)/g' PackageInfo ;\ - sed -i '' 's/%%NEKOVER%%/$(NEKOVER)/g' PackageInfo ;\ + sed -i '' 's/%%NEKOVER%%/$(NEKO_VERSION)/g' PackageInfo ;\ cd .. ;\ sed -i '' 's/%%VERSION%%/$(VERSION)/g' Distribution ;\ sed -i '' 's/%%VERSTRING%%/$(VERSION)/g' Distribution ;\ sed -i '' 's/%%VERLONG%%/$(VERSION)/g' Distribution ;\ - sed -i '' 's/%%NEKOVER%%/$(NEKOVER)/g' Distribution ;\ + sed -i '' 's/%%NEKOVER%%/$(NEKO_VERSION)/g' Distribution ;\ sed -i '' 's/%%INSTKB%%/$$INSTKBH/g' Distribution" # repackage cd $(INSTALLER_TMP_DIR)/pkg; xar --compression none -cf ../$(PACKAGE_FILE_NAME).pkg * diff --git a/Makefile.win b/Makefile.win index 67e1a97a..9c77a799 100644 --- a/Makefile.win +++ b/Makefile.win @@ -43,12 +43,12 @@ CC_CMD=($(COMPILER) $(ALL_CFLAGS) -c $< 2>tmp.cmi && $(FILTER)) || ($(FILTER) && endif ifeq ($(STATICLINK),0) - LIB_PARAMS = -cclib -lpcre -cclib -lz -cclib -lcrypt32 -cclib -lmbedtls -cclib -lmbedcrypto -cclib -lmbedx509 + LIB_PARAMS = -cclib -lpcre2-8 -cclib -lz -cclib -lcrypt32 -cclib -lmbedtls -cclib -lmbedcrypto -cclib -lmbedx509 endif PACKAGE_FILES=$(HAXE_OUTPUT) $(HAXELIB_OUTPUT) std \ "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep zlib1.dll | sed -e 's/^\s*//')" \ - "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libpcre-1.dll | sed -e 's/^\s*//')" \ + "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libpcre2-8-0.dll | sed -e 's/^\s*//')" \ "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedcrypto.dll | sed -e 's/^\s*//')" \ "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedtls.dll | sed -e 's/^\s*//')" \ "$$(cygcheck $(CURDIR)/$(HAXE_OUTPUT) | grep libmbedx509.dll | sed -e 's/^\s*//')" @@ -75,13 +75,15 @@ package_choco: mkdir -p OUTPUT 7z x -y out/$(PACKAGE_FILE_NAME)_bin.zip -oout > log.txt || type log.txt mv out/$(PACKAGE_FILE_NAME) out/choco - sed -e 's/@SNAPSHOT_VERSION@/$(HAXE_VERSION_SHORT)-SNAP$(COMMIT_DATE)/g' extra/choco/haxe.nuspec > out/choco/haxe.nuspec + cp extra/choco/haxe.nuspec out/choco/haxe.nuspec + sed -i 's/@SNAPSHOT_VERSION@/$(HAXE_VERSION_SHORT)-SNAP$(COMMIT_DATE)/g' out/choco/haxe.nuspec + sed -i 's/@NEKO_VERSION@/$(NEKO_VERSION)/g' out/choco/haxe.nuspec cd out/choco && choco pack mv out/choco/haxe.*.nupkg out rm -rf out/choco $(INSTALLER_TMP_DIR)/neko-win.zip: $(INSTALLER_TMP_DIR) - wget -nv https://github.com/HaxeFoundation/neko/releases/download/v2-3-0/neko-2.3.0-win$(NEKO_ARCH_STR).zip -O installer/neko-win.zip + wget -nv https://github.com/HaxeFoundation/neko/releases/download/$(NEKO_VERSION_TAG)/neko-$(NEKO_VERSION)-win$(NEKO_ARCH_STR).zip -O installer/neko-win.zip package_installer_win: $(INSTALLER_TMP_DIR)/neko-win.zip package_win $(eval OUTFILE := $(PACKAGE_OUT_DIR)/$(PACKAGE_FILE_NAME)_installer.zip) @@ -104,6 +106,7 @@ package_installer_win: $(INSTALLER_TMP_DIR)/neko-win.zip package_win sed -i "s/%%VERSION%%/$(HAXE_VERSION_SHORT)/g" $(INSTALLER_TMP_DIR)/installer.nsi sed -i "s/%%VERSTRING%%/$(HAXE_VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi sed -i "s/%%VERLONG%%/$(HAXE_VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi + sed -i "s/%%NEKO_VERSION%%/$(NEKO_VERSION)/g" $(INSTALLER_TMP_DIR)/installer.nsi cd $(INSTALLER_TMP_DIR) && makensis installer.nsi 7z a -r -tzip $(OUTFILE) $(INSTALLER_TMP_DIR)/*.exe dir $(PACKAGE_OUT_DIR) diff --git a/dune-project b/dune-project index 31dd0af3..b88b92b7 100644 --- a/dune-project +++ b/dune-project @@ -1,4 +1,4 @@ -(lang dune 1.11) +(lang dune 3.0) (name haxe) (package @@ -7,4 +7,4 @@ (package (name haxe_prebuild) -) \ No newline at end of file +) diff --git a/extra/BUILDING.md b/extra/BUILDING.md index 18c80933..851d0914 100644 --- a/extra/BUILDING.md +++ b/extra/BUILDING.md @@ -36,11 +36,11 @@ You need to install some native libraries as well as some OCaml libraries. To install the native libraries, use the appropriate system package manager. * Mac OS X - * Use [Homebrew](https://brew.sh/), `brew install zlib pcre`. + * Use [Homebrew](https://brew.sh/), `brew install zlib pcre2`. * Debian / Ubuntu - * `sudo apt install libpcre3-dev zlib1g-dev`. + * `sudo apt install libpcre2-dev zlib1g-dev libmbedtls-dev`. * Windows (Cygwin) - * Run the Cygwin [setup-x86_64.exe](https://cygwin.com/install.html) against the Cygwin installation directory. Install `make`, `git`, `zlib-devel`, `libpcre-devel`, `mingw64-x86_64-gcc-core`, `mingw64-x86_64-zlib`, and `mingw64-x86_64-pcre`. You may need to select "Not Installed" in the dropdown list to see the packages. Copy `zlib1.dll` and `libpcre-1.dll` from `path/to/cygwin/usr/x86_64-w64-mingw32/sys-root/mingw/bin` to the checked out Haxe source directory. + * Run the Cygwin [setup-x86_64.exe](https://cygwin.com/install.html) against the Cygwin installation directory. Install `make`, `git`, `zlib-devel`, `libpcre2-devel`, `mingw64-x86_64-gcc-core`, `mingw64-x86_64-zlib`, and `mingw64-x86_64-pcre2`. You may need to select "Not Installed" in the dropdown list to see the packages. Copy `zlib1.dll` and `libpcre2-8-0.dll` from `path/to/cygwin/usr/x86_64-w64-mingw32/sys-root/mingw/bin` to the checked out Haxe source directory. * Install Neko by either * Download the [Neko binaries](https://nekovm.org/download/), and add the extracted directory to the beginning of PATH. * Install the [Chocolatey Neko package](https://chocolatey.org/packages/neko). diff --git a/extra/CHANGES.txt b/extra/CHANGES.txt index 645893bf..2c9a0530 100644 --- a/extra/CHANGES.txt +++ b/extra/CHANGES.txt @@ -1,3 +1,109 @@ +2023-04-06 4.3.0 + + New features: + + all : support defaults for type parameters (#10518) + all : support @:op(a()) on abstracts (#10119) + all : support abstract keyword to reference the abstract (#10513) + all : support static var at expression-level (#10555) + all : support ?. safe navigation operator (#10561) + all : added ?? null coalescing operator (#10428) + all : add -w compiler option to configure warnings (#10612) + all : added new error reporting modes (#10863) + all : support custom metadata and defines (#10858) + + General improvements: + + all : made various optimizations in the analyzer + all : made various improvements to diagnostics + all : made various improvements to null-safety + all : optimize `.bind` for instance methods (#10737) + all : improved various parser error messages + all : improved compilation server performance + all : improved code generation for try/catch (#10519) + all : infer property accessor type from the property (#10569) + all : improved inference of local functions typed against abstracts (#10336) + all : improved completion on module-level fields + all : improved handling of native libraries on the compilation server + all : improved performance when generating locals (#10648) + all : made Std.parseInt more consistent across targets (#10664) + all : infer null literals as Null (#7736) + all : made field access errors more consistent (#10896) + all : consistently allow trailing commas (#11009) + all : migrated all relevant targets to PCRE2 (#10491) + all : made analyzer reconstruct do-while loops (#7979) + all : improved restrictions on init macros (#11043) + all : improved positions of @:structInit fields (#9318) + macro : support map literals in Context.makeExpr (#10259) + macro : added haxe.macro.Compiler.getConfiguration() (#10871) + macro : added withImports and withOption to haxe.macro.Context + macro : added getMacroStack and onAfterInitMacros to haxe.macro.Context (#11043) + macro : added haxe.macro.Context.makeMonomorph (#10728) + eval : added dictionary mode to objects, increasing performance in some cases (#10284) + eval : fixed Sys.exit handling messing up the compilation server (#10642) + eval : added -D eval-print-depth and -D eval-pretty-print (#10952, #10963) + cpp : supported type parameters on extern classes (#10415) + cpp : haxe.Int64 improvements (#9935) + cpp : non-blocking process exit code reading (#10321) + js : improved type names for debugger (#10894) + lua : added SSL implementation (#10593) + lua : fixed String API when -D no-inline was in place (#11057) + lua : non-zero exit code in case of uncaught exception (#11082) + java : deal with default implementations when loading extern libraries (#10466) + jvm : improved closure naming (#10571) + jvm : supported --jvm dir (#10614) + jvm : added some missing TShort and TFloat handling (#10722) + jvm : added experimental support for functional interfaces (#11019) + php : added and fixed several extern functions + + Standard Library: + + all : added atomic operations to several targets (#10610) + all : added Vector.fill (#10687) + sys : added sys.thread.Condition and Semaphore (#10503) + sys : added Http.getResponseHeaderValues to deal with multiple values of same key (#10812) + sys : make Sys.environment consistent between targets (#10460) + sys : consistent way to unset environment variables with Sys.putEnv (#10402) + + Bugfixes: + + all : properly disallowed macro reification in invalid places (#10883) + all : fixed behavior when extending abstract classes with final fields (#10139) + all : addressed some issues with constructor inlining (#10304) + all : fixed pattern matcher bug related to null-guards (#10291) + all : fixed weird parser behavior on first offset in files (#10322) + all : fixed various issues related to the final modifier + all : fixed various issues related to overload handling + all : fixed precedence of => vs. ?: (#10455) + all : made error positions in structure declarations more accurate (#9584) + all : brought back proper check when accessing super fields (#10521) + all : fixed @:publicFields on abstracts (#10541) + all : inherited some metadata to @:generic implementation classes (#10557) + all : fixed various problems in EventLoop and MainLoop + all : fixed infinite recursion from invalid inheritance (#10245) + all : fixed strange interaction between static extensions and macro functions (#10587) + all : fixed problems with @:generic on the compilation server (#10635) + all : fixed Context.onTypeNotFound being called multiple times for the same thing (#10678) + all : fixed type parameter problem involving abstract classes and interfaces (#10748) + all : fixed #if (a != b) yielding true for nonexistent values (#10868) + all : fixed interaction between @:generic and abstract class (#10735) + all : fixed @:using loading types too eagerly (#10107) + all : fixed parser ignoring subsequent documentation comments (#10171) + all : fixed parser function messing up unrelated file state in some cases (#10763) + all : unified Map printing to always use [] (#9260) + cpp : fixed problem with cpp.Pointer being used in enum arguments (#10831) + macro : improved handling of macro this expressions (#10793) + eval : fixed bit shift operations > 31 (#10752) + eval : fixed Bytes.toString causing an internal exception (#10623) + jvm : fixed @:native processing (#10280) + jvm : fixed Type.getEnumConstructs on native enums (#10508) + jvm : fixed @:volatile being ignored (#10594) + jvm : fixed some hashing issue when pattern matching on emojis (#10720) + jvm : fixed stack handling on return return (#10743) + hl : fixed various code generation problems + python : fixed SslSocket for newer python versions (#8401) + python : fixed syntax issue related to Vector (#11060) + 2021-03-06 4.2.5: New features: @@ -934,7 +1040,7 @@ Bugfixes: php7: fix Reflect.field() for strings (#6276) - php7: fix `@:enum abstract` generation without `-dce full` (#6175) + php7: fix `@:enum abstract` generation without `-dce full` (#6175) php7: fix using enum constructor with arguments as a call argument (#6177) php7: fix `null` property access (#6281) php7: fix setting values in a map stored in another map (#6257) @@ -980,7 +1086,7 @@ all : fixed DCE issue with interface fields (#6502) cpp : fixed return typing for embedded closures (#6121) - php7: fixed `@:enum abstract` generation without `-dce full` (#6175) + php7: fixed `@:enum abstract` generation without `-dce full` (#6175) php7: fixed using enum constructor with arguments as a call argument (#6177) php7: fixed accessing methods on dynamic values (#6211) php7: fixed `null` property access (#6281) @@ -1696,7 +1802,7 @@ all : allow to access root package with std prefix (std.Type for example) all : added haxe.EnumFlags sys : io.File.getChar/stdin/stdout/stderr are now in Sys class - cpp : Reflect.getField and Reflect.setField no longer call property functions. Use Reflect.getProperty and Refelect.setProperty instead. + cpp : Reflect.getField and Reflect.setField no longer call property functions. Use Reflect.getProperty and Refelect.setProperty instead. cpp : Default arguments now use Null for performance increase and interface compatibility cpp : Added metadata options for injecting native cpp code into headers, classes and functions php : added php.Lib.mail diff --git a/extra/ImportAll.hx b/extra/ImportAll.hx index c445e3ed..b6b63a02 100644 --- a/extra/ImportAll.hx +++ b/extra/ImportAll.hx @@ -99,6 +99,9 @@ class ImportAll { case "haxe.remoting.SyncSocketConnection": if( !(Context.defined("neko") || Context.defined("php") || Context.defined("cpp")) ) continue; case "neko.vm.Ui" | "sys.db.Sqlite" | "sys.db.Mysql" if ( Context.defined("interp") ): continue; case "sys.db.Sqlite" | "sys.db.Mysql" | "cs.db.AdoNet" if ( Context.defined("cs") ): continue; + case "haxe.atomic.AtomicBool" if(!Context.defined("target.atomics")): continue; + case "haxe.atomic.AtomicInt" if(!Context.defined("target.atomics")): continue; + case "haxe.atomic.AtomicObject" if(!Context.defined("target.atomics") || Context.defined("js") || Context.defined("cpp")): continue; } Context.getModule(cl); } else if( sys.FileSystem.isDirectory(p + "/" + file) ) diff --git a/extra/choco/haxe.nuspec b/extra/choco/haxe.nuspec index b7d3c87e..e080b7a3 100644 --- a/extra/choco/haxe.nuspec +++ b/extra/choco/haxe.nuspec @@ -17,12 +17,12 @@ false Haxe is an open source toolkit based on a modern, high level, strictly typed programming language, a cross-compiler, a complete cross-platform standard library and ways to access each platform's native capabilities. Haxe is an open source toolkit based on a modern, high level, strictly typed programming language, a cross-compiler, a complete cross-platform standard library and ways to access each platform's native capabilities. - + This package will install haxe and haxelib. After installation, you should run `haxelib setup %path%;`, where `%path%` is the haxelib repository folder for placing third-party libraries. The folder should be created manually before running the command. https://github.com/HaxeFoundation/haxe/blob/master/extra/CHANGES.txt haxe programming development foss cross-platform - + - \ No newline at end of file + diff --git a/extra/github-actions/Main.hx b/extra/github-actions/Main.hx index 35c2d0b5..983318ed 100644 --- a/extra/github-actions/Main.hx +++ b/extra/github-actions/Main.hx @@ -24,7 +24,8 @@ class Main { final path = reg.matched(2); final template = File.getContent('./$path'); final lines = template.split("\n"); - for (i in 0...lines.length) lines[i] = spaces + lines[i]; + for (i in 0...lines.length) + lines[i] = (spaces + lines[i]).rtrim(); lines.join("\n"); }); diff --git a/extra/github-actions/build-linux.yml b/extra/github-actions/build-linux.yml deleted file mode 100644 index 6a040cc1..00000000 --- a/extra/github-actions/build-linux.yml +++ /dev/null @@ -1,61 +0,0 @@ -- name: Install dependencies - run: | - set -ex - sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2 - sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls - sudo apt-get update -qqy - sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre3-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build libstring-shellquote-perl - -- name: Install OCaml libraries - run: | - set -ex - opam init # --disable-sandboxing - opam update - opam pin add haxe . --no-action - opam install haxe --deps-only --assume-depexts - opam list - ocamlopt -v - -- name: Set ADD_REVISION=1 for non-release - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: echo "ADD_REVISION=1" >> $GITHUB_ENV - -- name: Build Haxe - run: | - set -ex - eval $(opam env) - opam config exec -- make -s -j`nproc` STATICLINK=1 haxe - opam config exec -- make -s haxelib - make -s package_unix - ls -l out - ldd -v ./haxe - ldd -v ./haxelib - -# https://stackoverflow.com/questions/58033366/how-to-get-current-branch-within-github-actions -- name: Extract branch name - id: extract_branch - shell: bash - run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" - -- name: Build xmldoc - run: | - set -ex - make -s xmldoc - cat >extra/doc/info.json <&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam pin add ctypes 0.17.1 --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list') - & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v') - -- name: Expose mingw dll files - shell: pwsh - run: Write-Host "::add-path::${env:CYG_ROOT}/usr/$($env:MINGW_ARCH)-w64-mingw32/sys-root/mingw/bin" + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'git config --global --add safe.directory "$OLDPWD"') - name: Set ADD_REVISION=1 for non-release if: ${{ !startsWith(github.ref, 'refs/tags/') }} diff --git a/extra/github-actions/cache-opam.yml b/extra/github-actions/cache-opam.yml new file mode 100644 index 00000000..ee27a7a1 --- /dev/null +++ b/extra/github-actions/cache-opam.yml @@ -0,0 +1,6 @@ +- name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: ~/.opam/ + key: ${{ runner.os }}-${{ hashFiles('./opam', './libs/') }}-2 diff --git a/extra/github-actions/install-neko.yml b/extra/github-actions/install-neko-unix.yml similarity index 51% rename from extra/github-actions/install-neko.yml rename to extra/github-actions/install-neko-unix.yml index 0311ea5e..ab8eb43b 100644 --- a/extra/github-actions/install-neko.yml +++ b/extra/github-actions/install-neko-unix.yml @@ -1,5 +1,4 @@ -- name: Install Neko using snapshot from S3 (Unix) - if: ${{ !startsWith(env.PLATFORM, 'windows') }} +- name: Install Neko from S3 run: | set -ex @@ -13,15 +12,5 @@ sudo ln -s $NEKOPATH/*.ndll /usr/local/lib/neko/ echo "NEKOPATH=$NEKOPATH" >> $GITHUB_ENV -- name: Install Neko using snapshot from S3 (Windows) - if: ${{ startsWith(env.PLATFORM, 'windows') }} - shell: pwsh - run: | - Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip - Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP - $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* - echo "$NEKOPATH" >> $env:GITHUB_PATH - echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV - - name: Print Neko version run: neko -version 2>&1 diff --git a/extra/github-actions/install-neko-windows.yml b/extra/github-actions/install-neko-windows.yml new file mode 100644 index 00000000..fd5c2dfd --- /dev/null +++ b/extra/github-actions/install-neko-windows.yml @@ -0,0 +1,11 @@ +- name: Install Neko from S3 + shell: pwsh + run: | + Invoke-WebRequest https://build.haxe.org/builds/neko/$env:PLATFORM/neko_latest.zip -OutFile $env:RUNNER_TEMP/neko_latest.zip + Expand-Archive $env:RUNNER_TEMP/neko_latest.zip -DestinationPath $env:RUNNER_TEMP + $NEKOPATH = Get-ChildItem $env:RUNNER_TEMP/neko-*-* + echo "$NEKOPATH" >> $env:GITHUB_PATH + echo "NEKOPATH=$NEKOPATH" >> $env:GITHUB_ENV + +- name: Print Neko version + run: neko -version 2>&1 diff --git a/extra/github-actions/install-nsis.yml b/extra/github-actions/install-nsis.yml new file mode 100644 index 00000000..33ce6f1d --- /dev/null +++ b/extra/github-actions/install-nsis.yml @@ -0,0 +1,12 @@ +- name: choco install nsis + uses: nick-invision/retry@v1 + with: + timeout_minutes: 10 + max_attempts: 10 + command: choco install --no-progress nsis.portable --version 3.02 -y +- name: choco install things + shell: pwsh + run: choco install --no-progress curl wget 7zip.portable -y +- name: Prepend Chocolatey path + shell: pwsh + run: Write-Host "::add-path::C:\ProgramData\chocolatey\bin" diff --git a/extra/github-actions/install-ocaml-libs-windows.yml b/extra/github-actions/install-ocaml-libs-windows.yml new file mode 100644 index 00000000..e0abf154 --- /dev/null +++ b/extra/github-actions/install-ocaml-libs-windows.yml @@ -0,0 +1,11 @@ +- name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' + shell: pwsh + run: | + Set-PSDebug -Trace 1 + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam init mingw "https://github.com/fdopen/opam-repository-mingw.git#opam2" --comp 4.07.0+mingw${ARCH}c --switch 4.07.0+mingw${ARCH}c --auto-setup --yes 2>&1') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam update --yes 2>&1') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && opam pin add haxe . --kind=path --no-action --yes 2>&1') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam install haxe --deps-only --yes 2>&1') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'opam list') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'ocamlopt -v') diff --git a/extra/github-actions/install-ocaml-windows.yml b/extra/github-actions/install-ocaml-windows.yml new file mode 100644 index 00000000..3a0d18c3 --- /dev/null +++ b/extra/github-actions/install-ocaml-windows.yml @@ -0,0 +1,17 @@ +- name: Install OCaml + shell: pwsh + run: | + Set-PSDebug -Trace 1 + curl.exe -fsSL -o cygwin-setup.exe --retry 3 $($env:CYGWIN_SETUP) + Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre2-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre2 --allow-unsupported-windows" -Wait -RedirectStandardOutput cygwin-setup-out.txt + Get-Content cygwin-setup-out.txt + & "$($env:CYG_ROOT)/bin/bash.exe" @('--norc', '--noprofile', '/etc/postinstall/ca-certificates.sh') + curl.exe -fsSL -o "opam.tar.xz" --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.2/opam$($env:ARCH).tar.xz + curl.exe -fsSL -o "libmbedtls.tar.xz" --retry 3 https://github.com/Simn/mingw64-mbedtls/releases/download/2.16.3/mingw64-$($env:MINGW_ARCH)-mbedtls-2.16.3-1.tar.xz + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'curl -L https://cpanmin.us | perl - App::cpanminus') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cpanm IPC::System::Simple module') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cpanm String::ShellQuote') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'echo "$OLDPWD"') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -C / -xvf libmbedtls.tar.xz') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -xf opam.tar.xz') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && bash opam${ARCH}/install.sh') diff --git a/extra/github-actions/install-ocaml-windows64.yml b/extra/github-actions/install-ocaml-windows64.yml new file mode 100644 index 00000000..1f4a087f --- /dev/null +++ b/extra/github-actions/install-ocaml-windows64.yml @@ -0,0 +1,16 @@ +- name: Install OCaml + shell: pwsh + run: | + Set-PSDebug -Trace 1 + curl.exe -fsSL -o cygwin-setup.exe --retry 3 $($env:CYGWIN_SETUP) + Start-Process -FilePath "cygwin-setup.exe" -ArgumentList "-B -q -R $($env:CYG_ROOT) -l C:/tmp -s $($env:CYG_MIRROR) -P default -P make -P git -P zlib-devel -P rsync -P patch -P diffutils -P curl -P unzip -P tar -P m4 -P perl -P libpcre2-devel -P mbedtls-devel -P mingw64-$($env:MINGW_ARCH)-zlib -P mingw64-$($env:MINGW_ARCH)-gcc-core -P mingw64-$($env:MINGW_ARCH)-pcre2" -Wait -RedirectStandardOutput cygwin-setup-out.txt + Get-Content cygwin-setup-out.txt + curl.exe -fsSL -o "opam.tar.xz" --retry 3 https://github.com/fdopen/opam-repository-mingw/releases/download/0.0.0.2/opam$($env:ARCH).tar.xz + curl.exe -fsSL -o "libmbedtls.tar.xz" --retry 3 https://github.com/Simn/mingw64-mbedtls/releases/download/2.16.3/mingw64-$($env:MINGW_ARCH)-mbedtls-2.16.3-1.tar.xz + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'curl -L https://cpanmin.us | perl - App::cpanminus') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cpanm IPC::System::Simple module') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cpanm String::ShellQuote') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'echo "$OLDPWD"') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -C / -xvf libmbedtls.tar.xz') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && tar -xf opam.tar.xz') + & "$($env:CYG_ROOT)/bin/bash.exe" @('-lc', 'cd "$OLDPWD" && bash opam${ARCH}/install.sh') diff --git a/extra/github-actions/test-linux.yml b/extra/github-actions/test-linux.yml deleted file mode 100644 index 0e69a2b5..00000000 --- a/extra/github-actions/test-linux.yml +++ /dev/null @@ -1,46 +0,0 @@ -- name: Setup Haxe - run: | - # mkdir ./linuxBinaries - # curl -sSL https://build.haxe.org/builds/haxe/linux64/haxe_latest.tar.gz -o ./linuxBinaries/haxe_bin.tar.gz - - sudo apt install -qqy libmbedtls-dev - - set -ex - tar -xf linuxBinaries/*_bin.tar.gz -C linuxBinaries --strip-components=1 - sudo mkdir -p /usr/local/bin/ - sudo mkdir -p /usr/local/share/haxe/ - sudo ln -s `pwd`/linuxBinaries/haxe /usr/local/bin/haxe - sudo ln -s `pwd`/linuxBinaries/haxelib /usr/local/bin/haxelib - sudo ln -s `pwd`/linuxBinaries/std /usr/local/share/haxe/std - -- name: Print Haxe version - run: haxe -version - -- name: Setup haxelib - run: | - set -ex - mkdir ~/haxelib - haxelib setup ~/haxelib - -- name: Install apt packages - if: matrix.APT_PACKAGES - run: | - set -ex - sudo apt update -qqy - sudo apt install -qqy ${{matrix.APT_PACKAGES}} - -# - name: Flash setup -# if: matrix.target == 'flash9' -# run: export DISPLAY=:99.0 - -- name: Test - if: success() && !(matrix.SAUCE && matrix.SAUCE_ACCESS_KEY) - run: haxe RunCi.hxml - working-directory: ${{github.workspace}}/tests - -- name: Test (with SauceLabs) - if: matrix.target == 'js' && success() && matrix.SAUCE && matrix.SAUCE_ACCESS_KEY - run: haxe RunCi.hxml - working-directory: ${{github.workspace}}/tests - env: - SAUCE_ACCESS_KEY: matrix.SAUCE_ACCESS_KEY diff --git a/extra/github-actions/test-windows.yml b/extra/github-actions/test-windows.yml index 2056ab9d..8eaf81b6 100644 --- a/extra/github-actions/test-windows.yml +++ b/extra/github-actions/test-windows.yml @@ -36,6 +36,13 @@ hererocks lua53 -l5.3 -rlatest call lua53/bin/activate +- name: Install wget + if: matrix.target == 'flash' + shell: cmd + run: | + choco install wget + wget --version + - name: Setup haxelib shell: pwsh run: | diff --git a/extra/github-actions/workflows/main.yml b/extra/github-actions/workflows/main.yml index c3636efd..c860a56d 100644 --- a/extra/github-actions/workflows/main.yml +++ b/extra/github-actions/workflows/main.yml @@ -4,22 +4,13 @@ name: CI on: [push, pull_request] jobs: - cancel: - name: "Cancel previous runs" - runs-on: ubuntu-18.04 - timeout-minutes: 3 - steps: - - uses: styfle/cancel-workflow-action@0.3.1 - with: - workflow_id: 1447568 - access_token: ${{ github.token }} - windows64-build: runs-on: windows-latest env: ACTIONS_ALLOW_UNSECURE_COMMANDS: true PLATFORM: windows64 OPAMYES: 1 + OPAMROOT: D:\.opam CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ ARCH: 64 MINGW_ARCH: x86_64 @@ -30,7 +21,22 @@ jobs: with: submodules: recursive - @import install-neko.yml + - name: Use GNU Tar from msys + run: | + echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + rm C:\msys64\usr\bin\bash.exe + + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: D:\.opam + key: ${{ runner.os }}64-${{ hashFiles('./opam', './libs/') }} + + @import install-neko-windows.yml + @import install-nsis.yml + @import install-ocaml-windows64.yml + @import install-ocaml-libs-windows.yml @import build-windows.yml windows-build: @@ -39,7 +45,8 @@ jobs: ACTIONS_ALLOW_UNSECURE_COMMANDS: true PLATFORM: windows OPAMYES: 1 - CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ + OPAMROOT: D:\.opam + CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin-archive/20221123 ARCH: 32 MINGW_ARCH: i686 CYGWIN_SETUP: https://cygwin.com/setup-x86.exe @@ -49,11 +56,26 @@ jobs: with: submodules: recursive - @import install-neko.yml + - name: Use GNU Tar from msys + run: | + echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + rm C:\msys64\usr\bin\bash.exe + + - name: Cache opam + id: cache-opam + uses: actions/cache@v3.0.11 + with: + path: D:\.opam + key: ${{ runner.os }}32-${{ hashFiles('./opam', './libs/') }} + + @import install-neko-windows.yml + @import install-nsis.yml + @import install-ocaml-windows.yml + @import install-ocaml-libs-windows.yml @import build-windows.yml linux-build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 env: PLATFORM: linux64 OPAMYES: 1 @@ -62,8 +84,255 @@ jobs: with: submodules: recursive - @import install-neko.yml - @import build-linux.yml + @import cache-opam.yml + @import install-neko-unix.yml + + - name: Install dependencies + run: | + set -ex + sudo add-apt-repository ppa:avsm/ppa -y # provides OPAM 2 + sudo add-apt-repository ppa:haxe/ocaml -y # provides newer version of mbedtls + sudo apt-get update -qqy + sudo apt-get install -qqy ocaml-nox camlp5 opam libpcre2-dev zlib1g-dev libgtk2.0-dev libmbedtls-dev ninja-build libstring-shellquote-perl libipc-system-simple-perl + + - name: Install OCaml libraries + if: steps.cache-opam.outputs.cache-hit != 'true' + run: | + set -ex + opam init # --disable-sandboxing + opam update + opam pin add haxe . --no-action + opam install haxe --deps-only --assume-depexts + opam list + ocamlopt -v + + - name: Set ADD_REVISION=1 for non-release + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + run: echo "ADD_REVISION=1" >> $GITHUB_ENV + + - name: Build Haxe + run: | + set -ex + eval $(opam env) + opam config exec -- make -s -j`nproc` STATICLINK=1 haxe + opam config exec -- make -s haxelib + make -s package_unix + ls -l out + ldd -v ./haxe + ldd -v ./haxelib + + # https://stackoverflow.com/questions/58033366/how-to-get-current-branch-within-github-actions + - name: Extract branch name + id: extract_branch + shell: bash + run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + + - name: Build xmldoc + run: | + set -ex + make -s xmldoc + cat >extra/doc/info.json <> $GITHUB_ENV; + echo "CONTAINER_TAG=$(echo ${{ github.ref_name }} | sed -e 's/[^A-Za-z0-9\.]/-/g')" >> $GITHUB_ENV; + + - name: Build devcontainer + run: earthly --platform=linux/arm64 +devcontainer --IMAGE_NAME="ghcr.io/${CONTAINER_REG}_devcontainer" --IMAGE_TAG="${CONTAINER_TAG}-arm64" --IMAGE_CACHE="ghcr.io/haxefoundation/haxe_devcontainer:development-arm64" + env: + EARTHLY_PUSH: "${{ github.event_name == 'push' }}" + EARTHLY_USE_INLINE_CACHE: true + EARTHLY_SAVE_INLINE_CACHE: true + + - name: Set ADD_REVISION=1 for non-release + if: ${{ !startsWith(github.ref, 'refs/tags/') }} + run: echo "ADD_REVISION=1" >> $GITHUB_ENV + + - name: Build + run: earthly --platform=linux/arm64 +build --ADD_REVISION="$ADD_REVISION" --SET_SAFE_DIRECTORY="true" + env: + EARTHLY_PUSH: "${{ github.event_name == 'push' }}" + EARTHLY_REMOTE_CACHE: "ghcr.io/${{env.CONTAINER_REG}}_cache:build-${{env.CONTAINER_TAG}}-arm64" + + - name: Upload artifact + uses: actions/upload-artifact@v1.0.0 + with: + name: linuxArm64Binaries + path: out/linux/arm64 mac-build: runs-on: macos-latest @@ -76,7 +345,8 @@ jobs: with: submodules: recursive - @import install-neko.yml + @import cache-opam.yml + @import install-neko-unix.yml @import build-mac.yml windows64-test: @@ -91,8 +361,8 @@ jobs: strategy: fail-fast: false matrix: - # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/5024 - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, neko] + # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/10919 + target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, flash, neko] steps: - uses: actions/checkout@main with: @@ -101,7 +371,7 @@ jobs: with: name: win${{env.ARCH}}Binaries - @import install-neko.yml + @import install-neko-windows.yml @import test-windows.yml windows-test: @@ -117,8 +387,8 @@ jobs: fail-fast: false matrix: # TODO jvm: https://github.com/HaxeFoundation/haxe/issues/8601 - # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/5024 - target: [macro, js, hl, cpp, java, cs, php, python, neko] + # TODO enable lua after https://github.com/HaxeFoundation/haxe/issues/10919 + target: [macro, js, hl, cpp, java, cs, php, python, flash, neko] steps: - uses: actions/checkout@main with: @@ -127,44 +397,9 @@ jobs: with: name: win${{env.ARCH}}Binaries - @import install-neko.yml + @import install-neko-windows.yml @import test-windows.yml - linux-test: - needs: linux-build - runs-on: ubuntu-18.04 - env: - PLATFORM: linux64 - TEST: ${{matrix.target}} - HXCPP_COMPILE_CACHE: ~/hxcache - strategy: - fail-fast: false - matrix: - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, lua, neko] #flash9 - include: - - target: js - # SAUCE: 1 - # SAUCE_TUNNEL_ID: 'linux-test' - # SAUCE_BUILD: '${{env.GITHUB_RUN_NUMBER}}' # $(Build.BuildNumber) - - target: hl - APT_PACKAGES: cmake ninja-build libturbojpeg-dev - - target: cpp - APT_PACKAGES: gcc-multilib g++-multilib - - target: lua - APT_PACKAGES: ncurses-dev - # - target: flash9 - # APT_PACKAGES: libglib2.0-0 libfreetype6 xvfb - steps: - - uses: actions/checkout@main - with: - submodules: recursive - - uses: actions/download-artifact@v1 - with: - name: linuxBinaries - - @import install-neko.yml - @import test-linux.yml - mac-test: needs: mac-build runs-on: macos-latest @@ -172,10 +407,11 @@ jobs: PLATFORM: mac TEST: ${{matrix.target}} HXCPP_COMPILE_CACHE: ~/hxcache + HAXE_STD_PATH: /usr/local/share/haxe/std strategy: fail-fast: false matrix: - target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, lua, neko] #flash9 + target: [macro, js, hl, cpp, 'java,jvm', cs, php, python, flash, neko] include: - target: hl BREW_PACKAGES: ninja @@ -187,13 +423,13 @@ jobs: with: name: macBinaries - @import install-neko.yml + @import install-neko-unix.yml @import test-mac.yml deploy: - if: github.event_name != 'pull_request' - needs: [linux-test, mac-test, windows-test, windows64-test] - runs-on: ubuntu-18.04 + if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' + needs: [linux-test, linux-arm64, mac-test, windows-test, windows64-test] + runs-on: ubuntu-20.04 steps: # this is only needed for to get `COMMIT_DATE`... # maybe https://github.community/t/expose-commit-timestamp-in-the-github-context-data/16460/3 @@ -222,12 +458,14 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} HXBUILDS_S3ADDR: ${{ secrets.HXBUILDS_S3ADDR }} + AWS_EC2_METADATA_DISABLED: true run: | set -ex COMMIT_HASH_SHORT=${GITHUB_SHA:0:7} COMMIT_DATE=`TZ=UTC git show --quiet --date='format-local:%Y-%m-%d' --format="%cd"` FILE_NAME=haxe_${COMMIT_DATE}_${{ steps.extract_branch.outputs.branch }}_${COMMIT_HASH_SHORT} aws s3 cp linuxBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux64/${FILE_NAME}.tar.gz + aws s3 cp linuxArm64Binaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux-arm64/${FILE_NAME}.tar.gz aws s3 cp macBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac/${FILE_NAME}.tar.gz aws s3 cp macBinaries/*_installer.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac-installer/${FILE_NAME}.tar.gz aws s3 cp win64Binaries/*_bin.zip ${HXBUILDS_S3ADDR}/haxe/windows64/${FILE_NAME}.zip @@ -244,9 +482,11 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.HXBUILDS_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.HXBUILDS_AWS_SECRET_ACCESS_KEY }} HXBUILDS_S3ADDR: ${{ secrets.HXBUILDS_S3ADDR }} + AWS_EC2_METADATA_DISABLED: true run: | set -ex aws s3 cp linuxBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux64/haxe_latest.tar.gz + aws s3 cp linuxArm64Binaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/linux-arm64/haxe_latest.tar.gz aws s3 cp macBinaries/*_bin.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac/haxe_latest.tar.gz aws s3 cp macBinaries/*_installer.tar.gz ${HXBUILDS_S3ADDR}/haxe/mac-installer/haxe_latest.tar.gz aws s3 cp win64Binaries/*_bin.zip ${HXBUILDS_S3ADDR}/haxe/windows64/haxe_latest.zip @@ -263,10 +503,15 @@ jobs: aws s3 cp ${HXBUILDS_S3ADDR}/haxe/windows-choco/${FILE_NAME} ${HXBUILDS_S3ADDR}/haxe/windows-choco/haxe_latest.nupkg --acl public-read --website-redirect "${HXBUILDS_S3PATH}/haxe/windows-choco/${FILE_NAME}" deploy_apidoc: - if: github.event_name != 'pull_request' # TODO: also only when `GHP_REMOTE` is present - needs: [linux-test, mac-test, windows-test, windows64-test] - runs-on: ubuntu-18.04 + if: success() && github.repository_owner == 'HaxeFoundation' && github.event_name != 'pull_request' + needs: [linux-test, linux-arm64, mac-test, windows-test, windows64-test] + runs-on: ubuntu-20.04 steps: + - name: Install dependencies + run: | + set -ex + sudo apt-get install -qqy libc6 + - name: Download Haxe uses: actions/download-artifact@v2 with: diff --git a/extra/haxelib_src/CHANGES.md b/extra/haxelib_src/CHANGES.md index f61f98bd..1ad2693a 100644 --- a/extra/haxelib_src/CHANGES.md +++ b/extra/haxelib_src/CHANGES.md @@ -1,3 +1,12 @@ +## 4.1.0 (2023-04-06) + + - Added support for documenting custom defines and metadata ([#573](https://github.com/HaxeFoundation/haxelib/pull/573)) + - Fixed a segmentation fault on Linux systems + +## 4.0.3 (2023-02-27) + - Fixed large git installations hanging on windows ([#585](https://github.com/HaxeFoundation/haxelib/pull/585)) + - Corrected license in haxelib.json ([#535](https://github.com/HaxeFoundation/haxelib/pull/535)) + ## 4.0.2 (2019-11-11) - Fixed too strict requirements to haxelib.json data for private libs ([#484](https://github.com/HaxeFoundation/haxelib/issues/484)) diff --git a/extra/haxelib_src/haxelib.json b/extra/haxelib_src/haxelib.json index f87b9c51..98ff0509 100644 --- a/extra/haxelib_src/haxelib.json +++ b/extra/haxelib_src/haxelib.json @@ -1,11 +1,11 @@ { "name": "haxelib", "url" : "https://lib.haxe.org/documentation/", - "license": "GPL", + "license": "MIT", "tags": ["haxelib", "core"], "description": "The haxelib client", "classPath": "src", - "version": "4.0.2", - "releasenote": " * Fixed too strict requirements to haxelib.json data for private libs (#484)", + "version": "4.1.0", + "releasenote": "Added support for documenting custom defines and metadata (#573)", "contributors": ["HaxeFoundation", "back2dos", "ncannasse", "jason", "Simn", "nadako", "andyli"] } diff --git a/extra/haxelib_src/run.n b/extra/haxelib_src/run.n index 9ced81af66c4c8ebafd1495c30e7254375dbf0c8..3b86e0f48ea70729ebee788c9f8ef81b2a62aa5c 100644 GIT binary patch delta 90760 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 Pzi3IUPfi{Ff-3P3on927 diff --git a/extra/haxelib_src/schema.json b/extra/haxelib_src/schema.json index 4faf9e65..c27d38c1 100644 --- a/extra/haxelib_src/schema.json +++ b/extra/haxelib_src/schema.json @@ -47,6 +47,21 @@ }, "additionalProperties": false }, + "documentation": { + "type": "object", + "description": "Project's documentation resources", + "properties": { + "defines": { + "type": "string", + "description": "Relative path to json file describing this project's custom defines" + }, + "metadata": { + "type": "string", + "description": "Relative path to json file describing this project's custom metadata" + } + }, + "additionalProperties": false + }, "releasenote": { "description": "Short description of changes made in this version", "type": "string" diff --git a/extra/haxelib_src/src/haxelib/Data.hx b/extra/haxelib_src/src/haxelib/Data.hx index b5d696bb..7011b842 100644 --- a/extra/haxelib_src/src/haxelib/Data.hx +++ b/extra/haxelib_src/src/haxelib/Data.hx @@ -171,7 +171,35 @@ typedef Infos = { var contributors : Array; @:optional var tags : Array; @:optional var dependencies : Dependencies; - @:optional var main:String; + @:optional var main : String; + @:optional var documentation : LibraryDocumentation; +} + +/** Documentation data held in the `documentation` field of the `haxelib.json` file. **/ +typedef LibraryDocumentation = { + @:optional var defines : String; + @:optional var metadata : String; +} + +/** Metadata documentation data as should be declared in the json linked in the + * `documentation.metadata` field of the `haxelib.json` file. **/ +typedef MetadataDocumentation = { + var metadata : String; + var doc : String; + @:optional var platforms : Array; + @:optional var params : Array; + @:optional var targets : Array; + @:optional var links : Array; +} + +/** Define documentation data as should be declared in the json linked in the + * `documentation.defines` field of the `haxelib.json` file. **/ +typedef DefineDocumentation = { + var define : String; + var doc : String; + @:optional var platforms : Array; + @:optional var params : Array; + @:optional var links : Array; } @:enum abstract License(String) to String { @@ -330,6 +358,48 @@ class Data { } } + /** Throws an exception if files referenced in `documentation` field of an `infos` do not exist or are invalid **/ + public static function checkDocumentation( zip : List, infos : Infos ) { + if (infos.documentation == null) return; + + var hasDefines = infos.documentation.defines != null; + var hasMetadata = infos.documentation.metadata != null; + if (!hasDefines && !hasMetadata) return; + + var basePath = Data.locateBasePath(zip); + var definesPath = hasDefines ? basePath + infos.documentation.defines : null; + var metadataPath = hasMetadata ? basePath + infos.documentation.metadata : null; + var definesFound = false; + var metadataFound = false; + + for (f in zip) { + if (hasDefines && StringTools.startsWith(f.fileName, definesPath)) { + definesFound = true; + try { + var jsondata = Reader.unzip(f).toString(); + var defines:Array = Json.parse(jsondata); + Validator.validate(defines); + } catch (_:Dynamic) { + throw 'Defines documentation json file does not match expected format'; + } + } else if (hasMetadata && StringTools.startsWith(f.fileName, metadataPath)) { + metadataFound = true; + try { + var jsondata = Reader.unzip(f).toString(); + var metas:Array = Json.parse(jsondata); + Validator.validate(metas); + } catch (_:Dynamic) { + throw 'Metadata documentation json file does not match expected format'; + } + } + + if ((!hasDefines || definesFound) && (!hasMetadata || metadataFound)) break; + } + + if (hasDefines && !definesFound) throw 'Json file `${infos.documentation.defines}` not found'; + if (hasMetadata && !metadataFound) throw 'Json file `${infos.documentation.metadata}` not found'; + } + public static function readData( jsondata: String, check : CheckLevel ) : Infos { var doc:Infos = try Json.parse(jsondata) diff --git a/extra/haxelib_src/src/haxelib/client/Main.hx b/extra/haxelib_src/src/haxelib/client/Main.hx index 09de2eb4..d0d2ea92 100644 --- a/extra/haxelib_src/src/haxelib/client/Main.hx +++ b/extra/haxelib_src/src/haxelib/client/Main.hx @@ -634,6 +634,7 @@ class Main { var infos = Data.readInfos(zip,true); Data.checkClassPath(zip, infos); + Data.checkDocumentation(zip, infos); var user:String = infos.contributors[0]; @@ -1502,6 +1503,25 @@ class Main { Sys.println(dir); Sys.println("-D " + d.project + "="+d.info.version); + + if (d.info.documentation != null) { + var doc = d.info.documentation; + + // we'll have to change this to "4.3.0" after the release + if (haxeVersion() >= SemVer.ofString("4.3.0-rc.1")) { + // custom defines if defined + if (doc.defines != null && doc.defines != "") { + var path = Path.join([d.dir, doc.defines]); + Sys.println('--macro registerDefinesDescriptionFile(\'$path\', \'${d.info.name}\')'); + } + + // custom metadatas if defined + if (doc.metadata != null && doc.metadata != "") { + var path = Path.join([d.dir, doc.metadata]); + Sys.println('--macro registerMetadataDescriptionFile(\'$path\', \'${d.info.name}\')'); + } + } + } } } diff --git a/extra/haxelib_src/src/haxelib/client/Vcs.hx b/extra/haxelib_src/src/haxelib/client/Vcs.hx index 9abd5941..f5dab217 100644 --- a/extra/haxelib_src/src/haxelib/client/Vcs.hx +++ b/extra/haxelib_src/src/haxelib/client/Vcs.hx @@ -22,6 +22,13 @@ package haxelib.client; import sys.FileSystem; +#if haxe4 +import sys.thread.Thread; +import sys.thread.Lock; +#else +import neko.vm.Thread; +import neko.vm.Lock; +#end using haxelib.client.Vcs; interface IVcs { @@ -151,18 +158,47 @@ class Vcs implements IVcs { code: -1, out: Std.string(e) } - } - var out = p.stdout.readAll().toString(); - var err = p.stderr.readAll().toString(); - if (settings.debug && out != "") - Sys.println(out); - if (settings.debug && err != "") - Sys.stderr().writeString(err); - var code = p.exitCode(); - var ret = { - code: code, - out: code == 0 ? out : err - }; + }; + var ret = if (Sys.systemName() == "Windows") { + var streamsLock = new sys.thread.Lock(); + function readFrom(stream:haxe.io.Input, to: {value: String}) { + to.value = stream.readAll().toString(); + streamsLock.release(); + } + + var out = {value: ""}; + var err = {value: ""}; + Thread.create(readFrom.bind(p.stdout, out)); + Thread.create(readFrom.bind(p.stderr, err)); + + var code = p.exitCode(); + for (_ in 0...2) { + // wait until we finish reading from both streams + streamsLock.wait(); + } + + if (settings.debug && out.value != "") + Sys.println(out.value); + if (settings.debug && err.value != "") + Sys.stderr().writeString(err.value); + + { + code: code, + out: code == 0 ? out.value : err.value + }; + } else { + var out = p.stdout.readAll().toString(); + var err = p.stderr.readAll().toString(); + if (settings.debug && out != "") + Sys.println(out); + if (settings.debug && err != "") + Sys.stderr().writeString(err); + var code = p.exitCode(); + { + code: code, + out: code == 0 ? out : err + }; + }; p.close(); return ret; } diff --git a/extra/haxelib_src/test/libraries/libBadDefineJson/doc/defines.json b/extra/haxelib_src/test/libraries/libBadDefineJson/doc/defines.json new file mode 100644 index 00000000..75ef15e2 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadDefineJson/doc/defines.json @@ -0,0 +1,5 @@ +[ + { + "name": "Malformed define" + } +] diff --git a/extra/haxelib_src/test/libraries/libBadDefineJson/haxelib.json b/extra/haxelib_src/test/libraries/libBadDefineJson/haxelib.json new file mode 100644 index 00000000..fb424b81 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadDefineJson/haxelib.json @@ -0,0 +1,13 @@ +{ + "name": "BadDefineJson", + "url" : "http://example.org", + "license": "GPL", + "tags": ["bar", "test"], + "description": "This project is an example of an haxelib project", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "defines": "doc/defines.json" + }, + "contributors": ["Bar"] +} diff --git a/extra/haxelib_src/test/libraries/libBadMetaJson/doc/meta.json b/extra/haxelib_src/test/libraries/libBadMetaJson/doc/meta.json new file mode 100644 index 00000000..ab7a2915 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadMetaJson/doc/meta.json @@ -0,0 +1 @@ +This is not a json file diff --git a/extra/haxelib_src/test/libraries/libBadMetaJson/haxelib.json b/extra/haxelib_src/test/libraries/libBadMetaJson/haxelib.json new file mode 100644 index 00000000..743404a2 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadMetaJson/haxelib.json @@ -0,0 +1,13 @@ +{ + "name": "BadMetaJson", + "url" : "http://example.org", + "license": "GPL", + "tags": ["bar", "test"], + "description": "This project is an example of an haxelib project", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json" + }, + "contributors": ["Bar"] +} diff --git a/extra/haxelib_src/test/libraries/libBadMetaJson2/doc/meta.json b/extra/haxelib_src/test/libraries/libBadMetaJson2/doc/meta.json new file mode 100644 index 00000000..526dc73d --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadMetaJson2/doc/meta.json @@ -0,0 +1,5 @@ +[ + { + "name": "Malformed meta" + } +] diff --git a/extra/haxelib_src/test/libraries/libBadMetaJson2/haxelib.json b/extra/haxelib_src/test/libraries/libBadMetaJson2/haxelib.json new file mode 100644 index 00000000..cee33d03 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libBadMetaJson2/haxelib.json @@ -0,0 +1,13 @@ +{ + "name": "BadMetaJson2", + "url" : "http://example.org", + "license": "GPL", + "tags": ["bar", "test"], + "description": "This project is an example of an haxelib project", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json" + }, + "contributors": ["Bar"] +} diff --git a/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/defines.json b/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/defines.json new file mode 100644 index 00000000..63c26e08 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/defines.json @@ -0,0 +1,13 @@ +[ + { + "define": "test", + "doc": "Test define" + }, + { + "define": "test2", + "doc": "Test define 2", + "platforms": ["eval"], + "params": ["foo"], + "links": ["https://example.com"] + } +] diff --git a/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/meta.json b/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/meta.json new file mode 100644 index 00000000..84bdcf32 --- /dev/null +++ b/extra/haxelib_src/test/libraries/libDocumentationFiles/doc/meta.json @@ -0,0 +1,14 @@ +[ + { + "metadata": ":test", + "doc": "Some test metadata" + }, + { + "metadata": ":test2", + "doc": "Some other test metadata", + "platforms": ["eval"], + "params": ["foo"], + "links": ["https://example.com"], + "targets": ["TClass", "TClassField"] + } +] diff --git a/extra/haxelib_src/test/libraries/libDocumentationFiles/haxelib.json b/extra/haxelib_src/test/libraries/libDocumentationFiles/haxelib.json new file mode 100644 index 00000000..d8aea90b --- /dev/null +++ b/extra/haxelib_src/test/libraries/libDocumentationFiles/haxelib.json @@ -0,0 +1,14 @@ +{ + "name": "DocumentationFiles", + "url" : "http://example.org", + "license": "GPL", + "tags": ["bar", "test"], + "description": "This project is an example of an haxelib project", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json", + "defines": "doc/defines.json" + }, + "contributors": ["Bar"] +} diff --git a/extra/haxelib_src/test/libraries/libMissingMetaJson/haxelib.json b/extra/haxelib_src/test/libraries/libMissingMetaJson/haxelib.json new file mode 100644 index 00000000..9b17333d --- /dev/null +++ b/extra/haxelib_src/test/libraries/libMissingMetaJson/haxelib.json @@ -0,0 +1,13 @@ +{ + "name": "MissingMetaJson", + "url" : "http://example.org", + "license": "GPL", + "tags": ["bar", "test"], + "description": "This project is an example of an haxelib project", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json" + }, + "contributors": ["Bar"] +} diff --git a/extra/haxelib_src/test/tests/TestData.hx b/extra/haxelib_src/test/tests/TestData.hx index 82f2dac9..76cf14e5 100644 --- a/extra/haxelib_src/test/tests/TestData.hx +++ b/extra/haxelib_src/test/tests/TestData.hx @@ -103,6 +103,27 @@ class TestData extends TestBase { assertEquals( ok, true ); } + public function testCheckDocumentation() { + var results = [ + "DocumentationFiles" => true, + "BadMetaJson" => false, + "BadMetaJson2" => false, + "BadDefineJson" => false + ]; + + for (r in results.keys()) { + var zip = Reader.readZip(new BytesInput(File.getBytes('test/libraries/lib$r.zip'))); + var info = Data.readInfos(zip, CheckData); + + try { + Data.checkDocumentation(zip,info); + assertTrue(results.get(r)); + } catch (e:Dynamic) { + assertFalse(results.get(r)); + } + } + } + public function testReadDataWithCheck() { assertFalse( readDataOkay("bad json") ); diff --git a/extra/images/Readme.png b/extra/images/Readme.png index f101ccf0aeeecb651f6992fd6009e2073a173838..acd3733aa030e8a60a22e711a211ccaa7f7542fb 100644 GIT binary patch literal 14857 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 literal 8453 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 - if fl land (1 lsl !fbit) <> 0 then begin + List.iteri (fun fbit f -> + if fl land (1 lsl fbit) <> 0 then begin flags := f :: !flags; if f = JUnusable then error ("Unusable flag: " ^ string_of_int fl) - end; - incr fbit + end ) all_flags; (*if fl land (0x4000 - (1 lsl !fbit)) <> 0 then error ("Invalid access flags " ^ string_of_int fl);*) !flags diff --git a/libs/mbedtls/dune b/libs/mbedtls/dune index 890250ed..ca0780dd 100644 --- a/libs/mbedtls/dune +++ b/libs/mbedtls/dune @@ -2,8 +2,8 @@ (library (name mbedtls) - (c_names - mbedtls_stubs - ) + (foreign_stubs + (language c) + (names mbedtls_stubs)) (wrapped false) -) \ No newline at end of file +) diff --git a/libs/objsize/alloc.c b/libs/objsize/alloc.h similarity index 100% rename from libs/objsize/alloc.c rename to libs/objsize/alloc.h diff --git a/libs/objsize/bitarray.c b/libs/objsize/bitarray.h similarity index 98% rename from libs/objsize/bitarray.c rename to libs/objsize/bitarray.h index 19442893..abc38316 100644 --- a/libs/objsize/bitarray.c +++ b/libs/objsize/bitarray.h @@ -14,7 +14,7 @@ void PRF(_set)(TYPE arr[], size_t i, TYPE val); #define ALLOC_TYPE unsigned char #define ALLOC_PRF(x) ALPRF(x) -#include "alloc.c" +#include "alloc.h" size_t wordalign(size_t n) diff --git a/libs/objsize/c_objsize.c b/libs/objsize/c_objsize.c index 4b3413dd..5f222e07 100644 --- a/libs/objsize/c_objsize.c +++ b/libs/objsize/c_objsize.c @@ -7,7 +7,7 @@ #define PRF(x) bitarray##x -#include "bitarray.c" +#include "bitarray.h" #include "util.h" diff --git a/libs/objsize/dune b/libs/objsize/dune index dfdbebcd..f40e56c1 100644 --- a/libs/objsize/dune +++ b/libs/objsize/dune @@ -2,8 +2,7 @@ (library (name objsize) - (c_names c_objsize) - (c_flags (-I../../../../libs/objsize)) ; TODO: This is stupid + (foreign_stubs (language c) (names c_objsize) (flags -O1 -W -Wall -Werror -fPIC)) + (flags :standard -warn-error -a+8) (wrapped false) - (modules objsize) -) \ No newline at end of file + (modules objsize)) diff --git a/libs/pcre/Makefile b/libs/pcre/Makefile deleted file mode 100644 index dcbad32f..00000000 --- a/libs/pcre/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -ALL_CFLAGS = $(CFLAGS) -I pcre -LIBS = -OCAMLOPT=ocamlopt -OCAMLC=ocamlc -SRC = pcre.ml pcre_stubs.c - -all: bytecode native - -bytecode: pcre.cma - -native: pcre.cmxa - -pcre.cma: pcre_stubs.o pcre.ml - $(OCAMLC) -safe-string -a -o pcre.cma $(LIBS) pcre.ml - -pcre.cmxa: pcre.ml pcre_stubs.o - $(OCAMLOPT) -safe-string -a -o pcre.cmxa $(LIBS) pcre.ml - -pcre_stubs.o: pcre_stubs.c - $(OCAMLC) -safe-string $(ALL_CFLAGS) pcre_stubs.c - -clean: - rm -f pcre.cma pcre.cmi pcre.cmx pcre.cmxa pcre.o pcre.obj pcre_stubs.obj pcre_stubs.o - rm -f pcre.a libpcre.a libpcre.lib pcre.cmo - -.PHONY: all bytecode native clean -Makefile: ; -$(SRC): ; diff --git a/libs/pcre/dune b/libs/pcre/dune deleted file mode 100644 index 68d4fee0..00000000 --- a/libs/pcre/dune +++ /dev/null @@ -1,7 +0,0 @@ -(include_subdirs no) - -(library - (name pcre) - (c_names pcre_stubs) - (wrapped false) -) \ No newline at end of file diff --git a/libs/pcre/pcre_stubs.c b/libs/pcre/pcre_stubs.c deleted file mode 100644 index 12496cf6..00000000 --- a/libs/pcre/pcre_stubs.c +++ /dev/null @@ -1,736 +0,0 @@ -/* - PCRE-OCAML - Perl Compatibility Regular Expressions for OCaml - - Copyright (C) 1999- Markus Mottl - email: markus.mottl@gmail.com - WWW: http://www.ocaml.info - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#if defined(_WIN32) -# if defined(_DLL) -# define PCREextern __declspec(dllexport) -# else -# define PCREextern -# endif -#endif - -#if _WIN64 - typedef long long *ovec_dst_ptr; -#else - typedef long *ovec_dst_ptr; -#endif - -#if __GNUC__ >= 3 -# define inline inline __attribute__ ((always_inline)) -# define __unused __attribute__ ((unused)) -#else -# define __unused -# define inline -#endif - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "pcre.h" - -/* Error codes as defined for pcre 7.9, undefined in pcre 4.5 */ -#ifndef PCRE_ERROR_PARTIAL -#define PCRE_ERROR_PARTIAL (-12) -#endif -#ifndef PCRE_ERROR_BADPARTIAL -#define PCRE_ERROR_BADPARTIAL (-13) -#endif -#ifndef PCRE_ERROR_RECURSIONLIMIT -#define PCRE_ERROR_RECURSIONLIMIT (-21) -#endif - -typedef const unsigned char *chartables; /* Type of chartable sets */ - -/* Contents of callout data */ -struct cod { - long subj_start; /* Start of subject string */ - value *v_substrings_p; /* Pointer to substrings matched so far */ - value *v_cof_p; /* Pointer to callout function */ - value v_exn; /* Possible exception raised by callout function */ -}; - -/* Cache for exceptions */ -static value *pcre_exc_Error = NULL; /* Exception [Error] */ -static value *pcre_exc_Backtrack = NULL; /* Exception [Backtrack] */ - -/* Cache for polymorphic variants */ -static value var_Start_only; /* Variant [`Start_only] */ -static value var_ANCHORED; /* Variant [`ANCHORED] */ -static value var_Char; /* Variant [`Char char] */ -static value var_Not_studied; /* Variant [`Not_studied] */ -static value var_Studied; /* Variant [`Studied] */ -static value var_Optimal; /* Variant [`Optimal] */ - -static value None = Val_int(0); - -/* Converts subject offsets from C-integers to OCaml-Integers. - - This is a bit tricky, because there are 32- and 64-bit platforms around - and OCaml chooses the larger possibility for representing integers when - available (also in arrays) - not so the PCRE! -*/ -static inline void copy_ovector( - long subj_start, const int *ovec_src, ovec_dst_ptr ovec_dst, int subgroups2) -{ - if (subj_start == 0) - while (subgroups2--) { - *ovec_dst = Val_int(*ovec_src); - --ovec_src; --ovec_dst; - } - else - while (subgroups2--) { - *ovec_dst = Val_long(*ovec_src + subj_start); - --ovec_src; --ovec_dst; - } -} - -/* Callout handler */ -static int pcre_callout_handler(pcre_callout_block* cb) -{ - struct cod *cod = (struct cod *) cb->callout_data; - - if (cod != NULL) { - /* Callout is available */ - value v_res; - - /* Set up parameter array */ - value v_callout_data = caml_alloc_small(8, 0); - - const value v_substrings = *cod->v_substrings_p; - - const int capture_top = cb->capture_top; - int subgroups2 = capture_top << 1; - const int subgroups2_1 = subgroups2 - 1; - - const int *ovec_src = cb->offset_vector + subgroups2_1; - ovec_dst_ptr ovec_dst = &Field(Field(v_substrings, 1), 0) + subgroups2_1; - long subj_start = cod->subj_start; - - copy_ovector(subj_start, ovec_src, ovec_dst, subgroups2); - - Field(v_callout_data, 0) = Val_int(cb->callout_number); - Field(v_callout_data, 1) = v_substrings; - Field(v_callout_data, 2) = Val_int(cb->start_match + subj_start); - Field(v_callout_data, 3) = Val_int(cb->current_position + subj_start); - Field(v_callout_data, 4) = Val_int(capture_top); - Field(v_callout_data, 5) = Val_int(cb->capture_last); - Field(v_callout_data, 6) = Val_int(cb->pattern_position); - Field(v_callout_data, 7) = Val_int(cb->next_item_length); - - /* Perform callout */ - v_res = caml_callback_exn(*cod->v_cof_p, v_callout_data); - - if (Is_exception_result(v_res)) { - /* Callout raised an exception */ - const value v_exn = Extract_exception(v_res); - if (Field(v_exn, 0) == *pcre_exc_Backtrack) return 1; - cod->v_exn = v_exn; - return PCRE_ERROR_CALLOUT; - } - } - - return 0; -} - -/* Fetchs the named OCaml-values + caches them and - calculates + caches the variant hash values */ -CAMLprim value pcre_ocaml_init(value __unused v_unit) -{ - pcre_exc_Error = caml_named_value("Pcre.Error"); - pcre_exc_Backtrack = caml_named_value("Pcre.Backtrack"); - - var_Start_only = caml_hash_variant("Start_only"); - var_ANCHORED = caml_hash_variant("ANCHORED"); - var_Char = caml_hash_variant("Char"); - var_Not_studied = caml_hash_variant("Not_studied"); - var_Studied = caml_hash_variant("Studied"); - var_Optimal = caml_hash_variant("Optimal"); - - pcre_callout = &pcre_callout_handler; - - return Val_unit; -} - -/* Finalizing deallocation function for chartable sets */ -static void pcre_dealloc_tables(value v_table) -{ (pcre_free)((void *) Field(v_table, 1)); } - -/* Finalizing deallocation function for compiled regular expressions */ -static void pcre_dealloc_regexp(value v_rex) -{ - void *extra = (void *) Field(v_rex, 2); - (pcre_free)((void *) Field(v_rex, 1)); - if (extra != NULL) -#ifdef PCRE_STUDY_JIT_COMPILE - pcre_free_study(extra); -#else - pcre_free(extra); -#endif -} - -/* Makes OCaml-string from PCRE-version */ -CAMLprim value pcre_version_stub(value __unused v_unit) -{ - return caml_copy_string((char *) pcre_version()); -} - - -/* Raising exceptions */ - -static inline void raise_pcre_error(value v_arg) Noreturn; -static inline void raise_partial() Noreturn; -static inline void raise_bad_partial() Noreturn; -static inline void raise_bad_utf8() Noreturn; -static inline void raise_bad_utf8_offset() Noreturn; -static inline void raise_match_limit() Noreturn; -static inline void raise_recursion_limit() Noreturn; -static inline void raise_bad_pattern(const char *msg, int pos) Noreturn; -static inline void raise_internal_error(char *msg) Noreturn; - -static inline void raise_pcre_error(value v_arg) -{ caml_raise_with_arg(*pcre_exc_Error, v_arg); } - -static inline void raise_partial() { raise_pcre_error(Val_int(0)); } -static inline void raise_bad_partial() { raise_pcre_error(Val_int(1)); } -static inline void raise_bad_utf8() { raise_pcre_error(Val_int(2)); } -static inline void raise_bad_utf8_offset() { raise_pcre_error(Val_int(3)); } -static inline void raise_match_limit() { raise_pcre_error(Val_int(4)); } -static inline void raise_recursion_limit() { raise_pcre_error(Val_int(5)); } - -static inline void raise_bad_pattern(const char *msg, int pos) -{ - CAMLparam0(); - CAMLlocal1(v_msg); - value v_arg; - v_msg = caml_copy_string(msg); - v_arg = caml_alloc_small(2, 0); - Field(v_arg, 0) = v_msg; - Field(v_arg, 1) = Val_int(pos); - raise_pcre_error(v_arg); - CAMLnoreturn; -} - -static inline void raise_internal_error(char *msg) -{ - CAMLparam0(); - CAMLlocal1(v_msg); - value v_arg; - v_msg = caml_copy_string(msg); - v_arg = caml_alloc_small(1, 1); - Field(v_arg, 0) = v_msg; - raise_pcre_error(v_arg); - CAMLnoreturn; -} - -/* PCRE pattern compilation */ - -/* Makes compiled regular expression from compilation options, an optional - value of chartables and the pattern string */ -CAMLprim value pcre_compile_stub(value v_opt, value v_tables, value v_pat) -{ - value v_rex; /* Final result -> value of type [regexp] */ - const char *error = NULL; /* pointer to possible error message */ - int error_ofs = 0; /* offset in the pattern at which error occurred */ - - /* If v_tables = [None], then pointer to tables is NULL, otherwise - set it to the appropriate value */ - chartables tables = - (v_tables == None) ? NULL : (chartables) Field(Field(v_tables, 0), 1); - - /* Compiles the pattern */ - pcre *regexp = pcre_compile(String_val(v_pat), Int_val(v_opt), &error, - &error_ofs, tables); - - /* Raises appropriate exception with [BadPattern] if the pattern - could not be compiled */ - if (regexp == NULL) raise_bad_pattern(error, error_ofs); - - /* GC will do a full cycle every 1_000_000 regexp allocations (a typical - regexp probably consumes less than 100 bytes -> maximum of 100_000_000 - bytes unreclaimed regexps) */ - v_rex = caml_alloc_final(4, pcre_dealloc_regexp, 1, 1000000); - - /* Field[1]: compiled regular expression (Field[0] is finalizing - function! See above!) */ - Field(v_rex, 1) = (value) regexp; - - /* Field[2]: extra information about regexp when it has been studied - successfully */ - Field(v_rex, 2) = (value) NULL; - - /* Field[3]: If 0 -> regexp has not yet been studied - 1 -> regexp has already been studied */ - Field(v_rex, 3) = 0; - - return v_rex; -} - -/* Studies a regexp */ -CAMLprim value pcre_study_stub(value v_rex) -{ - /* If it has not yet been studied */ - if (! (int) Field(v_rex, 3)) { - const char *error = NULL; - pcre_extra *extra = pcre_study((pcre *) Field(v_rex, 1), 0, &error); - if (error != NULL) caml_invalid_argument((char *) error); - Field(v_rex, 2) = (value) extra; - Field(v_rex, 3) = Val_int(1); - } - return v_rex; -} - -/* Sets a match limit recursion for a regular expression imperatively */ -CAMLprim value pcre_set_imp_match_limit_recursion_stub(value v_rex, value v_lim) -{ - pcre_extra *extra = (pcre_extra *) Field(v_rex, 2); - if (extra == NULL) { - extra = pcre_malloc(sizeof(pcre_extra)); - extra->flags = PCRE_EXTRA_MATCH_LIMIT_RECURSION; - Field(v_rex, 2) = (value) extra; - } else { - unsigned long *flags_ptr = &extra->flags; - *flags_ptr = PCRE_EXTRA_MATCH_LIMIT_RECURSION | *flags_ptr; - } - extra->match_limit_recursion = Int_val(v_lim); - return v_rex; -} - -/* Gets the match limit recursion of a regular expression if it exists */ -CAMLprim value pcre_get_match_limit_recursion_stub(value v_rex) -{ - pcre_extra *extra = (pcre_extra *) Field(v_rex, 2); - if (extra == NULL) return None; - if (extra->flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) { - value v_lim = Val_int(extra->match_limit_recursion); - value v_res = caml_alloc_small(1, 0); - Field(v_res, 0) = v_lim; - return v_res; - } - return None; -} - -/* Sets a match limit for a regular expression imperatively */ -CAMLprim value pcre_set_imp_match_limit_stub(value v_rex, value v_lim) -{ - pcre_extra *extra = (pcre_extra *) Field(v_rex, 2); - if (extra == NULL) { - extra = pcre_malloc(sizeof(pcre_extra)); - extra->flags = PCRE_EXTRA_MATCH_LIMIT; - Field(v_rex, 2) = (value) extra; - } else { - unsigned long *flags_ptr = &extra->flags; - *flags_ptr = PCRE_EXTRA_MATCH_LIMIT | *flags_ptr; - } - extra->match_limit = Int_val(v_lim); - return v_rex; -} - -/* Gets the match limit of a regular expression if it exists */ -CAMLprim value pcre_get_match_limit_stub(value v_rex) -{ - pcre_extra *extra = (pcre_extra *) Field(v_rex, 2); - if (extra == NULL) return None; - if (extra->flags & PCRE_EXTRA_MATCH_LIMIT) { - value v_lim = Val_int(extra->match_limit); - value v_res = caml_alloc_small(1, 0); - Field(v_res, 0) = v_lim; - return v_res; - } - return None; -} - -/* Performs the call to the pcre_fullinfo function */ -static inline int pcre_fullinfo_stub(value v_rex, int what, void *where) -{ - return pcre_fullinfo((pcre *) Field(v_rex, 1), (pcre_extra *) Field(v_rex, 2), - what, where); -} - -/* Some stubs for info-functions */ - -/* Generic macro for getting integer results from pcre_fullinfo */ -#define make_info(tp, cnv, name, option) \ - CAMLprim value pcre_##name##_stub(value v_rex) \ - { \ - tp options; \ - const int ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_##option, &options); \ - if (ret != 0) raise_internal_error("pcre_##name##_stub"); \ - return cnv(options); \ - } - -make_info(unsigned long, Val_long, options, OPTIONS) -make_info(size_t, Val_long, size, SIZE) -make_info(size_t, Val_long, studysize, STUDYSIZE) -make_info(int, Val_int, capturecount, CAPTURECOUNT) -make_info(int, Val_int, backrefmax, BACKREFMAX) -make_info(int, Val_int, namecount, NAMECOUNT) -make_info(int, Val_int, nameentrysize, NAMEENTRYSIZE) - -CAMLprim value pcre_firstbyte_stub(value v_rex) -{ - int firstbyte; - const int ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_FIRSTBYTE, &firstbyte); - - if (ret != 0) raise_internal_error("pcre_firstbyte_stub"); - - switch (firstbyte) { - case -1 : return var_Start_only; break; /* [`Start_only] */ - case -2 : return var_ANCHORED; break; /* [`ANCHORED] */ - default : - if (firstbyte < 0 ) /* Should not happen */ - raise_internal_error("pcre_firstbyte_stub"); - else { - value v_firstbyte; - /* Allocates the non-constant constructor [`Char of char] and fills - in the appropriate value */ - v_firstbyte = caml_alloc_small(2, 0); - Field(v_firstbyte, 0) = var_Char; - Field(v_firstbyte, 1) = Val_int(firstbyte); - return v_firstbyte; - } - } -} - -CAMLprim value pcre_firsttable_stub(value v_rex) -{ - const unsigned char *ftable; - - int ret = - pcre_fullinfo_stub(v_rex, PCRE_INFO_FIRSTTABLE, (void *) &ftable); - - if (ret != 0) raise_internal_error("pcre_firsttable_stub"); - - if (ftable == NULL) return None; - else { - value v_res, v_res_str; - char *ptr; - int i; - - Begin_roots1(v_rex); - v_res_str = caml_alloc_string(32); - End_roots(); - - ptr = String_val(v_res_str); - for (i = 0; i <= 31; ++i) { *ptr = *ftable; ++ptr; ++ftable; } - - Begin_roots1(v_res_str); - /* Allocates [Some string] from firsttable */ - v_res = caml_alloc_small(1, 0); - End_roots(); - - Field(v_res, 0) = v_res_str; - - return v_res; - } -} - -CAMLprim value pcre_lastliteral_stub(value v_rex) -{ - int lastliteral; - const int ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_LASTLITERAL, - &lastliteral); - - if (ret != 0) raise_internal_error("pcre_lastliteral_stub"); - - if (lastliteral == -1) return None; - if (lastliteral < 0) raise_internal_error("pcre_lastliteral_stub"); - else { - /* Allocates [Some char] */ - value v_res = caml_alloc_small(1, 0); - Field(v_res, 0) = Val_int(lastliteral); - return v_res; - } -} - -CAMLprim value pcre_study_stat_stub(value v_rex) -{ - /* Generates the appropriate constant constructor [`Optimal] or - [`Studied] if regexp has already been studied */ - if (Field(v_rex, 3)) - return ((pcre_extra *) Field(v_rex, 2) == NULL) ? var_Optimal : var_Studied; - - return var_Not_studied; /* otherwise [`Not_studied] */ -} - -static inline void handle_exec_error(char *loc, const int ret) Noreturn; - -static inline void handle_exec_error(char *loc, const int ret) -{ - switch (ret) { - /* Dedicated exceptions */ - case PCRE_ERROR_NOMATCH : caml_raise_not_found(); - case PCRE_ERROR_PARTIAL : raise_partial(); - case PCRE_ERROR_MATCHLIMIT : raise_match_limit(); - case PCRE_ERROR_BADPARTIAL : raise_bad_partial(); - case PCRE_ERROR_BADUTF8 : raise_bad_utf8(); - case PCRE_ERROR_BADUTF8_OFFSET : raise_bad_utf8_offset(); - case PCRE_ERROR_RECURSIONLIMIT : raise_recursion_limit(); - /* Unknown error */ - default : { - char err_buf[100]; - snprintf(err_buf, 100, "%s: unhandled PCRE error code: %d", loc, ret); - raise_internal_error(err_buf); - } - } -} - -static inline void handle_pcre_exec_result( - int *ovec, value v_ovec, long ovec_len, long subj_start, int ret) -{ - ovec_dst_ptr ocaml_ovec = (ovec_dst_ptr) &Field(v_ovec, 0); - const int subgroups2 = ret * 2; - const int subgroups2_1 = subgroups2 - 1; - const int *ovec_src = ovec + subgroups2_1; - ovec_dst_ptr ovec_clear_stop = ocaml_ovec + (ovec_len * 2) / 3; - ovec_dst_ptr ovec_dst = ocaml_ovec + subgroups2_1; - copy_ovector(subj_start, ovec_src, ovec_dst, subgroups2); - while (++ovec_dst < ovec_clear_stop) *ovec_dst = -1; -} - -/* Executes a pattern match with runtime options, a regular expression, a - matching position, the start of the the subject string, a subject string, - a number of subgroup offsets, an offset vector and an optional callout - function */ -CAMLprim value pcre_exec_stub(value v_opt, value v_rex, value v_pos, - value v_subj_start, value v_subj, - value v_ovec, value v_maybe_cof) -{ - int ret; - long - pos = Long_val(v_pos), - len = caml_string_length(v_subj), - subj_start = Long_val(v_subj_start); - long ovec_len = Wosize_val(v_ovec); - - if (pos > len || pos < subj_start) - caml_invalid_argument("Pcre.pcre_exec_stub: illegal position"); - - if (subj_start > len || subj_start < 0) - caml_invalid_argument("Pcre.pcre_exec_stub: illegal subject start"); - - pos -= subj_start; - len -= subj_start; - - { - const pcre *code = (pcre *) Field(v_rex, 1); /* Compiled pattern */ - const pcre_extra *extra = (pcre_extra *) Field(v_rex, 2); /* Extra info */ - const char *ocaml_subj = - String_val(v_subj) + subj_start; /* Subject string */ - const int opt = Int_val(v_opt); /* Runtime options */ - - /* Special case when no callout functions specified */ - if (v_maybe_cof == None) { - int *ovec = (int *) &Field(v_ovec, 0); - - /* Performs the match */ - ret = pcre_exec(code, extra, ocaml_subj, len, pos, opt, ovec, ovec_len); - - if (ret < 0) handle_exec_error("pcre_exec_stub", ret); - else handle_pcre_exec_result(ovec, v_ovec, ovec_len, subj_start, ret); - } - - /* There are callout functions */ - else { - value v_cof = Field(v_maybe_cof, 0); - value v_substrings; - char *subj = caml_stat_alloc(sizeof(char) * len); - int *ovec = caml_stat_alloc(sizeof(int) * ovec_len); - struct cod cod = { 0, (value *) NULL, (value *) NULL, (value) NULL }; - struct pcre_extra new_extra = -#ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION -# ifdef PCRE_EXTRA_MARK -# ifdef PCRE_EXTRA_EXECUTABLE_JIT - { PCRE_EXTRA_CALLOUT_DATA, NULL, 0, NULL, NULL, 0, NULL, NULL }; -# else - { PCRE_EXTRA_CALLOUT_DATA, NULL, 0, NULL, NULL, 0, NULL }; -# endif -# else - { PCRE_EXTRA_CALLOUT_DATA, NULL, 0, NULL, NULL, 0 }; -# endif -#else - { PCRE_EXTRA_CALLOUT_DATA, NULL, 0, NULL, NULL }; -#endif - - cod.subj_start = subj_start; - memcpy(subj, ocaml_subj, len); - - Begin_roots4(v_rex, v_cof, v_substrings, v_ovec); - Begin_roots1(v_subj); - v_substrings = caml_alloc_small(2, 0); - End_roots(); - - Field(v_substrings, 0) = v_subj; - Field(v_substrings, 1) = v_ovec; - - cod.v_substrings_p = &v_substrings; - cod.v_cof_p = &v_cof; - new_extra.callout_data = &cod; - - if (extra == NULL) { - ret = pcre_exec(code, &new_extra, subj, len, pos, opt, ovec, - ovec_len); - } - else { - new_extra.flags = PCRE_EXTRA_CALLOUT_DATA | extra->flags; - new_extra.study_data = extra->study_data; - new_extra.match_limit = extra->match_limit; - new_extra.tables = extra->tables; -#ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION - new_extra.match_limit_recursion = extra->match_limit_recursion; -#endif - - ret = pcre_exec(code, &new_extra, subj, len, pos, opt, ovec, - ovec_len); - } - - caml_stat_free(subj); - End_roots(); - - if (ret < 0) { - caml_stat_free(ovec); - if (ret == PCRE_ERROR_CALLOUT) caml_raise(cod.v_exn); - else handle_exec_error("pcre_exec_stub(callout)", ret); - } else { - handle_pcre_exec_result(ovec, v_ovec, ovec_len, subj_start, ret); - caml_stat_free(ovec); - } - } - } - - return Val_unit; -} - -/* Byte-code hook for pcre_exec_stub - Needed, because there are more than 5 arguments */ -CAMLprim value pcre_exec_stub_bc(value *argv, int __unused argn) -{ - return pcre_exec_stub(argv[0], argv[1], argv[2], argv[3], - argv[4], argv[5], argv[6]); -} - -/* Generates a new set of chartables for the current locale (see man - page of PCRE */ -CAMLprim value pcre_maketables_stub(value __unused v_unit) -{ - /* GC will do a full cycle every 1_000_000 table set allocations (one - table set consumes 864 bytes -> maximum of 864_000_000 bytes unreclaimed - table sets) */ - const value v_res = caml_alloc_final(2, pcre_dealloc_tables, 1, 1000000); - Field(v_res, 1) = (value) pcre_maketables(); - return v_res; -} - -/* Wraps around the isspace-function */ -CAMLprim value pcre_isspace_stub(value v_c) -{ - return Val_bool(isspace(Int_val(v_c))); -} - -/* Returns number of substring associated with a name */ -CAMLprim value pcre_get_stringnumber_stub(value v_rex, value v_name) -{ - const int ret = pcre_get_stringnumber((pcre *) Field(v_rex, 1), - String_val(v_name)); - if (ret == PCRE_ERROR_NOSUBSTRING) - caml_invalid_argument("Named string not found"); - - return Val_int(ret); -} - -/* Returns array of names of named substrings in a regexp */ -CAMLprim value pcre_names_stub(value v_rex) -{ - CAMLparam0(); - CAMLlocal1(v_res); - int name_count; - int entry_size; - const char *tbl_ptr; - int i; - - int ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_NAMECOUNT, &name_count); - if (ret != 0) raise_internal_error("pcre_names_stub: namecount"); - - ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_NAMEENTRYSIZE, &entry_size); - if (ret != 0) raise_internal_error("pcre_names_stub: nameentrysize"); - - ret = pcre_fullinfo_stub(v_rex, PCRE_INFO_NAMETABLE, &tbl_ptr); - if (ret != 0) raise_internal_error("pcre_names_stub: nametable"); - - v_res = caml_alloc(name_count, 0); - - for (i = 0; i < name_count; ++i) { - value v_name = caml_copy_string(tbl_ptr + 2); - Store_field(v_res, i, v_name); - tbl_ptr += entry_size; - } - - CAMLreturn(v_res); -} - -/* Generic stub for getting integer results from pcre_config */ -static inline int pcre_config_int(int what) -{ - int ret; - pcre_config(what, (void *) &ret); - return ret; -} - -/* Generic stub for getting long integer results from pcre_config */ -static inline int pcre_config_long(int what) -{ - long ret; - pcre_config(what, (void *) &ret); - return ret; -} - -/* Some stubs for config-functions */ - -/* Returns boolean indicating UTF8-support */ -CAMLprim value pcre_config_utf8_stub(value __unused v_unit) -{ return Val_bool(pcre_config_int(PCRE_CONFIG_UTF8)); } - -/* Returns character used as newline */ -CAMLprim value pcre_config_newline_stub(value __unused v_unit) -{ return Val_int(pcre_config_int(PCRE_CONFIG_NEWLINE)); } - -/* Returns number of bytes used for internal linkage of regular expressions */ -CAMLprim value pcre_config_link_size_stub(value __unused v_unit) -{ return Val_int(pcre_config_int(PCRE_CONFIG_LINK_SIZE)); } - -/* Returns boolean indicating use of stack recursion */ -CAMLprim value pcre_config_stackrecurse_stub(value __unused v_unit) -{ return Val_bool(pcre_config_int(PCRE_CONFIG_STACKRECURSE)); } - -/* Returns default limit for calls to internal matching function */ -CAMLprim value pcre_config_match_limit_stub(value __unused v_unit) -{ return Val_long(pcre_config_long(PCRE_CONFIG_MATCH_LIMIT)); } - -/* Returns default limit for calls to internal matching function */ -CAMLprim value pcre_config_match_limit_recursion_stub(value __unused v_unit) -{ return Val_long(pcre_config_long(PCRE_CONFIG_MATCH_LIMIT_RECURSION)); } diff --git a/libs/pcre2/Makefile b/libs/pcre2/Makefile new file mode 100644 index 00000000..ecdc441d --- /dev/null +++ b/libs/pcre2/Makefile @@ -0,0 +1,28 @@ +ALL_CFLAGS = $(CFLAGS) -I pcre2 +LIBS = +OCAMLOPT=ocamlopt +OCAMLC=ocamlc +SRC = pcre2.ml pcre2_stubs.c + +all: bytecode native + +bytecode: pcre2.cma + +native: pcre2.cmxa + +pcre2.cma: pcre2_stubs.o pcre2.ml + $(OCAMLC) -safe-string -a -o pcre2.cma $(LIBS) pcre2.ml + +pcre2.cmxa: pcre2.ml pcre2_stubs.o + $(OCAMLOPT) -safe-string -a -o pcre2.cmxa $(LIBS) pcre2.ml + +pcre2_stubs.o: pcre2_stubs.c + $(OCAMLC) -safe-string $(ALL_CFLAGS) pcre2_stubs.c + +clean: + rm -f pcre2.cma pcre2.cmi pcre2.cmx pcre2.cmxa pcre2.o pcre2.obj pcre2_stubs.obj pcre2_stubs.o + rm -f pcre2.a libpcre2-8.a libpcre2-8.lib pcre2.cmo + +.PHONY: all bytecode native clean +Makefile: ; +$(SRC): ; diff --git a/libs/pcre2/dune b/libs/pcre2/dune new file mode 100644 index 00000000..6f77d6ff --- /dev/null +++ b/libs/pcre2/dune @@ -0,0 +1,9 @@ +(include_subdirs no) + +(library + (name pcre2) + (foreign_stubs + (language c) + (names pcre2_stubs)) + (wrapped false) +) diff --git a/libs/pcre/pcre.ml b/libs/pcre2/pcre2.ml similarity index 70% rename from libs/pcre/pcre.ml rename to libs/pcre2/pcre2.ml index 1619666a..231478c1 100644 --- a/libs/pcre/pcre.ml +++ b/libs/pcre2/pcre2.ml @@ -1,16 +1,20 @@ (* - PCRE-OCAML - Perl Compatibility Regular Expressions for OCaml + PCRE2-OCAML - Perl Compatibility Regular Expressions for OCaml + Copyright (C) 1999- Markus Mottl email: markus.mottl@gmail.com WWW: http://www.ocaml.info + This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,17 +22,14 @@ (* Public exceptions and their registration with the C runtime *) -let string_copy str = str -let buffer_add_subbytes = Buffer.add_subbytes - type error = | Partial - | BadPartial | BadPattern of string * int - | BadUTF8 - | BadUTF8Offset + | BadUTF + | BadUTFOffset | MatchLimit - | RecursionLimit + | DepthLimit + | WorkspaceSize | InternalError of string exception Error of error @@ -36,87 +37,141 @@ exception Backtrack exception Regexp_or of string * error (* Puts exceptions into global C-variables for fast retrieval *) -external pcre_ocaml_init : unit -> unit = "pcre_ocaml_init" +external pcre2_ocaml_init : unit -> unit = "pcre2_ocaml_init" (* Registers exceptions with the C runtime and caches polymorphic variants *) let () = - Callback.register_exception "Pcre.Error" (Error (InternalError "")); - Callback.register_exception "Pcre.Backtrack" Backtrack; - pcre_ocaml_init () + Callback.register_exception "Pcre2.Error" (Error (InternalError "")); + Callback.register_exception "Pcre2.Backtrack" Backtrack; + pcre2_ocaml_init () (* Compilation and runtime flags and their conversion functions *) -type icflag = int -type irflag = int +type icflag = int64 +type irflag = int64 (* Compilation flags *) type cflag = [ + | `ALLOW_EMPTY_CLASS + | `ALT_BSUX + | `ALT_CIRCUMFLEX + | `ALT_VERBNAMES + | `ANCHORED + | `AUTO_CALLOUT | `CASELESS - | `MULTILINE + | `DOLLAR_ENDONLY | `DOTALL + | `DUPNAMES + | `ENDANCHORED | `EXTENDED - | `ANCHORED - | `DOLLAR_ENDONLY - | `EXTRA - | `UNGREEDY - | `UTF8 - | `NO_UTF8_CHECK - | `NO_AUTO_CAPTURE - | `AUTO_CALLOUT + | `EXTENDED_MORE | `FIRSTLINE + | `LITERAL + | `MATCH_INVALID_UTF + | `MATCH_UNSET_BACKREF + | `MULTILINE + | `NEVER_BACKSLASH_C + | `NEVER_UCP + | `NEVER_UTF + | `NO_AUTO_CAPTURE + | `NO_AUTO_POSSESS + | `NO_DOTSTAR_ANCHOR + | `NO_START_OPTIMIZE + | `NO_UTF_CHECK | `UCP + | `UNGREEDY + | `USE_OFFSET_LIMIT + | `UTF ] let int_of_cflag = function - | `CASELESS -> 0x0001 - | `MULTILINE -> 0x0002 - | `DOTALL -> 0x0004 - | `EXTENDED -> 0x0008 - | `ANCHORED -> 0x0010 - | `DOLLAR_ENDONLY -> 0x0020 - | `EXTRA -> 0x0040 - | `UNGREEDY -> 0x0200 - | `UTF8 -> 0x0800 - | `NO_AUTO_CAPTURE -> 0x1000 - | `NO_UTF8_CHECK -> 0x2000 - | `AUTO_CALLOUT -> 0x4000 - | `FIRSTLINE -> 0x40000 - | `UCP -> 0x20000000 - -let coll_icflag icflag flag = int_of_cflag flag lor icflag -let cflags flags = List.fold_left coll_icflag 0 flags + | `ALLOW_EMPTY_CLASS -> 0x00000001L + | `ALT_BSUX -> 0x00000002L + | `AUTO_CALLOUT -> 0x00000004L + | `CASELESS -> 0x00000008L + | `DOLLAR_ENDONLY -> 0x00000010L + | `DOTALL -> 0x00000020L + | `DUPNAMES -> 0x00000040L + | `EXTENDED -> 0x00000080L + | `FIRSTLINE -> 0x00000100L + | `MATCH_UNSET_BACKREF -> 0x00000200L + | `MULTILINE -> 0x00000400L + | `NEVER_UCP -> 0x00000800L + | `NEVER_UTF -> 0x00001000L + | `NO_AUTO_CAPTURE -> 0x00002000L + | `NO_AUTO_POSSESS -> 0x00004000L + | `NO_DOTSTAR_ANCHOR -> 0x00008000L + | `NO_START_OPTIMIZE -> 0x00010000L + | `UCP -> 0x00020000L + | `UNGREEDY -> 0x00040000L + | `UTF -> 0x00080000L + | `NEVER_BACKSLASH_C -> 0x00100000L + | `ALT_CIRCUMFLEX -> 0x00200000L + | `ALT_VERBNAMES -> 0x00400000L + | `USE_OFFSET_LIMIT -> 0x00800000L + | `EXTENDED_MORE -> 0x01000000L + | `LITERAL -> 0x02000000L + | `MATCH_INVALID_UTF -> 0x04000000L + | `ENDANCHORED -> 0x20000000L + | `NO_UTF_CHECK -> 0x40000000L + | `ANCHORED -> 0x80000000L + + +let coll_icflag icflag flag = Int64.logor (int_of_cflag flag) icflag +let cflags flags = List.fold_left coll_icflag 0L flags let cflag_of_int = function - | 0x0001 -> `CASELESS - | 0x0002 -> `MULTILINE - | 0x0004 -> `DOTALL - | 0x0008 -> `EXTENDED - | 0x0010 -> `ANCHORED - | 0x0020 -> `DOLLAR_ENDONLY - | 0x0040 -> `EXTRA - | 0x0200 -> `UNGREEDY - | 0x0800 -> `UTF8 - | 0x1000 -> `NO_AUTO_CAPTURE - | 0x2000 -> `NO_UTF8_CHECK - | 0x4000 -> `AUTO_CALLOUT - | 0x40000 -> `FIRSTLINE - | 0x20000000 -> `UCP - | _ -> failwith "Pcre.cflag_list: unknown compilation flag" + | 0x00000001L -> `ALLOW_EMPTY_CLASS + | 0x00000002L -> `ALT_BSUX + | 0x00000004L -> `AUTO_CALLOUT + | 0x00000008L -> `CASELESS + | 0x00000010L -> `DOLLAR_ENDONLY + | 0x00000020L -> `DOTALL + | 0x00000040L -> `DUPNAMES + | 0x00000080L -> `EXTENDED + | 0x00000100L -> `FIRSTLINE + | 0x00000200L -> `MATCH_UNSET_BACKREF + | 0x00000400L -> `MULTILINE + | 0x00000800L -> `NEVER_UCP + | 0x00001000L -> `NEVER_UTF + | 0x00002000L -> `NO_AUTO_CAPTURE + | 0x00004000L -> `NO_AUTO_POSSESS + | 0x00008000L -> `NO_DOTSTAR_ANCHOR + | 0x00010000L -> `NO_START_OPTIMIZE + | 0x00020000L -> `UCP + | 0x00040000L -> `UNGREEDY + | 0x00080000L -> `UTF + | 0x00100000L -> `NEVER_BACKSLASH_C + | 0x00200000L -> `ALT_CIRCUMFLEX + | 0x00400000L -> `ALT_VERBNAMES + | 0x00800000L -> `USE_OFFSET_LIMIT + | 0x01000000L -> `EXTENDED_MORE + | 0x02000000L -> `LITERAL + | 0x04000000L -> `MATCH_INVALID_UTF + | 0x20000000L -> `ENDANCHORED + | 0x40000000L -> `NO_UTF_CHECK + | 0x80000000L -> `ANCHORED + | _ -> failwith "Pcre2.cflag_list: unknown compilation flag" let all_cflags = [ - 0x0001; 0x0002; 0x0004; 0x0008; 0x0010; 0x0020; - 0x0040; 0x0200; 0x0800; 0x1000; 0x2000; 0x4000; 0x40000; - 0x20000000 + 0x00000001L; 0x00000002L; 0x00000004L; 0x00000008L; + 0x00000010L; 0x00000020L; 0x00000040L; 0x00000080L; + 0x00000100L; 0x00000200L; 0x00000400L; 0x00000800L; + 0x00001000L; 0x00002000L; 0x00004000L; 0x00008000L; + 0x00010000L; 0x00020000L; 0x00040000L; 0x00080000L; + 0x00100000L; 0x00200000L; 0x00400000L; 0x00800000L; + 0x01000000L; 0x02000000L; 0x04000000L; + 0x20000000L; 0x40000000L; 0x80000000L; ] let cflag_list icflags = let coll flag_list flag = - if icflags land flag <> 0 then cflag_of_int flag :: flag_list - else flag_list in + if Int64.equal (Int64.logand icflags flag) 0L then flag_list + else cflag_of_int flag :: flag_list in List.fold_left coll [] all_cflags @@ -125,144 +180,180 @@ let cflag_list icflags = type rflag = [ | `ANCHORED + | `COPY_MATCHED_SUBJECT + | `DFA_RESTART + | `DFA_SHORTEST + | `ENDANCHORED | `NOTBOL | `NOTEOL | `NOTEMPTY - | `PARTIAL + | `NOTEMPTY_ATSTART + | `NO_JIT + | `NO_UTF_CHECK + | `PARTIAL_HARD + | `PARTIAL_SOFT ] let int_of_rflag = function - | `ANCHORED -> 0x0010 - | `NOTBOL -> 0x0080 - | `NOTEOL -> 0x0100 - | `NOTEMPTY -> 0x0400 - | `PARTIAL -> 0x8000 - -let coll_irflag irflag flag = int_of_rflag flag lor irflag -let rflags flags = List.fold_left coll_irflag 0 flags + | `NOTBOL -> 0x00000001L + | `NOTEOL -> 0x00000002L + | `NOTEMPTY -> 0x00000004L + | `NOTEMPTY_ATSTART -> 0x00000008L + | `PARTIAL_SOFT -> 0x00000010L + | `PARTIAL_HARD -> 0x00000020L + | `DFA_RESTART -> 0x00000040L + | `DFA_SHORTEST -> 0x00000080L + | `NO_JIT -> 0x00002000L + | `COPY_MATCHED_SUBJECT -> 0x00004000L + | `ENDANCHORED -> 0x20000000L + | `NO_UTF_CHECK -> 0x40000000L + | `ANCHORED -> 0x80000000L + +let coll_irflag irflag flag = Int64.logor (int_of_rflag flag) irflag +let rflags flags = List.fold_left coll_irflag 0L flags let rflag_of_int = function - | 0x0010 -> `ANCHORED - | 0x0080 -> `NOTBOL - | 0x0100 -> `NOTEOL - | 0x0400 -> `NOTEMPTY - | 0x8000 -> `PARTIAL - | _ -> failwith "Pcre.rflag_list: unknown runtime flag" - -let all_rflags = [0x0010; 0x0080; 0x0100; 0x0400; 0x8000] + | 0x00000001L -> `NOTBOL + | 0x00000002L -> `NOTEOL + | 0x00000004L -> `NOTEMPTY + | 0x00000008L -> `NOTEMPTY_ATSTART + | 0x00000010L -> `PARTIAL_SOFT + | 0x00000020L -> `PARTIAL_HARD + | 0x00000040L -> `DFA_RESTART + | 0x00000080L -> `DFA_SHORTEST + | 0x00002000L -> `NO_JIT + | 0x00004000L -> `COPY_MATCHED_SUBJECT + | 0x20000000L -> `ENDANCHORED + | 0x40000000L -> `NO_UTF_CHECK + | 0x80000000L -> `ANCHORED + | _ -> failwith "Pcre2.rflag_list: unknown runtime flag" + +let all_rflags = + [ + 0x00000001L; 0x00000002L; 0x00000004L; 0x00000008L; + 0x00000010L; 0x00000020L; 0x00000040L; 0x00000080L; + 0x00002000L; 0x00004000L; + 0x20000000L; 0x40000000L; 0x80000000L; + ] let rflag_list irflags = let coll flag_list flag = - if irflags land flag <> 0 then rflag_of_int flag :: flag_list - else flag_list in + if Int64.equal (Int64.logand irflags flag) 0L then flag_list + else rflag_of_int flag :: flag_list in List.fold_left coll [] all_rflags -(* Information on the PCRE-configuration (build-time options) *) +(* Information on the PCRE2-configuration (build-time options) *) -external pcre_version : unit -> string = "pcre_version_stub" +external pcre2_version : unit -> string = "pcre2_version_stub" -external pcre_config_utf8 : unit -> bool = "pcre_config_utf8_stub" [@@noalloc] +external pcre2_config_unicode : unit -> bool + = "pcre2_config_unicode_stub" [@@noalloc] -external pcre_config_newline : - unit -> char = "pcre_config_newline_stub" [@@noalloc] +external pcre2_config_newline : unit -> char + = "pcre2_config_newline_stub" [@@noalloc] -external pcre_config_link_size : - unit -> int = "pcre_config_link_size_stub" [@@noalloc] +external pcre2_config_link_size : unit -> (int [@untagged]) + = "pcre2_config_link_size_stub_bc" "pcre2_config_link_size_stub" [@@noalloc] -external pcre_config_match_limit : - unit -> int = "pcre_config_match_limit_stub" [@@noalloc] +external pcre2_config_match_limit : unit -> (int [@untagged]) + = "pcre2_config_match_limit_stub_bc" "pcre2_config_match_limit_stub" + [@@noalloc] -external pcre_config_match_limit_recursion : - unit -> int = "pcre_config_match_limit_recursion_stub" [@@noalloc] +external pcre2_config_depth_limit : unit -> (int [@untagged]) + = "pcre2_config_depth_limit_stub_bc" "pcre2_config_depth_limit_stub" + [@@noalloc] -external pcre_config_stackrecurse : - unit -> bool = "pcre_config_stackrecurse_stub" [@@noalloc] +external pcre2_config_stackrecurse : + unit -> bool = "pcre2_config_stackrecurse_stub" [@@noalloc] -let version = pcre_version () -let config_utf8 = pcre_config_utf8 () -let config_newline = pcre_config_newline () -let config_link_size = pcre_config_link_size () -let config_match_limit = pcre_config_match_limit () -let config_match_limit_recursion = pcre_config_match_limit_recursion () -let config_stackrecurse = pcre_config_stackrecurse () +let version = pcre2_version () +let config_unicode = pcre2_config_unicode () +let config_newline = pcre2_config_newline () +let config_link_size = pcre2_config_link_size () +let config_match_limit = pcre2_config_match_limit () +let config_depth_limit = pcre2_config_depth_limit () +let config_stackrecurse = pcre2_config_stackrecurse () (* Information on patterns *) -type firstbyte_info = +type firstcodeunit_info = [ `Char of char | `Start_only | `ANCHORED ] -type study_stat = - [ `Not_studied - | `Studied - | `Optimal ] - type regexp -external options : regexp -> icflag = "pcre_options_stub" -external size : regexp -> int = "pcre_size_stub" -external studysize : regexp -> int = "pcre_studysize_stub" -external capturecount : regexp -> int = "pcre_capturecount_stub" -external backrefmax : regexp -> int = "pcre_backrefmax_stub" -external namecount : regexp -> int = "pcre_namecount_stub" -external names : regexp -> string array = "pcre_names_stub" -external nameentrysize : regexp -> int = "pcre_nameentrysize_stub" -external firstbyte : regexp -> firstbyte_info = "pcre_firstbyte_stub" -external firsttable : regexp -> string option = "pcre_firsttable_stub" -external lastliteral : regexp -> char option = "pcre_lastliteral_stub" -external study_stat : regexp -> study_stat = "pcre_study_stat_stub" [@@noalloc] +external options : regexp -> (icflag [@unboxed]) + = "pcre2_argoptions_stub_bc" "pcre2_argoptions_stub" + +external size : regexp -> (int [@untagged]) + = "pcre2_size_stub_bc" "pcre2_size_stub" + +external capturecount : regexp -> (int [@untagged]) + = "pcre2_capturecount_stub_bc" "pcre2_capturecount_stub" +external backrefmax : regexp -> (int [@untagged]) + = "pcre2_backrefmax_stub_bc" "pcre2_backrefmax_stub" + +external namecount : regexp -> (int [@untagged]) + = "pcre2_namecount_stub_bc" "pcre2_namecount_stub" + +external nameentrysize : regexp -> (int [@untagged]) + = "pcre2_nameentrysize_stub_bc" "pcre2_nameentrysize_stub" + +external names : regexp -> string array = "pcre2_names_stub" +external firstcodeunit : regexp -> firstcodeunit_info = "pcre2_firstcodeunit_stub" +external lastcodeunit : regexp -> char option = "pcre2_lastcodeunit_stub" (* Compilation of patterns *) type chtables -external maketables : unit -> chtables = "pcre_maketables_stub" +external maketables : unit -> chtables = "pcre2_maketables_stub" -(* Internal use only! *) -external pcre_study : regexp -> unit = "pcre_study_stub" +external compile : (icflag [@unboxed]) -> chtables option -> string -> regexp + = "pcre2_compile_stub_bc" "pcre2_compile_stub" -external compile : - icflag -> chtables option -> string -> regexp = "pcre_compile_stub" - -external get_match_limit : regexp -> int option = "pcre_get_match_limit_stub" +(* external get_match_limit : regexp -> int option = "pcre2_get_match_limit_stub" *) (* Internal use only! *) -external set_imp_match_limit : - regexp -> int -> regexp = "pcre_set_imp_match_limit_stub" [@@noalloc] +external set_imp_match_limit : regexp -> (int [@untagged]) -> regexp + = "pcre2_set_imp_match_limit_stub_bc" "pcre2_set_imp_match_limit_stub" + [@@noalloc] -external get_match_limit_recursion : - regexp -> int option = "pcre_get_match_limit_recursion_stub" +(* external get_depth_limit : + regexp -> int option = "pcre2_get_depth_limit_stub" *) (* Internal use only! *) -external set_imp_match_limit_recursion : - regexp -> int -> regexp = "pcre_set_imp_match_limit_recursion_stub" [@@noalloc] +external set_imp_depth_limit : regexp -> (int [@untagged]) -> regexp + = "pcre2_set_imp_depth_limit_stub_bc" "pcre2_set_imp_depth_limit_stub" + [@@noalloc] +(* TODO implement jit using new pcre2_jit_compile api *) let regexp - ?(study = true) ?limit ?limit_recursion - ?(iflags = 0) ?flags ?chtables pat = + (* ?(jit_compile = false) *) + ?limit ?depth_limit + ?(iflags = 0L) ?flags ?chtables pat = let rex = match flags with | Some flag_list -> compile (cflags flag_list) chtables pat | _ -> compile iflags chtables pat in - if study then pcre_study rex; let rex = match limit with | None -> rex | Some lim -> set_imp_match_limit rex lim in - match limit_recursion with + match depth_limit with | None -> rex - | Some lim -> set_imp_match_limit_recursion rex lim + | Some lim -> set_imp_depth_limit rex lim let regexp_or - ?study ?limit ?limit_recursion ?(iflags = 0) ?flags ?chtables pats = + (* ?jit_compile *) ?limit ?depth_limit ?(iflags = 0L) ?flags ?chtables pats = let check pat = - try ignore (regexp ~study:false ~iflags ?flags ?chtables pat) + try ignore (regexp ~iflags ?flags ?chtables pat) with Error error -> raise (Regexp_or (pat, error)) in List.iter check pats; @@ -270,7 +361,7 @@ let regexp_or let cnv pat = "(?:" ^ pat ^ ")" in String.concat "|" (List.rev (List.rev_map cnv pats)) in - regexp ?study ?limit ?limit_recursion ~iflags ?flags ?chtables big_pat + regexp (* ?jit_compile *) ?limit ?depth_limit ~iflags ?flags ?chtables big_pat let bytes_unsafe_blit_string str str_ofs bts bts_ofs len = let str_bts = Bytes.unsafe_of_string str in @@ -301,7 +392,7 @@ let quote s = (* Matching of patterns and subpattern extraction *) (* Default regular expression when none is provided by the user *) -let def_rex = regexp "\\s+" +let def_rex = regexp (* ~jit_compile:true *) "\\s+" type substrings = string * int array @@ -325,7 +416,7 @@ let num_of_subs (_, ovector) = Array.length ovector / 3 let get_offset_start ovector str_num = if str_num < 0 || str_num >= Array.length ovector / 3 then - invalid_arg "Pcre.get_offset_start: illegal offset"; + invalid_arg "Pcre2.get_offset_start: illegal offset"; let offset = str_num lsl 1 in offset, Array.unsafe_get ovector offset @@ -370,8 +461,10 @@ let get_opt_substrings ?(full_match = true) (_, ovector as substrings) = let len = (Array.length ovector / 3) - 1 in Array.init len (fun n -> unsafe_get_opt_substring substrings (n + 1)) -external get_stringnumber : - regexp -> string -> int = "pcre_get_stringnumber_stub" +external get_stringnumber : regexp -> string -> (int [@untagged]) + = + "pcre2_substring_number_from_name_stub_bc" + "pcre2_substring_number_from_name_stub" let get_named_substring rex name substrings = get_substring substrings (get_stringnumber rex name) @@ -379,48 +472,68 @@ let get_named_substring rex name substrings = let get_named_substring_ofs rex name substrings = get_substring_ofs substrings (get_stringnumber rex name) -external unsafe_pcre_exec : - irflag -> +external unsafe_pcre2_match : + (irflag [@unboxed]) -> regexp -> - pos : int -> - subj_start : int -> + pos : (int [@untagged]) -> + subj_start : (int [@untagged]) -> subj : string -> int array -> callout option -> - unit = "pcre_exec_stub_bc" "pcre_exec_stub" + unit = "pcre2_match_stub_bc" "pcre2_match_stub" let make_ovector rex = let subgroups1 = capturecount rex + 1 in let subgroups2 = subgroups1 lsl 1 in subgroups2, Array.make (subgroups1 + subgroups2) 0 -let pcre_exec ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?(pos = 0) +external unsafe_pcre2_dfa_match : + (irflag [@unboxed]) -> + regexp -> + pos : (int [@untagged]) -> + subj_start : (int [@untagged]) -> + subj : string -> + int array -> + callout option -> + workspace : int array -> + unit = "pcre2_dfa_match_stub_bc" "pcre2_match_stub0" + +let pcre2_dfa_match ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) + ?callout ?(workspace = Array.make 20 0) subj = + let rex = match pat with Some str -> regexp str | _ -> rex in + let iflags = match flags with Some flags -> rflags flags | _ -> iflags in + let _, ovector = make_ovector rex in + unsafe_pcre2_dfa_match + iflags rex ~pos ~subj_start:0 ~subj ovector callout ~workspace; + ovector + +let pcre2_match ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let _, ovector = make_ovector rex in - unsafe_pcre_exec iflags rex ~pos ~subj_start:0 ~subj ovector callout; + unsafe_pcre2_match iflags rex ~pos ~subj_start:0 ~subj ovector callout; ovector let exec ?iflags ?flags ?rex ?pat ?pos ?callout subj = - subj, pcre_exec ?iflags ?flags ?rex ?pat ?pos ?callout subj + subj, pcre2_match ?iflags ?flags ?rex ?pat ?pos ?callout subj let next_match ?iflags ?flags ?rex ?pat ?(pos = 0) ?callout (subj, ovector) = let pos = Array.unsafe_get ovector 1 + pos in let subj_len = String.length subj in if pos < 0 || pos > subj_len then - invalid_arg "Pcre.next_match: illegal offset"; - subj, pcre_exec ?iflags ?flags ?rex ?pat ~pos ?callout subj + invalid_arg "Pcre2.next_match: illegal offset"; + subj, pcre2_match ?iflags ?flags ?rex ?pat ~pos ?callout subj let rec copy_lst ar n = function | [] -> ar | h :: t -> Array.unsafe_set ar n h; copy_lst ar (n - 1) t -let exec_all ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?pos ?callout subj = +let exec_all ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?pos ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let (_, ovector as sstrs) = exec ~iflags ~rex ?pos ?callout subj in - let null_flags = iflags lor 0x0400 in + let null_flags = Int64.logor iflags 0x00000004L in (* `NOTEMPTY *) let subj_len = String.length subj in let rec loop pos (subj, ovector as sstrs) n lst = let maybe_ovector = @@ -428,8 +541,8 @@ let exec_all ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?pos ?callout subj = let first = Array.unsafe_get ovector 0 in if first = pos && Array.unsafe_get ovector 1 = pos then if pos = subj_len then None - else Some (pcre_exec ~iflags:null_flags ~rex ~pos ?callout subj) - else Some (pcre_exec ~iflags ~rex ~pos ?callout subj) + else Some (pcre2_match ~iflags:null_flags ~rex ~pos ?callout subj) + else Some (pcre2_match ~iflags ~rex ~pos ?callout subj) with Not_found -> None in match maybe_ovector with | Some ovector -> @@ -454,7 +567,7 @@ let extract_all_opt ?iflags ?flags ?rex ?pat ?pos ?full_match ?callout subj = Array.map (get_opt_substrings ?full_match) many_sstrs let pmatch ?iflags ?flags ?rex ?pat ?pos ?callout subj = - try ignore (pcre_exec ?iflags ?flags ?rex ?pat ?pos ?callout subj); true + try ignore (pcre2_match ?iflags ?flags ?rex ?pat ?pos ?callout subj); true with Not_found -> false @@ -559,7 +672,7 @@ let calc_trans_lst subgroups2 ovector subj templ subst_lst = return_lst (subj, !ix, Array.unsafe_get ovector (!pos + 1) - !ix) in List.fold_left coll (0, []) subst_lst -let replace ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let replace ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?(itempl = def_subst) ?templ ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in @@ -568,17 +681,17 @@ let replace ?(iflags = 0) ?flags ?(rex = def_rex) ?pat | Some str -> subst str | _ -> itempl in let subj_len = String.length subj in - if pos < 0 || pos > subj_len then invalid_arg "Pcre.replace: illegal offset"; + if pos < 0 || pos > subj_len then invalid_arg "Pcre2.replace: illegal offset"; let subgroups2, ovector = make_ovector rex in let nsubs = (subgroups2 lsr 1) - 1 in if max_br > nsubs then - failwith "Pcre.replace: backreference denotes nonexistent subpattern"; - if with_lp && nsubs = 0 then failwith "Pcre.replace: no backreferences"; + failwith "Pcre2.replace: backreference denotes nonexistent subpattern"; + if with_lp && nsubs = 0 then failwith "Pcre2.replace: no backreferences"; let rec loop full_len trans_lsts cur_pos = if cur_pos > subj_len || try - unsafe_pcre_exec + unsafe_pcre2_match iflags rex ~pos:cur_pos ~subj_start:0 ~subj ovector callout; false @@ -617,19 +730,19 @@ let replace ?(iflags = 0) ?flags ?(rex = def_rex) ?pat else loop full_len trans_lsts last in loop 0 [] pos -let qreplace ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let qreplace ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?(templ = "") ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let subj_len = String.length subj in - if pos < 0 || pos > subj_len then invalid_arg "Pcre.qreplace: illegal offset"; + if pos < 0 || pos > subj_len then invalid_arg "Pcre2.qreplace: illegal offset"; let templ_len = String.length templ in let _, ovector = make_ovector rex in let rec loop full_len subst_lst cur_pos = if cur_pos > subj_len || try - unsafe_pcre_exec + unsafe_pcre2_match iflags rex ~pos:cur_pos ~subj_start:0 ~subj ovector callout; false with Not_found -> true @@ -666,18 +779,18 @@ let qreplace ?(iflags = 0) ?flags ?(rex = def_rex) ?pat else loop full_len subst_lst last in loop 0 [] pos -let substitute_substrings ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let substitute_substrings ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?callout ~subst subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let subj_len = String.length subj in - if pos < 0 || pos > subj_len then invalid_arg "Pcre.substitute: illegal offset"; + if pos < 0 || pos > subj_len then invalid_arg "Pcre2.substitute: illegal offset"; let _, ovector = make_ovector rex in let rec loop full_len subst_lst cur_pos = if cur_pos > subj_len || try - unsafe_pcre_exec + unsafe_pcre2_match iflags rex ~pos:cur_pos ~subj_start:0 ~subj ovector callout; false with Not_found -> true @@ -719,7 +832,7 @@ let substitute ?iflags ?flags ?rex ?pat ?pos ?callout ~subst:str_subst subj = str_subst (string_unsafe_sub subj first (last - first)) in substitute_substrings ?iflags ?flags ?rex ?pat ?pos ?callout ~subst subj -let replace_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?(pos = 0) +let replace_first ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?(itempl = def_subst) ?templ ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in @@ -730,10 +843,10 @@ let replace_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?(pos = 0) let subgroups2, ovector = make_ovector rex in let nsubs = (subgroups2 lsr 1) - 1 in if max_br > nsubs then - failwith "Pcre.replace_first: backreference denotes nonexistent subpattern"; - if with_lp && nsubs = 0 then failwith "Pcre.replace_first: no backreferences"; + failwith "Pcre2.replace_first: backreference denotes nonexistent subpattern"; + if with_lp && nsubs = 0 then failwith "Pcre2.replace_first: no backreferences"; try - unsafe_pcre_exec iflags rex ~pos ~subj_start:0 ~subj ovector callout; + unsafe_pcre2_match iflags rex ~pos ~subj_start:0 ~subj ovector callout; let res_len, trans_lst = calc_trans_lst subgroups2 ovector subj templ subst_lst in let first = Array.unsafe_get ovector 0 in @@ -746,15 +859,15 @@ let replace_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat ?(pos = 0) let ofs = List.fold_left coll first trans_lst in bytes_unsafe_blit_string subj last res ofs rest; Bytes.unsafe_to_string res - with Not_found -> string_copy subj + with Not_found -> subj -let qreplace_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let qreplace_first ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?(templ = "") ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let _, ovector = make_ovector rex in try - unsafe_pcre_exec iflags rex ~pos ~subj_start:0 ~subj ovector callout; + unsafe_pcre2_match iflags rex ~pos ~subj_start:0 ~subj ovector callout; let first = Array.unsafe_get ovector 0 in let last = Array.unsafe_get ovector 1 in let len = String.length templ in @@ -765,15 +878,15 @@ let qreplace_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat bytes_unsafe_blit_string templ 0 res first len; bytes_unsafe_blit_string subj last res postfix_start rest; Bytes.unsafe_to_string res - with Not_found -> string_copy subj + with Not_found -> subj -let substitute_substrings_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let substitute_substrings_first ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?callout ~subst subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let _, ovector = make_ovector rex in try - unsafe_pcre_exec iflags rex ~pos ~subj_start:0 ~subj ovector callout; + unsafe_pcre2_match iflags rex ~pos ~subj_start:0 ~subj ovector callout; let subj_len = String.length subj in let prefix_len = Array.unsafe_get ovector 0 in let last = Array.unsafe_get ovector 1 in @@ -786,7 +899,7 @@ let substitute_substrings_first ?(iflags = 0) ?flags ?(rex = def_rex) ?pat bytes_unsafe_blit_string templ 0 res prefix_len templ_len; bytes_unsafe_blit_string subj last res postfix_start postfix_len; Bytes.unsafe_to_string res - with Not_found -> string_copy subj + with Not_found -> subj let substitute_first ?iflags ?flags ?rex ?pat ?pos ?callout ~subst:str_subst subj = @@ -803,7 +916,7 @@ let substitute_first ?iflags ?flags ?rex ?pat ?pos let internal_psplit flags rex max pos callout subj = let subj_len = String.length subj in if subj_len = 0 then [] - else if max = 1 then [string_copy subj] + else if max = 1 then [subj] else let subgroups2, ovector = make_ovector rex in @@ -831,7 +944,7 @@ let internal_psplit flags rex max pos callout subj = if cnt = 0 then if prematch && try - unsafe_pcre_exec + unsafe_pcre2_match flags rex ~pos ~subj_start:pos ~subj ovector callout; true with Not_found -> false @@ -845,7 +958,7 @@ let internal_psplit flags rex max pos callout subj = else if try - unsafe_pcre_exec + unsafe_pcre2_match flags rex ~pos ~subj_start:pos ~subj ovector callout; false with Not_found -> true @@ -859,8 +972,9 @@ let internal_psplit flags rex max pos callout subj = if len = 0 then "" :: strs else if try - unsafe_pcre_exec - (flags lor 0x0410) rex ~pos ~subj_start:pos ~subj + unsafe_pcre2_match + (* `ANCHORED | `NOTEMPTY *) + (Int64.logor flags 0x80000004L) rex ~pos ~subj_start:pos ~subj ovector callout; true with Not_found -> false @@ -880,13 +994,13 @@ let internal_psplit flags rex max pos callout subj = let rec strip_all_empty = function "" :: t -> strip_all_empty t | l -> l -external isspace : char -> bool = "pcre_isspace_stub" [@@noalloc] +external isspace : char -> bool = "pcre2_isspace_stub" [@@noalloc] let rec find_no_space ix len str = if ix = len || not (isspace (String.unsafe_get str ix)) then ix else find_no_space (ix + 1) len str -let split ?(iflags = 0) ?flags ?rex ?pat ?(pos = 0) ?(max = 0) ?callout subj = +let split ?(iflags = 0L) ?flags ?rex ?pat ?(pos = 0) ?(max = 0) ?callout subj = let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let res = match pat, rex with @@ -895,7 +1009,7 @@ let split ?(iflags = 0) ?flags ?rex ?pat ?(pos = 0) ?(max = 0) ?callout subj = | _ -> (* special case for Perl-splitting semantics *) let len = String.length subj in - if pos > len || pos < 0 then failwith "Pcre.split: illegal offset"; + if pos > len || pos < 0 then failwith "Pcre2.split: illegal offset"; let new_pos = find_no_space pos len subj in internal_psplit iflags def_rex max new_pos callout subj in List.rev (if max = 0 then strip_all_empty res else res) @@ -915,13 +1029,13 @@ let rec strip_all_empty_full = function | Delim _ :: rest -> strip_all_empty_full rest | l -> l -let full_split ?(iflags = 0) ?flags ?(rex = def_rex) ?pat +let full_split ?(iflags = 0L) ?flags ?(rex = def_rex) ?pat ?(pos = 0) ?(max = 0) ?callout subj = let rex = match pat with Some str -> regexp str | _ -> rex in let iflags = match flags with Some flags -> rflags flags | _ -> iflags in let subj_len = String.length subj in if subj_len = 0 then [] - else if max = 1 then [Text (string_copy subj)] + else if max = 1 then [Text (subj)] else let subgroups2, ovector = make_ovector rex in @@ -952,7 +1066,7 @@ let full_split ?(iflags = 0) ?flags ?(rex = def_rex) ?pat if cnt = 0 then if prematch && try - unsafe_pcre_exec + unsafe_pcre2_match iflags rex ~pos ~subj_start:pos ~subj ovector callout; true with Not_found -> false @@ -970,7 +1084,7 @@ let full_split ?(iflags = 0) ?flags ?(rex = def_rex) ?pat else if try - unsafe_pcre_exec + unsafe_pcre2_match iflags rex ~pos ~subj_start:pos ~subj ovector callout; false with Not_found -> true @@ -987,8 +1101,9 @@ let full_split ?(iflags = 0) ?flags ?(rex = def_rex) ?pat let empty_groups = handle_subgroups [] in if try - unsafe_pcre_exec - (iflags lor 0x0410) rex ~pos ~subj_start:pos ~subj + unsafe_pcre2_match + (* `ANCHORED | `NOTEMPTY *) + (Int64.logor iflags 0x80000004L) rex ~pos ~subj_start:pos ~subj ovector callout; true with Not_found -> false @@ -1031,4 +1146,4 @@ let foreach_file filenames f = let file = open_in filename in try f filename file; close_in file with exn -> close_in file; raise exn in - List.iter do_with_file filenames \ No newline at end of file + List.iter do_with_file filenames diff --git a/libs/pcre2/pcre2_stubs.c b/libs/pcre2/pcre2_stubs.c new file mode 100644 index 00000000..91989f56 --- /dev/null +++ b/libs/pcre2/pcre2_stubs.c @@ -0,0 +1,791 @@ +/* + PCRE2-OCAML - Perl Compatibility Regular Expressions for OCaml + + Copyright (C) 1999- Markus Mottl + email: markus.mottl@gmail.com + WWW: http://www.ocaml.info + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#if defined(_WIN32) +# define snprintf _snprintf +# if defined(_DLL) +# define PCREextern __declspec(dllexport) +# else +# define PCREextern +# endif +#endif + +#if _WIN64 + typedef long long *caml_int_ptr; +#else + typedef long *caml_int_ptr; +#endif + +#if __GNUC__ >= 3 +# define __unused __attribute__ ((unused)) +#else +# define __unused +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define PCRE2_CODE_UNIT_WIDTH 8 + +#include + +typedef const unsigned char *chartables; /* Type of chartable sets */ + +/* Contents of callout data */ +struct cod { + long subj_start; /* Start of subject string */ + value *v_substrings_p; /* Pointer to substrings matched so far */ + value *v_cof_p; /* Pointer to callout function */ + value v_exn; /* Possible exception raised by callout function */ +}; + +/* Cache for exceptions */ +static const value *pcre2_exc_Error = NULL; /* Exception [Error] */ +static const value *pcre2_exc_Backtrack = NULL; /* Exception [Backtrack] */ + +/* Cache for polymorphic variants */ +static value var_Start_only; /* Variant [`Start_only] */ +static value var_ANCHORED; /* Variant [`ANCHORED] */ +static value var_Char; /* Variant [`Char char] */ + +static value None = Val_int(0); + +/* Data associated with OCaml values of PCRE regular expression */ +struct pcre2_ocaml_regexp { pcre2_code *rex; pcre2_match_context *mcontext; }; + +#define Pcre2_ocaml_regexp_val(v) \ + ((struct pcre2_ocaml_regexp *) Data_custom_val(v)) + +#define get_rex(v) Pcre2_ocaml_regexp_val(v)->rex +#define get_mcontext(v) Pcre2_ocaml_regexp_val(v)->mcontext + +#define set_rex(v, r) Pcre2_ocaml_regexp_val(v)->rex = r +#define set_mcontext(v, c) Pcre2_ocaml_regexp_val(v)->mcontext = c + +/* Data associated with OCaml values of PCRE tables */ +struct pcre2_ocaml_tables { chartables tables; }; + +#define Pcre2_ocaml_tables_val(v) \ + ((struct pcre2_ocaml_tables *) Data_custom_val(v)) + +#define get_tables(v) Pcre2_ocaml_tables_val(v)->tables +#define set_tables(v, t) Pcre2_ocaml_tables_val(v)->tables = t + +/* Converts subject offsets from C-integers to OCaml-Integers. + + This is a bit tricky, because there are 32- and 64-bit platforms around + and OCaml chooses the larger possibility for representing integers when + available (also in arrays) - not so the PCRE! +*/ +static inline void copy_ovector( + long subj_start, const size_t* ovec_src, caml_int_ptr ovec_dst, uint32_t subgroups2) +{ + if (subj_start == 0) + while (subgroups2--) { + *ovec_dst = Val_int(*ovec_src); + --ovec_src; --ovec_dst; + } + else + while (subgroups2--) { + *ovec_dst = Val_long(*ovec_src + subj_start); + --ovec_src; --ovec_dst; + } +} + +/* Callout handler */ +static int pcre2_callout_handler(pcre2_callout_block* cb, struct cod* cod) +{ + if (cod != NULL) { + /* Callout is available */ + value v_res; + + /* Set up parameter array */ + value v_callout_data = caml_alloc_small(8, 0); + + const value v_substrings = *cod->v_substrings_p; + + const uint32_t capture_top = cb->capture_top; + uint32_t subgroups2 = capture_top << 1; + const uint32_t subgroups2_1 = subgroups2 - 1; + + const size_t *ovec_src = cb->offset_vector + subgroups2_1; + caml_int_ptr ovec_dst = &Field(Field(v_substrings, 1), 0) + subgroups2_1; + long subj_start = cod->subj_start; + + copy_ovector(subj_start, ovec_src, ovec_dst, subgroups2); + + Field(v_callout_data, 0) = Val_int(cb->callout_number); + Field(v_callout_data, 1) = v_substrings; + Field(v_callout_data, 2) = Val_int(cb->start_match + subj_start); + Field(v_callout_data, 3) = Val_int(cb->current_position + subj_start); + Field(v_callout_data, 4) = Val_int(capture_top); + Field(v_callout_data, 5) = Val_int(cb->capture_last); + Field(v_callout_data, 6) = Val_int(cb->pattern_position); + Field(v_callout_data, 7) = Val_int(cb->next_item_length); + + /* Perform callout */ + v_res = caml_callback_exn(*cod->v_cof_p, v_callout_data); + + if (Is_exception_result(v_res)) { + /* Callout raised an exception */ + const value v_exn = Extract_exception(v_res); + if (Field(v_exn, 0) == *pcre2_exc_Backtrack) return 1; + cod->v_exn = v_exn; + return PCRE2_ERROR_CALLOUT; + } + } + + return 0; +} + +/* Fetches the named OCaml-values + caches them and + calculates + caches the variant hash values */ +CAMLprim value pcre2_ocaml_init(value __unused v_unit) +{ + pcre2_exc_Error = caml_named_value("Pcre2.Error"); + pcre2_exc_Backtrack = caml_named_value("Pcre2.Backtrack"); + + var_Start_only = caml_hash_variant("Start_only"); + var_ANCHORED = caml_hash_variant("ANCHORED"); + var_Char = caml_hash_variant("Char"); + + return Val_unit; +} + +/* Finalizing deallocation function for chartable sets */ +static void pcre2_dealloc_tables(value v_tables) +{ +#if PCRE2_MINOR >= 34 + pcre2_maketables_free(NULL, get_tables(v_tables)); +#else + free((void*)get_tables(v_tables)); +#endif +} + +/* Finalizing deallocation function for compiled regular expressions */ +static void pcre2_dealloc_regexp(value v_rex) +{ + pcre2_code_free(get_rex(v_rex)); + pcre2_match_context_free(get_mcontext(v_rex)); +} + + +/* Raising exceptions */ + +CAMLnoreturn_start +static inline void raise_pcre2_error(value v_arg) +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_partial() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_bad_utf() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_bad_utf_offset() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_match_limit() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_depth_limit() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_workspace_size() +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_bad_pattern(int code, size_t pos) +CAMLnoreturn_end; + +CAMLnoreturn_start +static inline void raise_internal_error(char *msg) +CAMLnoreturn_end; + +static inline void raise_pcre2_error(value v_arg) +{ caml_raise_with_arg(*pcre2_exc_Error, v_arg); } + +static inline void raise_partial() { raise_pcre2_error(Val_int(0)); } +static inline void raise_bad_utf() { raise_pcre2_error(Val_int(1)); } +static inline void raise_bad_utf_offset() { raise_pcre2_error(Val_int(2)); } +static inline void raise_match_limit() { raise_pcre2_error(Val_int(3)); } +static inline void raise_depth_limit() { raise_pcre2_error(Val_int(4)); } +static inline void raise_workspace_size() { raise_pcre2_error(Val_int(5)); } + +static inline void raise_bad_pattern(int code, size_t pos) +{ + CAMLparam0(); + CAMLlocal1(v_msg); + value v_arg; + v_msg = caml_alloc_string(128); + pcre2_get_error_message(code, (PCRE2_UCHAR *)String_val(v_msg), 128); + v_arg = caml_alloc_small(2, 0); + Field(v_arg, 0) = v_msg; + Field(v_arg, 1) = Val_int(pos); + raise_pcre2_error(v_arg); + CAMLnoreturn; +} + +static inline void raise_internal_error(char *msg) +{ + CAMLparam0(); + CAMLlocal1(v_msg); + value v_arg; + v_msg = caml_copy_string(msg); + v_arg = caml_alloc_small(1, 1); + Field(v_arg, 0) = v_msg; + raise_pcre2_error(v_arg); + CAMLnoreturn; +} + +/* PCRE pattern compilation */ + +static struct custom_operations regexp_ops = { + "pcre2_ocaml_regexp", + pcre2_dealloc_regexp, + custom_compare_default, + custom_hash_default, + custom_serialize_default, + custom_deserialize_default, + custom_compare_ext_default +}; + +/* Makes compiled regular expression from compilation options, an optional + value of chartables and the pattern string */ + +CAMLprim value pcre2_compile_stub(int64_t v_opt, value v_tables, value v_pat) +{ + value v_rex; /* Final result -> value of type [regexp] */ + int error_code = 0; /* error code for potential error */ + size_t error_ofs = 0; /* offset in the pattern at which error occurred */ + size_t length = caml_string_length(v_pat); + + pcre2_compile_context* ccontext = NULL; + /* If v_tables = [None], then pointer to tables is NULL, otherwise + set it to the appropriate value */ + if (v_tables != None) { + ccontext = pcre2_compile_context_create(NULL); + pcre2_set_character_tables(ccontext, get_tables(Field(v_tables, 0))); + } + + /* Compiles the pattern */ + pcre2_code* regexp = pcre2_compile((PCRE2_SPTR)String_val(v_pat), length, v_opt, + &error_code, &error_ofs, ccontext); + + pcre2_compile_context_free(ccontext); + + /* Raises appropriate exception with [BadPattern] if the pattern + could not be compiled */ + if (regexp == NULL) raise_bad_pattern(error_code, error_ofs); + + /* GC will do a full cycle every 1_000_000 regexp allocations (a typical + regexp probably consumes less than 100 bytes -> maximum of 100_000_000 + bytes unreclaimed regexps) */ + v_rex = + caml_alloc_custom(®exp_ops, + sizeof(struct pcre2_ocaml_regexp), 1, 1000000); + + set_rex(v_rex, regexp); + set_mcontext(v_rex, pcre2_match_context_create(NULL)); + + return v_rex; +} + +CAMLprim value pcre2_compile_stub_bc(value v_opt, value v_tables, value v_pat) +{ + return pcre2_compile_stub(Long_val(v_opt), v_tables, v_pat); +} + +/* Gets the depth limit of a regular expression if it exists */ +/* CAMLprim value pcre2_get_depth_limit_stub(value v_rex); */ + +/* Gets the match limit of a regular expression if it exists */ +/* CAMLprim value pcre2_get_match_limit_stub(value v_rex); */ + + +/* Sets a match limit for a regular expression imperatively */ + +CAMLprim value pcre2_set_imp_match_limit_stub(value v_rex, value v_lim) { + pcre2_match_context* mcontext = get_mcontext(v_rex); + pcre2_set_match_limit(mcontext, v_lim); + return v_rex; +} + +CAMLprim value pcre2_set_imp_match_limit_stub_bc(value v_rex, value v_lim) +{ + return pcre2_set_imp_match_limit_stub(v_rex, Int_val(v_lim)); +} + + +/* Sets a depth limit for a regular expression imperatively */ + +CAMLprim value pcre2_set_imp_depth_limit_stub(value v_rex, intnat v_lim) { + pcre2_match_context* mcontext = get_mcontext(v_rex); + pcre2_set_depth_limit(mcontext, v_lim); + return v_rex; +} + +CAMLprim value pcre2_set_imp_depth_limit_stub_bc(value v_rex, value v_lim) +{ + return pcre2_set_imp_depth_limit_stub(v_rex, Int_val(v_lim)); +} + + +/* Performs the call to the pcre2_pattern_info function */ +static inline int pcre2_pattern_info_stub(value v_rex, int what, void* where) +{ + return pcre2_pattern_info(get_rex(v_rex), what, where); +} + +/* Some stubs for info-functions */ + +/* Generic macro for getting integer results from pcre2_pattern_info */ +#define make_intnat_info(tp, name, option) \ + CAMLprim intnat pcre2_##name##_stub(value v_rex) \ + { \ + tp options; \ + const int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_##option, &options); \ + if (ret != 0) raise_internal_error("pcre2_##name##_stub"); \ + return options; \ + } \ + \ + CAMLprim value pcre2_##name##_stub_bc(value v_rex) \ + { return Val_int(pcre2_##name##_stub(v_rex)); } + +make_intnat_info(size_t, size, SIZE) +make_intnat_info(int, capturecount, CAPTURECOUNT) +make_intnat_info(int, backrefmax, BACKREFMAX) +make_intnat_info(int, namecount, NAMECOUNT) +make_intnat_info(int, nameentrysize, NAMEENTRYSIZE) + +CAMLprim int64_t pcre2_argoptions_stub(value v_rex) +{ + uint32_t options; + const int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_ARGOPTIONS, &options); + if (ret != 0) raise_internal_error("pcre2_##name##_stub"); + return (int64_t)options; +} + +CAMLprim value pcre2_argoptions_stub_bc(value v_rex) +{ return Val_long(pcre2_argoptions_stub(v_rex)); } + +CAMLprim value pcre2_firstcodeunit_stub(value v_rex) +{ + uint32_t firstcodetype; + const int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_FIRSTCODETYPE, &firstcodetype); + + if (ret != 0) raise_internal_error("pcre2_firstcodeunit_stub"); + + switch (firstcodetype) { + case 2 : return var_Start_only; break; /* [`Start_only] */ + case 0 : return var_ANCHORED; break; /* [`ANCHORED] */ + case 1: { + uint32_t firstcodeunit; + const int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_FIRSTCODEUNIT, &firstcodeunit); + if (ret != 0) raise_internal_error("pcre2_firstcodeunit_stub"); + + value v_firstbyte; + /* Allocates the non-constant constructor [`Char of char] and fills + in the appropriate value */ + v_firstbyte = caml_alloc_small(2, 0); + Field(v_firstbyte, 0) = var_Char; + Field(v_firstbyte, 1) = Val_int(firstcodeunit); + + return v_firstbyte; + break; + } + default: /* Should not happen */ + raise_internal_error("pcre2_firstcodeunit_stub"); + } +} + +CAMLprim value pcre2_lastcodeunit_stub(value v_rex) +{ + uint32_t lastcodetype; + const int ret = + pcre2_pattern_info_stub(v_rex, PCRE2_INFO_LASTCODETYPE, &lastcodetype); + + if (ret != 0) raise_internal_error("pcre2_lastcodeunit_stub"); + + if (lastcodetype == 0) return None; + if (lastcodetype != 1) raise_internal_error("pcre2_lastcodeunit_stub"); + else { + /* Allocates [Some char] */ + value v_res = caml_alloc_small(1, 0); + uint32_t lastcodeunit; + const int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_LASTCODEUNIT, &lastcodeunit); + if (ret != 0) raise_internal_error("pcre2_lastcodeunit_stub"); + Field(v_res, 0) = Val_int(lastcodeunit); + return v_res; + } +} + +CAMLnoreturn_start +static inline void handle_match_error(char *loc, const int ret) +CAMLnoreturn_end; + +static inline void handle_match_error(char *loc, const int ret) +{ + switch (ret) { + /* Dedicated exceptions */ + case PCRE2_ERROR_NOMATCH : caml_raise_not_found(); + case PCRE2_ERROR_PARTIAL : raise_partial(); + case PCRE2_ERROR_MATCHLIMIT : raise_match_limit(); + case PCRE2_ERROR_BADUTFOFFSET : raise_bad_utf_offset(); + case PCRE2_ERROR_DEPTHLIMIT : raise_depth_limit(); + case PCRE2_ERROR_DFA_WSSIZE : raise_workspace_size(); + default : { + if (PCRE2_ERROR_UTF8_ERR21 <= ret && ret <= PCRE2_ERROR_UTF8_ERR1) + raise_bad_utf(); + /* Unknown error */ + char err_buf[100]; + snprintf(err_buf, 100, "%s: unhandled PCRE2 error code: %d", loc, ret); + raise_internal_error(err_buf); + } + } +} + +static inline void handle_pcre2_match_result( + size_t *ovec, value v_ovec, size_t ovec_len, long subj_start, uint32_t ret) +{ + caml_int_ptr ocaml_ovec = (caml_int_ptr) &Field(v_ovec, 0); + const uint32_t subgroups2 = ret * 2; + const uint32_t subgroups2_1 = subgroups2 - 1; + const size_t *ovec_src = ovec + subgroups2_1; + caml_int_ptr ovec_clear_stop = ocaml_ovec + (ovec_len * 2) / 3; + caml_int_ptr ovec_dst = ocaml_ovec + subgroups2_1; + copy_ovector(subj_start, ovec_src, ovec_dst, subgroups2); + while (++ovec_dst < ovec_clear_stop) *ovec_dst = -1; +} + +/* Executes a pattern match with runtime options, a regular expression, a + matching position, the start of the the subject string, a subject string, + a number of subgroup offsets, an offset vector and an optional callout + function */ + +CAMLprim value pcre2_match_stub0( + int64_t v_opt, value v_rex, intnat v_pos, intnat v_subj_start, value v_subj, + value v_ovec, value v_maybe_cof, value v_workspace) +{ + int ret; + int is_dfa = v_workspace != (value) NULL; + long + pos = v_pos, + subj_start = v_subj_start; + size_t + ovec_len = Wosize_val(v_ovec), + len = caml_string_length(v_subj); + + if (pos > (long)len || pos < subj_start) + caml_invalid_argument("Pcre2.pcre2_match_stub: illegal position"); + + if (subj_start > (long)len || subj_start < 0) + caml_invalid_argument("Pcre2.pcre2_match_stub: illegal subject start"); + + pos -= subj_start; + len -= subj_start; + + { + const pcre2_code *code = get_rex(v_rex); /* Compiled pattern */ + pcre2_match_context* mcontext = get_mcontext(v_rex); /* Match context */ + PCRE2_SPTR ocaml_subj = (PCRE2_SPTR)String_val(v_subj) + subj_start; /* Subject string */ + + pcre2_match_data* match_data = pcre2_match_data_create_from_pattern(code, NULL); + + /* Special case when no callout functions specified */ + if (v_maybe_cof == None) { + /* Performs the match */ + if (is_dfa) + ret = + pcre2_dfa_match(code, ocaml_subj, len, pos, v_opt, match_data, mcontext, + (int *) &Field(v_workspace, 0), Wosize_val(v_workspace)); + else + ret = pcre2_match(code, ocaml_subj, len, pos, v_opt, match_data, mcontext); + + size_t *ovec = pcre2_get_ovector_pointer(match_data); + + if (ret < 0) { + pcre2_match_data_free(match_data); + handle_match_error("pcre2_match_stub", ret); + } else { + handle_pcre2_match_result(ovec, v_ovec, ovec_len, subj_start, ret); + } + } + + /* There are callout functions */ + else { + value v_cof = Field(v_maybe_cof, 0); + value v_substrings; + PCRE2_UCHAR* subj = caml_stat_alloc(sizeof(char) * len); + int workspace_len; + int *workspace; + struct cod cod = { 0, (value *) NULL, (value *) NULL, (value) NULL }; + pcre2_match_context* new_mcontext = pcre2_match_context_copy(mcontext); + + pcre2_set_callout(new_mcontext, (int (*)(pcre2_callout_block_8*, void*))&pcre2_callout_handler, &cod); + + cod.subj_start = subj_start; + memcpy(subj, ocaml_subj, len); + + Begin_roots4(v_rex, v_cof, v_substrings, v_ovec); + Begin_roots1(v_subj); + v_substrings = caml_alloc_small(2, 0); + End_roots(); + + Field(v_substrings, 0) = v_subj; + Field(v_substrings, 1) = v_ovec; + + cod.v_substrings_p = &v_substrings; + cod.v_cof_p = &v_cof; + + if (is_dfa) { + workspace_len = Wosize_val(v_workspace); + workspace = caml_stat_alloc(sizeof(int) * workspace_len); + ret = + pcre2_dfa_match(code, subj, len, pos, v_opt, match_data, new_mcontext, + (int *) &Field(v_workspace, 0), workspace_len); + } else + ret = + pcre2_match(code, subj, len, pos, v_opt, match_data, new_mcontext); + + caml_stat_free(subj); + End_roots(); + + pcre2_match_context_free(new_mcontext); + size_t* ovec = pcre2_get_ovector_pointer(match_data); + if (ret < 0) { + if (is_dfa) caml_stat_free(workspace); + pcre2_match_data_free(match_data); + if (ret == PCRE2_ERROR_CALLOUT) caml_raise(cod.v_exn); + else handle_match_error("pcre2_match_stub(callout)", ret); + } else { + handle_pcre2_match_result(ovec, v_ovec, ovec_len, subj_start, ret); + if (is_dfa) { + caml_int_ptr ocaml_workspace_dst = + (caml_int_ptr) &Field(v_workspace, 0); + const int *workspace_src = workspace; + const int *workspace_src_stop = workspace + workspace_len; + while (workspace_src != workspace_src_stop) { + *ocaml_workspace_dst = *workspace_src; + ocaml_workspace_dst++; + workspace_src++; + } + caml_stat_free(workspace); + } + } + } + pcre2_match_data_free(match_data); + } + + return Val_unit; +} + +CAMLprim value pcre2_match_stub( + int64_t v_opt, value v_rex, intnat v_pos, intnat v_subj_start, value v_subj, + value v_ovec, value v_maybe_cof) +{ + return pcre2_match_stub0(v_opt, v_rex, v_pos, v_subj_start, v_subj, + v_ovec, v_maybe_cof, (value) NULL); +} + +/* Byte-code hook for pcre2_match_stub + Needed, because there are more than 5 arguments */ +CAMLprim value pcre2_match_stub_bc(value *argv, int __unused argn) +{ + return + pcre2_match_stub0( + Long_val(argv[0]), argv[1], Int_val(argv[2]), Int_val(argv[3]), + argv[4], argv[5], argv[6], (value) NULL); +} + +/* Byte-code hook for pcre2_dfa_match_stub + Needed, because there are more than 5 arguments */ +CAMLprim value pcre2_dfa_match_stub_bc(value *argv, int __unused argn) +{ + return + pcre2_match_stub0( + Long_val(argv[0]), argv[1], Int_val(argv[2]), Int_val(argv[3]), + argv[4], argv[5], argv[6], argv[7]); +} + +static struct custom_operations tables_ops = { + "pcre2_ocaml_tables", + pcre2_dealloc_tables, + custom_compare_default, + custom_hash_default, + custom_serialize_default, + custom_deserialize_default, + custom_compare_ext_default +}; + +/* Generates a new set of chartables for the current locale (see man + page of PCRE */ +CAMLprim value pcre2_maketables_stub(value __unused v_unit) +{ + /* GC will do a full cycle every 1_000_000 table set allocations (one + table set consumes 864 bytes -> maximum of 864_000_000 bytes unreclaimed + table sets) */ + const value v_tables = + caml_alloc_custom( + &tables_ops, sizeof(struct pcre2_ocaml_tables), 1, 1000000); + set_tables(v_tables, pcre2_maketables(NULL)); + return v_tables; +} + +/* Wraps around the isspace-function */ +CAMLprim value pcre2_isspace_stub(value v_c) +{ + return Val_bool(isspace(Int_val(v_c))); +} + + +/* Returns number of substring associated with a name */ + +CAMLprim intnat pcre2_substring_number_from_name_stub(value v_rex, value v_name) +{ + const int ret = pcre2_substring_number_from_name(get_rex(v_rex), (PCRE2_SPTR)String_val(v_name)); + if (ret == PCRE2_ERROR_NOSUBSTRING) + caml_invalid_argument("Named string not found"); + + return ret; +} + +CAMLprim value pcre2_substring_number_from_name_stub_bc(value v_rex, value v_name) +{ + return Val_int(pcre2_substring_number_from_name_stub(v_rex, v_name)); +} + + +/* Returns array of names of named substrings in a regexp */ +CAMLprim value pcre2_names_stub(value v_rex) +{ + CAMLparam1(v_rex); + CAMLlocal1(v_res); + uint32_t name_count; + uint32_t entry_size; + const char *tbl_ptr; + uint32_t i; + + int ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_NAMECOUNT, &name_count); + if (ret != 0) raise_internal_error("pcre2_names_stub: namecount"); + + ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_NAMEENTRYSIZE, &entry_size); + if (ret != 0) raise_internal_error("pcre2_names_stub: nameentrysize"); + + ret = pcre2_pattern_info_stub(v_rex, PCRE2_INFO_NAMETABLE, &tbl_ptr); + if (ret != 0) raise_internal_error("pcre2_names_stub: nametable"); + + v_res = caml_alloc(name_count, 0); + + for (i = 0; i < name_count; ++i) { + value v_name = caml_copy_string(tbl_ptr + 2); + Store_field(v_res, i, v_name); + tbl_ptr += entry_size; + } + + CAMLreturn(v_res); +} + +/* Generic stub for getting integer results from pcre2_config */ +static inline int pcre2_config_int(int what) +{ + int ret; + pcre2_config(what, (void *) &ret); + return ret; +} + +/* Generic stub for getting long integer results from pcre2_config */ +static inline long pcre2_config_long(int what) +{ + long ret; + pcre2_config(what, (void *) &ret); + return ret; +} + + +/* Some stubs for config-functions */ + +/* Makes OCaml-string from PCRE-version */ +CAMLprim value pcre2_version_stub(value __unused v_unit) { + CAMLparam1(v_unit); + CAMLlocal1(v_version); + v_version = caml_alloc_string(32); + + pcre2_config(PCRE2_CONFIG_VERSION, (void *)String_val(v_version)); + + CAMLreturn(v_version); +} + +/* Returns boolean indicating unicode support */ +CAMLprim value pcre2_config_unicode_stub(value __unused v_unit) +{ return Val_bool(pcre2_config_int(PCRE2_CONFIG_UNICODE)); } + + +/* Returns character used as newline */ +CAMLprim value pcre2_config_newline_stub(value __unused v_unit) +{ return Val_int(pcre2_config_int(PCRE2_CONFIG_NEWLINE)); } + + +/* Returns number of bytes used for internal linkage of regular expressions */ + +CAMLprim intnat pcre2_config_link_size_stub(value __unused v_unit) +{ return pcre2_config_int(PCRE2_CONFIG_LINKSIZE); } + +CAMLprim value pcre2_config_link_size_stub_bc(value v_unit) +{ return Val_int(pcre2_config_link_size_stub(v_unit)); } + + +/* Returns default limit for calls to internal matching function */ + +CAMLprim intnat pcre2_config_match_limit_stub(value __unused v_unit) +{ return pcre2_config_long(PCRE2_CONFIG_MATCHLIMIT); } + +CAMLprim value pcre2_config_match_limit_stub_bc(value v_unit) +{ return Val_int(pcre2_config_match_limit_stub(v_unit)); } + + +/* Returns default limit for depth of nested backtracking */ + +CAMLprim intnat pcre2_config_depth_limit_stub(value __unused v_unit) +{ return pcre2_config_long(PCRE2_CONFIG_DEPTHLIMIT); } + +CAMLprim value pcre2_config_depth_limit_stub_bc(value v_unit) +{ return Val_int(pcre2_config_depth_limit_stub(v_unit)); } + + +/* Returns boolean indicating use of stack recursion */ +CAMLprim intnat pcre2_config_stackrecurse_stub(value __unused v_unit) +{ return Val_bool(pcre2_config_int(PCRE2_CONFIG_STACKRECURSE)); } diff --git a/libs/swflib/as3hlparse.ml b/libs/swflib/as3hlparse.ml index 3a39db20..3aa51998 100644 --- a/libs/swflib/as3hlparse.ml +++ b/libs/swflib/as3hlparse.ml @@ -886,6 +886,7 @@ let flatten t = Array.iter (browse_field ctx) s.hls_fields; browse_method ctx s.hls_method; ) t; + let classes = List.sort (fun c1 c2 -> c1.hlc_index - c2.hlc_index) (List.rev !classes) in let methods = List.sort (fun m1 m2 -> m1.hlmt_index - m2.hlmt_index) (List.rev !methods) in (* done *) let rec ctx = { @@ -898,7 +899,7 @@ let flatten t = fnames = new_lookup flatten_name; fmetas = new_lookup flatten_meta; fmethods = new_index_lookup methods flatten_method; - fclasses = new_index_lookup (List.rev !classes) flatten_class; + fclasses = new_index_lookup classes flatten_class; fjumps = []; ffunctions = []; } in diff --git a/opam b/opam index 3ecb84f1..2304be27 100644 --- a/opam +++ b/opam @@ -1,6 +1,6 @@ opam-version: "2.0" name: "haxe" -version: "4.2.4" +version: "4.3.0" synopsis: "Multi-target universal programming language" description: """ Haxe is an open source toolkit based on a modern, @@ -22,15 +22,14 @@ depends: [ "ocaml" {>= "4.02"} "ocamlfind" {build} "dune" {>= "1.11"} - "camlp5" {build & = "8.00~alpha05"} + "camlp5" {build & = "8.00"} "sedlex" {>= "2.0"} "xml-light" - "extlib" {>= "1.7.6"} + "extlib" {>= "1.7.8"} "ptmap" {>= "2.0.0"} "sha" - "conf-libpcre" + "conf-libpcre2-8" "conf-zlib" - "conf-mbedtls" "conf-neko" - "luv" {<= "0.5.8"} (* see https://github.com/aantron/luv/issues/121 *) -] \ No newline at end of file + "luv" +] diff --git a/src-json/define.json b/src-json/define.json index 6ced016e..bffcb26c 100644 --- a/src-json/define.json +++ b/src-json/define.json @@ -1,7 +1,7 @@ [ { "name": "AbsolutePath", - "define": "absolute_path", + "define": "absolute-path", "doc": "Print absolute file path in trace output." }, { @@ -12,28 +12,34 @@ }, { "name": "AnalyzerOptimize", - "define": "analyzer_optimize", + "define": "analyzer-optimize", "doc": "Perform advanced optimizations." }, + { + "name": "AnalyzerTimes", + "define": "analyzer-times", + "doc": "Record detailed timers for the analyzer", + "params": ["level: 0 | 1 | 2"] + }, { "name": "AnnotateSource", - "define": "annotate_source", + "define": "annotate-source", "doc": "Add additional comments to generated source code.", "platforms": ["cpp"] }, { "name": "CheckXmlProxy", - "define": "check_xml_proxy", + "define": "check-xml-proxy", "doc": "Check the used fields of the XML proxy." }, { "name": "CoreApi", - "define": "core_api", + "define": "core-api", "doc": "Defined in the core API context." }, { "name": "CoreApiSerialize", - "define": "core_api_serialize", + "define": "core-api-serialize", "doc": "Mark some generated core API classes with the `Serializable` attribute on C#.", "platforms": ["cs"] }, @@ -44,7 +50,7 @@ }, { "name": "CsVer", - "define": "cs_ver", + "define": "cs-ver", "doc": "The C# version to target.", "platforms": ["cs"], "params": ["version"] @@ -63,7 +69,7 @@ }, { "name": "DceDebug", - "define": "dce_debug", + "define": "dce-debug", "doc": "Show DCE log.", "links": ["https://haxe.org/manual/cr-dce.html"] }, @@ -74,7 +80,7 @@ }, { "name": "DisableUnicodeStrings", - "define": "disable_unicode_strings", + "define": "disable-unicode-strings", "doc": "Disable Unicode support in `String` type.", "platforms": ["cpp"] }, @@ -86,24 +92,24 @@ }, { "name": "DisplayStdin", - "define": "display_stdin", + "define": "display-stdin", "doc": "Read the contents of a file specified in `--display` from standard input." }, { "name": "DllExport", - "define": "dll_export", + "define": "dll-export", "doc": "GenCPP experimental linking.", "platforms": ["cpp"] }, { "name": "DllImport", - "define": "dll_import", + "define": "dll-import", "doc": "Handle Haxe-generated .NET DLL imports.", "platforms": ["cs"] }, { "name": "DocGen", - "define": "doc_gen", + "define": "doc-gen", "doc": "Do not perform any removal/change in order to correctly generate documentation." }, { @@ -114,65 +120,78 @@ }, { "name": "DumpPath", - "define": "dump_path", + "define": "dump-path", "doc": "Path to generate dumps to (default: \"dump\").", "params": ["path"] }, { "name": "DumpDependencies", - "define": "dump_dependencies", + "define": "dump-dependencies", "doc": "Dump the classes dependencies in a dump subdirectory." }, { "name": "DumpIgnoreVarIds", - "define": "dump_ignore_var_ids", + "define": "dump-ignore-var-ids", "doc": "Remove variable IDs from non-pretty dumps (helps with diff)." }, { "name": "DynamicInterfaceClosures", - "define": "dynamic_interface_closures", + "define": "dynamic-interface-closures", "doc": "Use slow path for interface closures to save space.", "platforms": ["cpp"] }, { "name": "EraseGenerics", - "define": "erase_generics", + "define": "erase-generics", "doc": "Erase generic classes on C#.", "platforms": ["cs"] }, { "name": "EvalCallStackDepth", - "define": "eval_call_stack_depth", + "define": "eval-call-stack-depth", "doc": "Set maximum call stack depth for eval. (default: 1000)", "platforms": ["eval"], "params": ["depth"] }, { "name": "EvalDebugger", - "define": "eval_debugger", - "doc": "Support debugger in macro/interp mode. Allows `host:port` value to open a socket. Implies eval_stack.", + "define": "eval-debugger", + "doc": "Support debugger in macro/interp mode. Allows `host:port` value to open a socket. Implies eval-stack.", + "platforms": ["eval"] + }, + { + "name": "EvalPrintDepth", + "define": "eval-print-depth", + "doc": "Set maximum print depth (before replacing with '<...>') for eval. (default: 5)", + "platforms": ["eval"], + "params": ["depth"] + }, + { + "name": "EvalPrettyPrint", + "define": "eval-pretty-print", + "doc": "Enable indented output for eval printing.", "platforms": ["eval"] }, { "name": "EvalStack", - "define": "eval_stack", + "define": "eval-stack", "doc": "Record stack information in macro/interp mode.", "platforms": ["eval"] }, { "name": "EvalTimes", - "define": "eval_times", - "doc": "Record per-method execution times in macro/interp mode. Implies eval_stack.", + "define": "eval-times", + "doc": "Record per-method execution times in macro/interp mode. Implies eval-stack.", "platforms": ["eval"] }, { "name": "FilterTimes", - "define": "filter_times", + "define": "filter-times", "doc": "Record per-filter execution times upon --times." }, { "name": "FastCast", - "define": "fast_cast", + "define": "fast-cast", "doc": "Enables an experimental casts cleanup on C# and Java.", "platforms": ["cs", "java"] }, @@ -184,38 +203,38 @@ }, { "name": "FileExtension", - "define": "file_extension", + "define": "file-extension", "doc": "Output filename extension for cpp source code.", "platforms": ["cpp"] }, { "name": "FlashStrict", - "define": "flash_strict", + "define": "flash-strict", "doc": "More strict typing for flash target.", "platforms": ["flash"] }, { "name": "FlashUseStage", - "define": "flash_use_stage", + "define": "flash-use-stage", "doc": "Keep the SWF library initial stage.", "platforms": ["flash"] }, { - "devcomment": "force_lib_check is only here as a debug facility - compiler checking allows errors to be found more easily", + "devcomment": "force-lib-check is only here as a debug facility - compiler checking allows errors to be found more easily", "name": "ForceLibCheck", - "define": "force_lib_check", - "doc": "Force the compiler to check `--net-lib` and `–-java-lib` added classes (internal).", + "define": "force-lib-check", + "doc": "Force the compiler to check `--net-lib` and `--java-lib` added classes (internal).", "platforms": ["cs", "java"] }, { "name": "ForceNativeProperty", - "define": "force_native_property", + "define": "force-native-property", "doc": "Tag all properties with `:nativeProperty` metadata for 3.1 compatibility.", "platforms": ["cpp"] }, { "name": "GencommonDebug", - "define": "gencommon_debug", + "define": "gencommon-debug", "doc": "GenCommon internal.", "platforms": ["cs", "java"] }, @@ -226,13 +245,13 @@ }, { "name": "HaxeBoot", - "define": "haxe_boot", + "define": "haxe-boot", "doc": "Give the name 'haxe' to the flash boot class instead of a generated name.", "platforms": ["flash"] }, { "name": "HaxeVer", - "define": "haxe_ver", + "define": "haxe-ver", "doc": "The current Haxe version value as decimal number. E.g. 3.407 for 3.4.7.", "reserved": true }, @@ -242,41 +261,46 @@ "doc": "The current Haxe version value in SemVer format.", "reserved": true }, + { + "name": "HaxeNext", + "define": "haxe-next", + "doc": "Enable experimental features that are meant to be released on next Haxe version." + }, { "name": "HlVer", - "define": "hl_ver", + "define": "hl-ver", "doc": "The HashLink version to target. (default: 1.10.0)", "platforms": ["hl"], "params": ["version"] }, { "name": "HxcppApiLevel", - "define": "hxcpp_api_level", + "define": "hxcpp-api-level", "doc": "Provided to allow compatibility between hxcpp versions.", "platforms": ["cpp"], "reserved": true }, { "name": "HxcppGcGenerational", - "define": "HXCPP_GC_GENERATIONAL", + "define": "HXCPP-GC-GENERATIONAL", "doc": "Experimental Garbage Collector.", "platforms": ["cpp"] }, { "name": "HxcppDebugger", - "define": "HXCPP_DEBUGGER", - "doc": "Include additional information for hxcpp_debugger.", + "define": "HXCPP-DEBUGGER", + "doc": "Include additional information for hxcpp-debugger.", "platforms": ["cpp"] }, { "name": "HxcppSmartStings", - "define": "hxcpp_smart_strings", - "doc": "Use wide strings in hxcpp. (Turned on by default unless `-D disable_unicode_strings` is specified.)", + "define": "hxcpp-smart-strings", + "doc": "Use wide strings in hxcpp. (Turned on by default unless `-D disable-unicode-strings` is specified.)", "platforms": ["cpp"] }, { "name": "IncludePrefix", - "define": "include_prefix", + "define": "include-prefix", "doc": "Prepend path to generated include files.", "platforms": ["cpp"] }, @@ -293,20 +317,20 @@ }, { "name": "JavaVer", - "define": "java_ver", + "define": "java-ver", "doc": "Sets the Java version to be targeted.", "platforms": ["java"], "params": ["version: 5-7"] }, { "name": "JsClassic", - "define": "js_classic", + "define": "js-classic", "doc": "Don't use a function wrapper and strict mode in JS output.", "platforms": ["js"] }, { "name": "JsEs", - "define": "js_es", + "define": "js-es", "doc": "Generate JS compliant with given ES standard version. (default: 5)", "platforms": ["js"], "params": ["version: 3 | 5 | 6"], @@ -314,7 +338,7 @@ }, { "name": "JsEnumsAsArrays", - "define": "js_enums_as_arrays", + "define": "js-enums-as-arrays", "doc": "Generate enum representation as array instead of as object.", "platforms": ["js"] }, @@ -326,19 +350,19 @@ }, { "name": "JsUnflatten", - "define": "js_unflatten", + "define": "js-unflatten", "doc": "Generate nested objects for packages and types.", "platforms": ["js"] }, { "name": "JsSourceMap", - "define": "js_source_map", - "doc": "Generate JavaScript source map even in non-debug mode. Deprecated in favor of `-D source_map`.", + "define": "js-source-map", + "doc": "Generate JavaScript source map even in non-debug mode. Deprecated in favor of `-D source-map`.", "platforms": ["js"] }, { "name": "SourceMap", - "define": "source_map", + "define": "source-map", "doc": "Generate source map for compiled files.", "platforms": ["php", "js"] }, @@ -362,31 +386,31 @@ }, { "name": "KeepOldOutput", - "define": "keep_old_output", + "define": "keep-old-output", "doc": "Keep old source files in the output directory.", "platforms": ["cs", "java"] }, { "name": "LoopUnrollMaxCost", - "define": "loop_unroll_max_cost", + "define": "loop-unroll-max-cost", "doc": "Maximum cost (number of expressions * iterations) before loop unrolling is canceled. (default: 250)", "params": ["cost"] }, { "name": "LuaJit", - "define": "lua_jit", + "define": "lua-jit", "doc": "Enable the jit compiler for lua (version 5.2 only).", "platforms": ["lua"] }, { "name": "LuaVanilla", - "define": "lua_vanilla", + "define": "lua-vanilla", "doc": "Generate code lacking compiled extern lib support (e.g. utf8).", "platforms": ["lua"] }, { "name": "LuaVer", - "define": "lua_ver", + "define": "lua-ver", "doc": "The lua version to target.", "platforms": ["lua"], "params": ["version"] @@ -400,39 +424,47 @@ }, { "name": "MacroTimes", - "define": "macro_times", + "define": "macro-times", "doc": "Display per-macro timing when used with `--times`." }, { "name": "NetVer", - "define": "net_ver", + "define": "net-ver", "doc": "Sets the .NET version to be targeted.", "platforms": ["cs"], - "params": ["version: 20-45"] + "params": ["version: 20-50"] }, { "name": "NetcoreVer", - "define": "netcore_ver", + "define": "netcore-ver", "doc": "Sets the .NET core version to be targeted", "platforms": ["cs"], "params": ["version: x.x.x"] }, { "name": "NetTarget", - "define": "net_target", + "define": "net-target", "doc": "Sets the .NET target. `netcore` (.NET core), `xbox`, `micro` (Micro Framework), `compact` (Compact Framework) are some valid values. (default: `net`)", "platforms": ["cs"], "params": ["name"] }, { "name": "NekoSource", - "define": "neko_source", + "define": "neko-source", "doc": "Output neko source instead of bytecode.", "platforms": ["neko"] }, + { + "name": "NekoNoHaxelibPaths", + "define": "neko-no-haxelib-paths", + "doc": "Disable hard-coded Haxelib ndll paths.", + "platforms": [ + "neko" + ] + }, { "name": "NekoV1", - "define": "neko_v1", + "define": "neko-v1", "doc": "Keep Neko 1.x compatibility.", "platforms": ["neko"] }, @@ -450,7 +482,7 @@ }, { "name": "NoDebug", - "define": "no_debug", + "define": "no-debug", "doc": "Remove all debug macros from cpp output." }, { @@ -466,41 +498,41 @@ }, { "name": "NoOpt", - "define": "no_opt", + "define": "no-opt", "doc": "Disable optimizations." }, { "name": "NoInline", - "define": "no_inline", + "define": "no-inline", "doc": "Disable inlining.", "links": ["https://haxe.org/manual/class-field-inline.html"] }, { "name": "KeepInlinePositions", - "define": "keep_inline_positions", + "define": "keep-inline-positions", "doc": "Don't substitute positions of inlined expressions with the position of the place of inlining.", "links": ["https://haxe.org/manual/class-field-inline.html"] }, { "name": "NoRoot", - "define": "no_root", + "define": "no-root", "doc": "Generate top-level types into the `haxe.root` namespace.", "platforms": ["cs"] }, { "name": "NoMacroCache", - "define": "no_macro_cache", + "define": "no-macro-cache", "doc": "Disable macro context caching." }, { "name": "NoSwfCompress", - "define": "no_swf_compress", + "define": "no-swf-compress", "doc": "Disable SWF output compression.", "platforms": ["flash"] }, { "name": "NoTraces", - "define": "no_traces", + "define": "no-traces", "doc": "Disable all trace calls." }, { @@ -516,44 +548,49 @@ }, { "name": "PhpPrefix", - "define": "php_prefix", + "define": "php-prefix", "doc": "Root namespace for generated php classes. E.g. if compiled with`-D php-prefix=some.sub`, then all classes will be generated in `\\some\\sub` namespace.", "platforms": ["php"], "params": ["dot-separated namespace"] }, { "name": "PhpLib", - "define": "php_lib", + "define": "php-lib", "doc": "Select the name for the php lib folder.", "platforms": ["php"], "params": ["folder name"] }, { "name": "PhpFront", - "define": "php_front", + "define": "php-front", "doc": "Select the name for the php front file. (default: `index.php`)", "platforms": ["php"], "params": ["filename"] }, { "name": "PythonVersion", - "define": "python_version", + "define": "python-version", "doc": "The python version to target. (default: 3.3)", "platforms": ["python"], "params": ["version"] }, { "name": "RealPosition", - "define": "real_position", + "define": "real-position", "doc": "Disables Haxe source mapping when targetting C#, removes position comments in Java and Php output.", "platforms": ["cs", "java", "php"] }, { "name": "ReplaceFiles", - "define": "replace_files", + "define": "replace-files", "doc": "GenCommon internal.", "platforms": ["cs", "java"] }, + { + "name": "RetainUntypedMeta", + "define": "retain-untyped-meta", + "doc": "Prevents arbitrary expression metadata from being discarded upon typing." + }, { "name": "Scriptable", "define": "scriptable", @@ -598,59 +635,65 @@ }, { "name": "SwfCompressLevel", - "define": "swf_compress_level", + "define": "swf-compress-level", "doc": "Set the amount of compression for the SWF output.", "platforms": ["flash"], "params": ["level: 1-9"] }, { "name": "SwfDebugPassword", - "define": "swf_debug_password", + "define": "swf-debug-password", "doc": "Set a password for debugging.", "platforms": ["flash"], "params": ["password"] }, { "name": "SwfDirectBlit", - "define": "swf_direct_blit", + "define": "swf-direct-blit", "doc": "Use hardware acceleration to blit graphics.", "platforms": ["flash"] }, { "name": "SwfGpu", - "define": "swf_gpu", + "define": "swf-gpu", "doc": "Use GPU compositing features when drawing graphics.", "platforms": ["flash"] }, + { + "name": "SwfHeader", + "define": "swf-header", + "doc": "define SWF header (width:height:fps:color)", + "platforms": ["flash"] + }, { "name": "SwfMetadata", - "define": "swf_metadata", + "define": "swf-metadata", "doc": "Include contents of the given file as metadata in the SWF.", "platforms": ["flash"], "params": ["file"] }, { "name": "SwfPreloaderFrame", - "define": "swf_preloader_frame", + "define": "swf-preloader-frame", "doc": "Insert empty first frame in SWF.", "platforms": ["flash"] }, { "name": "SwfProtected", - "define": "swf_protected", + "define": "swf-protected", "doc": "Compile Haxe `private` as `protected` in the SWF instead of `public`.", "platforms": ["flash"] }, { "name": "SwfScriptTimeout", - "define": "swf_script_timeout", + "define": "swf-script-timeout", "doc": "Maximum ActionScript processing time before script stuck dialog box displays.", "platforms": ["flash"], "params": ["time in seconds"] }, { "name": "SwfUseDoAbc", - "define": "swf_use_doabc", + "define": "swf-use-doabc", "doc": "Use `DoAbc` SWF-tag instead of `DoAbcDefine`.", "platforms": ["flash"] }, @@ -668,7 +711,7 @@ }, { "name": "UseNekoc", - "define": "use_nekoc", + "define": "use-nekoc", "doc": "Use `nekoc` compiler instead of the internal one.", "platforms": ["neko"] }, @@ -686,12 +729,34 @@ }, { "name": "WarnVarShadowing", - "define": "warn_var_shadowing", + "define": "warn-var-shadowing", "doc": "Warn about shadowing variable declarations." }, { "name": "NoTre", - "define": "no_tre", + "define": "no-tre", "doc": "Disable tail recursion elimination." + }, + { + "name": "MessageReporting", + "define": "message-reporting", + "doc": "Select message reporting mode for compiler output. (default: classic)", + "params": ["mode: classic | pretty | indent"] + }, + { + "name": "NoColor", + "define": "no-color", + "doc": "Disable ANSI color codes when using rich output." + }, + { + "name": "MessagesLogFile", + "define": "messages-log-file", + "doc": "Path to a text file to write messages log to, in addition to regular output." + }, + { + "name": "MessagesLogFormat", + "define": "messages-log-format", + "doc": "Select message reporting mode for messages log output. (default: indent)", + "params": ["format: classic | pretty | indent"] } ] diff --git a/src-json/meta.json b/src-json/meta.json index c7715644..7aca530e 100644 --- a/src-json/meta.json +++ b/src-json/meta.json @@ -112,9 +112,9 @@ { "name": "Build", "metadata": ":build", - "doc": "Builds a class or enum from a macro.", + "doc": "Builds a class, enum, or abstract from a macro.", "params": ["Build macro call"], - "targets": ["TClass", "TEnum"], + "targets": ["TClass", "TEnum", "TAbstract"], "links": ["https://haxe.org/manual/macro-type-building.html"] }, { @@ -517,6 +517,12 @@ "platforms": ["hl"], "targets": ["TClass", "TClassField"] }, + { + "name": "HxCompletion", + "metadata": ":hx.completion", + "doc": "Internally used for completion", + "internal": true + }, { "name": "HxGen", "metadata": ":hxGen", @@ -612,6 +618,14 @@ "params": ["Output type package", "Output type name"], "targets": ["TClass", "TEnum"] }, + { + "name": "JavaDefault", + "metadata": ":java.default", + "doc": "Equivalent to the default modifier of the Java language", + "platforms": ["java"], + "params": [], + "targets": ["TClassField"] + }, { "name": "JavaNative", "metadata": ":javaNative", @@ -1272,6 +1286,12 @@ "targets": ["TClassField"], "internal": true }, + { + "name": "HaxeWarning", + "metadata": ":haxe.warning", + "doc": "Modifies warning options, equivalent to the -w CLI argument", + "targets": ["TClass","TClassField"] + }, { "name": "Void", "metadata": ":void", @@ -1283,5 +1303,17 @@ "metadata": ":needsExceptionStack", "doc": "Internally used for some of auto-generated `catch` vars", "internal": true + }, + { + "name": "WrappedException", + "metadata": ":wrappedException", + "doc": "Internally used for exceptions wrapping in `throw` expressions.", + "internal": true + }, + { + "name": "NativeArrayAccess", + "metadata": ":nativeArrayAccess", + "doc": "When used on an extern class which implements haxe.ArrayAccess native array access syntax will be generated", + "platforms": ["cpp"] } ] diff --git a/src-json/warning.json b/src-json/warning.json new file mode 100644 index 00000000..42c0cf70 --- /dev/null +++ b/src-json/warning.json @@ -0,0 +1,107 @@ +[ + { + "name": "WAll", + "doc": "The mother of all warnings", + "generic": true + }, + { + "name": "WInternal", + "doc": "Reserved for internal use", + "generic": true + }, + { + "name": "WTemp", + "doc": "Reserved for internal use", + "generic": true + }, + { + "name": "WInfo", + "doc": "Generic information like $type", + "generic": true + }, + { + "name": "WUser", + "doc": "Custom user warnings, e.g. from Context.warning", + "generic": true + }, + { + "name": "WCompiler", + "doc": "Warnings from the compiler frontend, e.g. argument parsing", + "generic": true + }, + { + "name": "WParser", + "doc": "Warnings related to lexing and parsing", + "generic": true + }, + { + "name": "WTyper", + "doc": "Warnings from the typer", + "generic": true + }, + { + "name": "WMacro", + "doc": "Warning related to macros", + "generic": true + }, + { + "name": "WOptimizer", + "doc": "Warnings related to optimization", + "generic": true + }, + { + "name": "WGenerator", + "doc": "Warnings related to code generation", + "generic": true + }, + { + "name": "WDeprecated", + "doc": "This is deprecated and should no longer be used" + }, + { + "name": "WVarInit", + "doc": "A local variable might be used before being assigned a value", + "parent": "WTyper" + }, + { + "name": "WVarShadow", + "doc": "A local variable hides another by using the same name", + "parent": "WTyper" + }, + { + "name": "WExternWithExpr", + "doc": "A non-inline field marked as extern has an expression", + "parent": "WTyper" + }, + { + "name": "WStaticInitOrder", + "doc": "The compiler could not determine a type order due to mutually dependent static initializations", + "parent": "WTyper" + }, + { + "name": "WClosureCompare", + "doc": "Closures are being compared, which might be undefined", + "parent": "WTyper" + }, + { + "name": "WReservedTypePath", + "doc": "A type path is being used that is supposed to be reserved on the current target", + "parent": "WTyper" + }, + { + "name": "WPatternMatcher", + "doc": "Warnings related to the pattern matcher", + "parent": "WTyper" + }, + { + "name": "WUnusedPattern", + "doc": "The pattern is not used because previous cases cover", + "parent": "WPatternMatcher" + }, + { + "name": "WConstructorInliningCancelled", + "doc": "Constructor call could not be inlined because a field is uninitialized", + "parent": "WTyper" + } + +] \ No newline at end of file diff --git a/src-prebuild/prebuild.ml b/src-prebuild/prebuild.ml index 842f6644..8ae72deb 100644 --- a/src-prebuild/prebuild.ml +++ b/src-prebuild/prebuild.ml @@ -2,10 +2,21 @@ open Json exception Prebuild_error of string +type parsed_warning = { + w_name : string; + w_doc : string; + w_parent : string option; + w_generic : bool; +} + let as_string = function | JString s -> Some s | _ -> None +let as_int = function + | JInt i -> Some i + | _ -> None + let as_params = function | JArray s -> Some (List.map (function | JString s -> s @@ -66,6 +77,16 @@ let get_optional_field name map default fields = | Some v -> v with Not_found -> default +let get_optional_field2 name map fields = + try + let field = List.find (fun (n, _) -> n = name) fields in + let value = map (snd field) in + match value with + | None -> raise (Prebuild_error ("field `" ^ name ^ "` has invalid data")) + | Some v -> Some v + with Not_found -> + None + let get_field name map fields = let field = try List.find (fun (n, _) -> n = name) fields with Not_found -> raise (Prebuild_error ("no `" ^ name ^ "` field")) in let value = map (snd field) in @@ -99,6 +120,18 @@ let parse_meta json = (* internal *) get_optional_field "internal" as_bool false fields, (* links *) get_optional_field "links" as_links [] fields +let parse_warning json = + let fields = match json with + | JObject fl -> fl + | _ -> raise (Prebuild_error "not an object") + in + { + w_name = get_field "name" as_string fields; + w_doc = get_field "doc" as_string fields; + w_parent = get_optional_field2 "parent" as_string fields; + w_generic = get_optional_field "generic" as_bool false fields; + } + let parse_file_array path map = let file = open_in path in let data = Std.input_all file in @@ -109,6 +142,20 @@ let parse_file_array path map = | JArray s -> List.map map s | _ -> raise (Prebuild_error "not an array") +let s_escape ?(hex=true) s = + let b = Buffer.create (String.length s) in + for i = 0 to (String.length s) - 1 do + match s.[i] with + | '\n' -> Buffer.add_string b "\\n" + | '\t' -> Buffer.add_string b "\\t" + | '\r' -> Buffer.add_string b "\\r" + | '"' -> Buffer.add_string b "\\\"" + | '\\' -> Buffer.add_string b "\\\\" + | c when int_of_char c < 32 && hex -> Buffer.add_string b (Printf.sprintf "\\x%.2X" (int_of_char c)) + | c -> Buffer.add_char b c + done; + Buffer.contents b + let gen_platforms = function | [] -> [] | platforms -> ["Platforms [" ^ (String.concat ";" platforms) ^ "]"] @@ -126,6 +173,7 @@ let gen_define_info defines = let platforms_str = gen_platforms platforms in let params_str = gen_params params in let links_str = gen_links links in + let define = String.concat "_" (ExtString.String.nsplit define "-") in "\t| " ^ name ^ " -> \"" ^ define ^ "\",(" ^ (Printf.sprintf "%S" doc) ^ ",[" ^ (String.concat "; " (platforms_str @ params_str @ links_str)) ^ "])" ) defines in String.concat "\n" define_str @@ -156,6 +204,32 @@ let gen_meta_info metas = ) metas in String.concat "\n" meta_str +let gen_warning_type warnings = + let warning_str = List.map (function + w -> + Printf.sprintf "\t| %s" w.w_name + ) warnings in + String.concat "\n" warning_str + +let gen_warning_parse warnings = + let warning_str = List.map (function + w -> + Printf.sprintf "\t| \"%s\" -> %s" w.w_name w.w_name + ) warnings in + let warning_str = warning_str @ ["\t| _ -> raise Exit"] in + String.concat "\n" warning_str + + +let gen_warning_obj warnings = + let warning_str = List.map (fun w -> + let w_parent = match w.w_parent with + | None -> if w.w_name = "WAll" then "None" else "Some WAll" + | Some w -> Printf.sprintf "Some %s" w + in + Printf.sprintf "\t| %s -> {w_name = \"%s\"; w_doc = \"%s\"; w_generic = %b; w_parent = %s}" w.w_name w.w_name (s_escape w.w_doc) w.w_generic w_parent + ) warnings in + String.concat "\n" warning_str + let autogen_header = "(* This file is auto-generated using prebuild from files in src-json *) (* Do not edit manually! *) " @@ -185,6 +259,19 @@ type meta_usage = | TTypeParameter | TVariable +let parse_meta_usage = function + | \"TClass\" -> TClass + | \"TClassField\" -> TClassField + | \"TAbstract\" -> TAbstract + | \"TAbstractField\" -> TAbstractField + | \"TEnum\" -> TEnum + | \"TTypedef\" -> TTypedef + | \"TAnyField\" -> TAnyField + | \"TExpr\" -> TExpr + | \"TTypeParameter\" -> TTypeParameter + | \"TVariable\" -> TVariable + | t -> raise (failwith (\"invalid metadata target \" ^ t)) + type meta_parameter = | HasParam of string | Platforms of platform list @@ -202,10 +289,10 @@ match Array.to_list (Sys.argv) with Printf.printf "%s" define_header; Printf.printf "type strict_defined =\n"; Printf.printf "%s" (gen_define_type defines); - Printf.printf "\n\t| Last\n\n"; (* must be last *) + Printf.printf "\n\t| Last\n\t| Custom of string\n\n"; Printf.printf "let infos = function\n"; Printf.printf "%s" (gen_define_info defines); - Printf.printf "\n\t| Last -> die \"\" __LOC__\n" + Printf.printf "\n\t| Last -> die \"\" __LOC__\n\t| Custom s -> s,(\"\",[])\n" | [_; "meta"; meta_path]-> let metas = parse_file_array meta_path parse_meta in Printf.printf "%s" meta_header; @@ -215,6 +302,23 @@ match Array.to_list (Sys.argv) with Printf.printf "let get_info = function\n"; Printf.printf "%s" (gen_meta_info metas); Printf.printf "\n\t| Last -> die \"\" __LOC__\n\t| Dollar s -> \"$\" ^ s,(\"\",[])\n\t| Custom s -> s,(\"\",[])\n" + | [_; "warning"; warning_path]-> + let warnings = parse_file_array warning_path parse_warning in + print_endline "type warning ="; + print_endline (gen_warning_type warnings); + print_endline ""; + print_endline "type warning_obj = {"; + print_endline "\tw_name : string;"; + print_endline "\tw_doc : string;"; + print_endline "\tw_generic : bool;"; + print_endline "\tw_parent : warning option;"; + print_endline "}"; + print_endline ""; + print_endline "let warning_obj = function"; + print_endline (gen_warning_obj warnings); + print_endline ""; + print_endline "let from_string = function"; + print_endline (gen_warning_parse warnings); | _ :: "libparams" :: params -> Printf.printf "(%s)" (String.concat " " (List.map (fun s -> Printf.sprintf "\"%s\"" s) params)) | [_ ;"version";add_revision;branch;sha] -> diff --git a/src/codegen/codegen.ml b/src/codegen/codegen.ml index 303b6251..f9c55f7f 100644 --- a/src/codegen/codegen.ml +++ b/src/codegen/codegen.ml @@ -75,51 +75,6 @@ let escape_res_name name allow_dirs = else "-x" ^ (string_of_int (Char.code chr))) name -let update_cache_dependencies t = - let visited_anons = ref [] in - let rec check_t m t = match t with - | TInst(c,tl) -> - add_dependency m c.cl_module; - List.iter (check_t m) tl; - | TEnum(en,tl) -> - add_dependency m en.e_module; - List.iter (check_t m) tl; - | TType(t,tl) -> - add_dependency m t.t_module; - List.iter (check_t m) tl; - | TAbstract(a,tl) -> - add_dependency m a.a_module; - List.iter (check_t m) tl; - | TFun(targs,tret) -> - List.iter (fun (_,_,t) -> check_t m t) targs; - check_t m tret; - | TAnon an -> - if not (List.memq an !visited_anons) then begin - visited_anons := an :: !visited_anons; - PMap.iter (fun _ cf -> check_field m cf) an.a_fields - end - | TMono r -> - (match r.tm_type with - | Some t -> check_t m t - | _ -> ()) - | TLazy f -> - check_t m (lazy_type f) - | TDynamic t -> - if t == t_dynamic then - () - else - check_t m t - and check_field m cf = - check_t m cf.cf_type - in - match t with - | TClassDecl c -> - List.iter (check_field c.cl_module) c.cl_ordered_statics; - List.iter (check_field c.cl_module) c.cl_ordered_fields; - (match c.cl_constructor with None -> () | Some cf -> check_field c.cl_module cf); - | _ -> - () - (* -------------------------------------------------------------------------- *) (* FIX OVERRIDES *) @@ -169,7 +124,7 @@ let fix_override com c f fd = have to detect this case and change the variable (issue #2712). *) begin match follow v.v_type with | TInst({cl_kind = KTypeParameter [tc]} as cp,_) when com.platform = Flash -> - if List.mem_assoc (snd cp.cl_path) c.cl_params then raise (Unify_error []) + if List.exists (fun tp -> tp.ttp_name = (snd cp.cl_path)) c.cl_params then raise (Unify_error []) | _ -> () end; @@ -257,7 +212,7 @@ let rec is_volatile t = is_volatile (lazy_type f) | TType (t,tl) -> (match t.t_path with - | _ -> is_volatile (apply_params t.t_params tl t.t_type)) + | _ -> is_volatile (apply_typedef t tl)) | _ -> false diff --git a/src/codegen/dotnet.ml b/src/codegen/dotnet.ml index bdf141db..e90cfe95 100644 --- a/src/codegen/dotnet.ml +++ b/src/codegen/dotnet.ml @@ -265,9 +265,9 @@ let convert_ilenum ctx p ?(is_flag=false) ilcls = | Some IChar i | Some IByte i | Some IShort i -> - [Meta.CsNative, [EConst (Int (string_of_int i) ), p], p ], Int64.of_int i + [Meta.CsNative, [EConst (Int (string_of_int i, None) ), p], p ], Int64.of_int i | Some IInt i -> - [Meta.CsNative, [EConst (Int (Int32.to_string i) ), p], p ], Int64.of_int32 i + [Meta.CsNative, [EConst (Int (Int32.to_string i, None) ), p], p ], Int64.of_int32 i | Some IFloat32 f | Some IFloat64 f -> [], Int64.of_float f | Some IInt64 i -> @@ -323,7 +323,7 @@ let convert_ilfield ctx p field = | CInitOnly | CLiteral -> true, acc | _ -> readonly,acc ) (false,[cff_access]) field.fflags.ff_contract in - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then + if Common.raw_defined ctx.ncom "net_loader_debug" then Printf.printf "\t%sfield %s : %s\n" (if List.mem_assoc AStatic acc then "static " else "") cff_name (IlMetaDebug.ilsig_s field.fsig.ssig); let kind = match readonly with | true -> @@ -363,7 +363,7 @@ let convert_ilevent ctx p ev = else acc in - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then + if Common.raw_defined ctx.ncom "net_loader_debug" then Printf.printf "\tevent %s : %s\n" name (IlMetaDebug.ilsig_s ev.esig.ssig); let acc = add_m acc ev.eadd in let acc = add_m acc ev.eremove in @@ -400,7 +400,7 @@ let convert_ilmethod ctx p is_interface m is_explicit_impl = (APrivate,null_pos), ((Meta.Protected, [], p) :: meta) | FAPublic -> (APublic,null_pos), meta | _ -> - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then + if Common.raw_defined ctx.ncom "net_loader_debug" then Printf.printf "\tmethod %s (skipped) : %s\n" cff_name (IlMetaDebug.ilsig_s m.msig.ssig); raise Exit in @@ -412,7 +412,7 @@ let convert_ilmethod ctx p is_interface m is_explicit_impl = | _ -> acc, is_final ) ([acc],None) m.mflags.mf_contract in let acc = (AOverload,p) :: acc in - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then + if Common.raw_defined ctx.ncom "net_loader_debug" then Printf.printf "\t%smethod %s : %s\n" (if !is_static then "static " else "") cff_name (IlMetaDebug.ilsig_s m.msig.ssig); let acc = match is_final with @@ -473,6 +473,7 @@ let convert_ilmethod ctx p is_interface m is_explicit_impl = tp_name = "M" ^ string_of_int t.tnumber,null_pos; tp_params = []; tp_constraints = None; + tp_default = None; tp_meta = []; } ) m.mtypes in @@ -551,7 +552,7 @@ let convert_ilprop ctx p prop is_explicit_impl = | _ -> "never"); | Some _ -> "set" in - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then + if Common.raw_defined ctx.ncom "net_loader_debug" then Printf.printf "\tproperty %s (%s,%s) : %s\n" prop.pname get set (IlMetaDebug.ilsig_s prop.psig.ssig); let ilsig = match prop.psig.snorm with | LMethod (_,ret,[]) -> ret @@ -594,7 +595,7 @@ let mk_special_call name p args = mke (ECast( mke (EUntyped( mke (ECall( mke (EConst(Ident name)) p, args )) p )) p , None)) p let mk_this_call name p args = - mke (ECall( mke (EField(mke (EConst(Ident "this")) p ,name)) p, args )) p + mke (ECall( mke (efield(mke (EConst(Ident "this")) p ,name)) p, args )) p let mk_metas metas p = List.map (fun m -> m,[],p) metas @@ -643,6 +644,7 @@ let convert_delegate ctx p ilcls = tp_name = ("T" ^ string_of_int t.tnumber),null_pos; tp_params = []; tp_constraints = None; + tp_default = None; tp_meta = []; } ) ilcls.ctypes in @@ -651,7 +653,7 @@ let convert_delegate ctx p ilcls = let clsname = match ilcls.cpath with | (ns,inner,n) -> get_clsname ctx (ns,inner,"Delegate_"^n) in - let expr = (ECall( (EField( (EConst(Ident (clsname)),p), fn_name ),p), [(EConst(Ident"arg1"),p);(EConst(Ident"arg2"),p)]),p) in + let expr = (ECall( (efield( (EConst(Ident (clsname)),p), fn_name ),p), [(EConst(Ident"arg1"),p);(EConst(Ident"arg2"),p)]),p) in FFun { f_params = types; f_args = [("arg1",null_pos),false,[],Some (abs_type,null_pos),None;("arg2",null_pos),false,[],Some (abs_type,null_pos),None]; @@ -717,7 +719,7 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with | _ -> let flags = ref [HExtern] in (* todo: instead of CsNative, use more specific definitions *) - if PMap.mem "net_loader_debug" ctx.ncom.defines.Define.values then begin + if Common.raw_defined ctx.ncom "net_loader_debug" then begin let sup = match ilcls.csuper with | None -> [] | Some c -> [IlMetaDebug.ilsig_s c.ssig] in let sup = sup @ List.map (fun i -> IlMetaDebug.ilsig_s i.ssig) ilcls.cimplements in print_endline ("converting " ^ ilpath_s ilcls.cpath ^ " : " ^ (String.concat ", " sup)) @@ -816,6 +818,7 @@ let convert_ilclass ctx p ?(delegate=false) ilcls = match ilcls.csuper with tp_name = "T" ^ string_of_int p.tnumber,null_pos; tp_params = []; tp_constraints = None; + tp_default = None; tp_meta = []; }) ilcls.ctypes in @@ -1141,14 +1144,14 @@ class net_library com name file_path std = object(self) let cache = IlMetaReader.create_cache () in let meta = IlMetaReader.read_meta_tables ctx clr_header cache in close_in (r.PeReader.ch); - if PMap.mem "net_loader_debug" com.defines.Define.values then + if Common.raw_defined com "net_loader_debug" then print_endline ("for lib " ^ file_path); let il_typedefs = Hashtbl.copy meta.il_typedefs in Hashtbl.clear meta.il_typedefs; Hashtbl.iter (fun _ td -> let path = IlMetaTools.get_path (TypeDef td) in - if PMap.mem "net_loader_debug" com.defines.Define.values then + if Common.raw_defined com "net_loader_debug" then Printf.printf "found %s\n" (s_type_path (self#netpath_to_hx path)); Hashtbl.replace com.net_path_map (self#netpath_to_hx path) path; Hashtbl.replace meta.il_typedefs path td @@ -1190,7 +1193,7 @@ class net_library com name file_path std = object(self) let pack = match fst path with | ["haxe";"root"] -> [] | p -> p in let cp = ref [] in let rec build path = try - if PMap.mem "net_loader_debug" com.defines.Define.values then + if Common.raw_defined com "net_loader_debug" then Printf.printf "looking up %s\n" (s_type_path path); match self#lookup path with | Some({csuper = Some{snorm = LClass( (["System"],[],("Delegate"|"MulticastDelegate")),_)}} as cls) @@ -1241,12 +1244,12 @@ let add_net_lib com file std extern = let before_generate com = (* netcore version *) - let netcore_ver = try Some(PMap.find "netcore_ver" com.defines.Define.values) with Not_found -> None in + let netcore_ver = try Some(Common.defined_value com Define.NetcoreVer) with Not_found -> None in (* net version *) let net_ver = try - let ver = PMap.find "net_ver" com.defines.Define.values in + let ver = Common.defined_value com Define.NetVer in try int_of_string ver with Failure _ -> raise (Arg.Bad "Invalid value for -D net-ver. Expected format: xx (e.g. 20, 35, 40, 45, 50)") with Not_found when netcore_ver != None -> (* 4.7 was released around .NET core 2.1 *) @@ -1274,7 +1277,7 @@ let before_generate com = (* net target *) let net_target = try - String.lowercase (PMap.find "net_target" com.defines.Define.values) + String.lowercase (Common.defined_value com Define.NetTarget) with | Not_found -> "net" in diff --git a/src/codegen/gencommon/castDetect.ml b/src/codegen/gencommon/castDetect.ml index 4028bded..fe6547c4 100644 --- a/src/codegen/gencommon/castDetect.ml +++ b/src/codegen/gencommon/castDetect.ml @@ -185,7 +185,9 @@ let rec type_eq gen param a b = ) l1 l2 with Unify_error l -> Type.error (cannot_unify a b :: l)) - | TDynamic a , TDynamic b -> + | TDynamic None , TDynamic None -> + () + | TDynamic (Some a) , TDynamic (Some b) -> type_eq gen param a b | TAnon a1, TAnon a2 -> (try @@ -625,22 +627,24 @@ let choose_ctor gen cl tparams etl maybe_empty_t p = match arglist, elist with | [], [] -> true | [(_,_,t)], elist when ExtType.is_rest (follow t) -> - let is_rest_array arg_t = - Type.fast_eq (Abstract.follow_with_abstracts t) (Abstract.follow_with_abstracts arg_t) - in - (match elist with - | [arg_t] when is_rest_array arg_t -> true - | _ -> - match follow t with - | TAbstract ({ a_path = ["haxe"],"Rest" }, [t1]) -> - let t1 = run_follow gen t1 in - (try - List.iter (fun et -> unify et t1) elist; - true - with Unify_error _ -> - false - ) - | _ -> die "" __LOC__ + (match follow t with + | TAbstract ({ a_path = ["haxe"],"Rest" } as a, [t1]) -> + let is_rest_array arg_t = + let boxed = TAbstract (a, [get_boxed gen t1]) in + Type.fast_eq (Abstract.follow_with_abstracts boxed) (Abstract.follow_with_abstracts arg_t) + in + (match elist with + | [arg_t] when is_rest_array arg_t -> true + | _ -> + let t1 = run_follow gen t1 in + (try + List.iter (fun et -> unify et t1) elist; + true + with Unify_error _ -> + false + ) + ) + | _ -> die "" __LOC__ ) | (_,_,t) :: arglist, et :: elist -> (try @@ -648,7 +652,6 @@ let choose_ctor gen cl tparams etl maybe_empty_t p = unify et t; check_arg arglist elist with Unify_error el -> - (* List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) p) el; *) false ) | _ -> @@ -739,7 +742,7 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist (* this function will receive the original function argument, the applied function argument and the original function parameters. *) (* from this info, it will infer the applied tparams for the function *) - let infer_params pos (original_args:((string * bool * t) list * t)) (applied_args:((string * bool * t) list * t)) (params:(string * t) list) calls_parameters_explicitly : tparams = + let infer_params pos (original_args:((string * bool * t) list * t)) (applied_args:((string * bool * t) list * t)) (params:typed_type_param list) calls_parameters_explicitly : tparams = match params with | [] -> [] | _ -> @@ -766,12 +769,12 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist | [Cannot_unify (b, TAbstract(a,params))] -> let a = apply_params a.a_params params a.a_this in if not (shallow_eq a b) then - gen.gcon.warning ("This expression may be invalid") pos + gen.gwarning WGenerator ("This expression may be invalid") pos | _ -> - gen.gcon.warning ("This expression may be invalid") pos + gen.gwarning WGenerator ("This expression may be invalid") pos ) | Invalid_argument _ -> - gen.gcon.warning ("This expression may be invalid") pos + gen.gwarning WGenerator ("This expression may be invalid") pos ); List.map (fun t -> @@ -823,7 +826,7 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist (* f,f.cf_type, false *) select_overload gen e1.etype ((f.cf_type,f) :: List.map (fun f -> f.cf_type,f) f.cf_overloads) [] [], true | _ -> - gen.gcon.warning "Overloaded classfield typed as anonymous" ecall.epos; + gen.gwarning WGenerator "Overloaded classfield typed as anonymous" ecall.epos; (cf, actual_t, true), true in @@ -846,7 +849,7 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist end; { cf_orig with cf_name = cf.cf_name },actual_t,false | None -> - gen.gcon.warning "Cannot find matching overload" ecall.epos; + gen.gwarning WGenerator "Cannot find matching overload" ecall.epos; cf, actual_t, true else cf,actual_t,error @@ -928,7 +931,7 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist elist); }, elist with Invalid_argument _ -> - gen.gcon.warning ("This expression may be invalid" ) ecall.epos; + gen.gwarning WGenerator ("This expression may be invalid" ) ecall.epos; { ecall with eexpr = TCall({ e1 with eexpr = TField(!ef, f) }, elist) }, elist in let new_ecall = if fparams <> [] then gen.gparam_func_call new_ecall { e1 with eexpr = TField(!ef, f) } fparams elist else new_ecall in @@ -958,7 +961,7 @@ let handle_type_parameter gen e e1 ef ~clean_ef ~overloads_cast_to_base f elist *) | _ -> let pt = match e with | None -> real_type | Some _ -> snd (get_fun e1.etype) in - let _params = match follow pt with | TEnum(_, p) -> p | _ -> gen.gcon.warning (debug_expr e1) e1.epos; die "" __LOC__ in + let _params = match follow pt with | TEnum(_, p) -> p | _ -> gen.gwarning WGenerator (debug_expr e1) e1.epos; die "" __LOC__ in let args, ret = get_fun efield.ef_type in let actual_t = TFun(List.map (fun (n,o,t) -> (n,o,gen.greal_type t)) args, gen.greal_type ret) in (* @@ -1142,7 +1145,7 @@ let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_paramete let base_type = match follow et with | TInst({ cl_path = ([], "Array") } as cl, bt) -> gen.greal_type_param (TClassDecl cl) bt | _ -> - gen.gcon.warning (debug_type et) e.epos; + gen.gwarning WGenerator (debug_type et) e.epos; (match gen.gcurrent_class with | Some cl -> print_endline (s_type_path cl.cl_path) | _ -> ()); @@ -1186,7 +1189,7 @@ let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_paramete ) (wrap_rest_args gen (TFun (args,rt)) eparams e.epos) args in { e with eexpr = TCall(ef, eparams) } with | Not_found -> - gen.gcon.warning "No overload found for this constructor call" e.epos; + gen.gwarning WGenerator "No overload found for this constructor call" e.epos; { e with eexpr = TCall(ef, List.map run eparams) }) | TCall (ef, eparams) -> (match ef.etype with @@ -1214,7 +1217,7 @@ let configure gen ?(overloads_cast_to_base = false) maybe_empty_t calls_paramete ) (wrap_rest_args gen (TFun (args,rt)) eparams e.epos) args in { e with eexpr = TNew(cl, tparams, eparams) } with | Not_found -> - gen.gcon.warning "No overload found for this constructor call" e.epos; + gen.gwarning WGenerator "No overload found for this constructor call" e.epos; { e with eexpr = TNew(cl, tparams, List.map run eparams) }) | TUnop((Increment | Decrement) as op, flag, ({ eexpr = TArray (arr, idx) } as e2)) when (match follow arr.etype with TInst({ cl_path = ["cs"],"NativeArray" },_) -> true | _ -> false) -> diff --git a/src/codegen/gencommon/closuresToClass.ml b/src/codegen/gencommon/closuresToClass.ml index 98b3fc9a..2d7904de 100644 --- a/src/codegen/gencommon/closuresToClass.ml +++ b/src/codegen/gencommon/closuresToClass.ml @@ -283,13 +283,15 @@ let rec get_type_params acc t = List.filter (fun t -> not (List.memq t acc)) (cl :: params) @ acc; | TFun (params,tret) -> List.fold_left get_type_params acc ( tret :: List.map (fun (_,_,t) -> t) params ) - | TDynamic t -> - (match t with | TDynamic _ -> acc | _ -> get_type_params acc t) + | TDynamic None -> + acc + | TDynamic (Some t) -> + get_type_params acc t | TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) -> get_type_params acc ( Abstract.get_underlying_type a pl) | TAnon a -> PMap.fold (fun cf acc -> - let params = List.map (fun (_,t) -> match follow t with + let params = List.map (fun tp -> match follow tp.ttp_type with | TInst(c,_) -> c | _ -> die "" __LOC__) cf.cf_params in @@ -396,7 +398,7 @@ let configure gen ft = in (*let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(map_param cl, []) )) tparams in*) - let cltypes = List.map (fun cl -> (snd cl.cl_path, TInst(cl, []) )) tparams in + let cltypes = List.map (fun cl -> mk_type_param (snd cl.cl_path) (TInst(cl, [])) None) tparams in (* create a new class that extends abstract function class, with a ctor implementation that will setup all captured variables *) let cfield = match gen.gcurrent_classfield with @@ -426,7 +428,7 @@ let configure gen ft = let mk_this v pos = { - (mk_field_access gen { eexpr = TConst TThis; etype = TInst(cls, List.map snd cls.cl_params); epos = pos } v.v_name pos) + (mk_field_access gen { eexpr = TConst TThis; etype = TInst(cls, extract_param_types cls.cl_params); epos = pos } v.v_name pos) with etype = v.v_type } in @@ -476,9 +478,9 @@ let configure gen ft = eexpr = TCall({ eexpr = TField({ eexpr = TConst TThis; - etype = TInst(cls, List.map snd cls.cl_params); + etype = TInst(cls, extract_param_types cls.cl_params); epos = pos; - }, FInstance(cls, List.map snd cls.cl_params, cf)); + }, FInstance(cls, extract_param_types cls.cl_params, cf)); etype = cf.cf_type; epos = pos; }, List.map (fun (v,_) -> mk_local v pos) tfunc.tf_args); @@ -617,17 +619,17 @@ let configure gen ft = | TInst(c,_), TInst(c2,_) -> c == c2 | _ -> false in - let passoc = List.map2 (fun (_,t) m -> t,m) types monos in - let cltparams = List.map (fun (_,t) -> + let passoc = List.map2 (fun tp m -> tp.ttp_type,m) types monos in + let cltparams = List.map (fun tp -> try - snd (List.find (fun (t2,_) -> same_cl t t2) passoc) - with | Not_found -> t) cls.cl_params + snd (List.find (fun (t2,_) -> same_cl tp.ttp_type t2) passoc) + with | Not_found -> tp.ttp_type) cls.cl_params in { e with eexpr = TNew(cls, cltparams, List.rev captured) } with | Not_found -> if in_tparam then begin - gen.gcon.warning "This expression may be invalid" e.epos; + gen.gwarning WGenerator "This expression may be invalid" e.epos; e end else (* It is possible that we are recursively calling a function @@ -642,8 +644,8 @@ let configure gen ft = (Meta.Custom(":tparamcall"), [], e.epos), e ) } | Unify_error el -> - List.iter (fun el -> gen.gcon.warning (Error.unify_error_msg (print_context()) el) e.epos) el; - gen.gcon.warning "This expression may be invalid" e.epos; + List.iter (fun el -> gen.gwarning WGenerator (Error.unify_error_msg (print_context()) el) e.epos) el; + gen.gwarning WGenerator "This expression may be invalid" e.epos; e ) (* (handle_anon_func:texpr->tfunc->texpr) (dynamic_func_call:texpr->texpr->texpr list->texpr) *) @@ -899,7 +901,7 @@ struct let map_base_classfields cl map_fn = let pos = cl.cl_pos in - let this_t = TInst(cl,List.map snd cl.cl_params) in + let this_t = TInst(cl,extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in @@ -1091,7 +1093,7 @@ struct loop arity [] in - let this = mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) pos in + let this = mk (TConst TThis) (TInst (cl, extract_param_types cl.cl_params)) pos in let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in let mk_invoke_switch i api = diff --git a/src/codegen/gencommon/dynamicOperators.ml b/src/codegen/gencommon/dynamicOperators.ml index e114db1e..57e356ca 100644 --- a/src/codegen/gencommon/dynamicOperators.ml +++ b/src/codegen/gencommon/dynamicOperators.ml @@ -119,7 +119,7 @@ let init com handle_strings (should_change:texpr->bool) (equals_handler:texpr->t { e with eexpr = TBinop (op, mk_cast com.basic.tbool (run e1), mk_cast com.basic.tbool (run e2)) } | OpAnd | OpOr | OpXor | OpShl | OpShr | OpUShr -> { e with eexpr = TBinop (op, mk_cast com.basic.tint (run e1), mk_cast com.basic.tint (run e2)) } - | OpAssign | OpAssignOp _ | OpInterval | OpArrow | OpIn -> + | OpAssign | OpAssignOp _ | OpInterval | OpArrow | OpIn | OpNullCoal -> Globals.die "" __LOC__) | TUnop (Increment as op, flag, e1) diff --git a/src/codegen/gencommon/enumToClass.ml b/src/codegen/gencommon/enumToClass.ml index 5814a920..b1d8c6fe 100644 --- a/src/codegen/gencommon/enumToClass.ml +++ b/src/codegen/gencommon/enumToClass.ml @@ -99,11 +99,11 @@ struct (match gen.gcon.platform with | Cs when Common.defined gen.gcon Define.CoreApiSerialize -> - cl.cl_meta <- ( Meta.Meta, [ (EField( (EConst (Ident "System"), null_pos ), "Serializable" ), null_pos) ], null_pos ) :: cl.cl_meta + cl.cl_meta <- ( Meta.Meta, [ (efield( (EConst (Ident "System"), null_pos ), "Serializable" ), null_pos) ], null_pos ) :: cl.cl_meta | _ -> ()); let c_types = if handle_type_params then - List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params + List.map (fun tp -> {tp with ttp_type=TInst (map_param (get_cl_t tp.ttp_type), [])}) en.e_params else [] in @@ -121,13 +121,13 @@ struct | TFun(params,ret) -> let dup_types = if handle_type_params then - List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) en.e_params + List.map (fun tp -> {tp with ttp_type = TInst (map_param (get_cl_t tp.ttp_type), [])}) en.e_params else [] in let ef_type = - let fn, types = if handle_type_params then snd, dup_types else (fun _ -> t_dynamic), en.e_params in + let fn, types = if handle_type_params then extract_param_type, dup_types else (fun _ -> t_dynamic), en.e_params in let t = apply_params en.e_params (List.map fn types) ef.ef_type in apply_params ef.ef_params (List.map fn ef.ef_params) t in @@ -144,7 +144,7 @@ struct eexpr = TFunction({ tf_args = tf_args; tf_type = ret; - tf_expr = mk_block ( mk_return { eexpr = TNew(cl,List.map snd dup_types, [make_int gen.gcon.basic old_i pos; arr_decl] ); etype = TInst(cl, List.map snd dup_types); epos = pos } ); + tf_expr = mk_block ( mk_return { eexpr = TNew(cl,extract_param_types dup_types, [make_int gen.gcon.basic old_i pos; arr_decl] ); etype = TInst(cl, extract_param_types dup_types); epos = pos } ); }); etype = ef_type; epos = pos diff --git a/src/codegen/gencommon/fixOverrides.ml b/src/codegen/gencommon/fixOverrides.ml index f79195a5..43995535 100644 --- a/src/codegen/gencommon/fixOverrides.ml +++ b/src/codegen/gencommon/fixOverrides.ml @@ -61,7 +61,7 @@ let run ~explicit_fn_name ~get_vmtype gen = ) c.cl_ordered_fields; md | TClassDecl c when not (has_class_flag c CExtern) -> - let this = { eexpr = TConst TThis; etype = TInst(c,List.map snd c.cl_params); epos = c.cl_pos } in + let this = { eexpr = TConst TThis; etype = TInst(c,extract_param_types c.cl_params); epos = c.cl_pos } in (* look through all interfaces, and try to find a type that applies exactly *) let rec loop_iface (iface:tclass) itl = List.iter (fun (s,stl) -> loop_iface s (List.map (apply_params iface.cl_params itl) stl)) iface.cl_implements; @@ -81,19 +81,19 @@ let run ~explicit_fn_name ~get_vmtype gen = Overloads.same_overload_args ~get_vmtype ftype t f f2 ) overloads | _ :: _ -> - (match field_access gen (TInst(c, List.map snd c.cl_params)) f.cf_name with + (match field_access gen (TInst(c, extract_param_types c.cl_params)) f.cf_name with | FClassField(_,_,_,f2,false,t,_) -> t,f2 (* if it's not an overload, all functions should have the same signature *) | _ -> raise Not_found) | [] -> raise Not_found in replace_mono t2; (* if we find a function with the exact type of real_ftype, it means this interface has already been taken care of *) - if not (type_iseq (get_real_fun gen (apply_params f2.cf_params (List.map snd f.cf_params) t2)) real_ftype) then begin + if not (type_iseq (get_real_fun gen (apply_params f2.cf_params (extract_param_types f.cf_params) t2)) real_ftype) then begin (match f.cf_kind with | Method (MethNormal | MethInline) -> () | _ -> raise Not_found); let t2 = get_real_fun gen t2 in if List.length f.cf_params <> List.length f2.cf_params then raise Not_found; replace_mono t2; - match follow (apply_params f2.cf_params (List.map snd f.cf_params) t2), follow real_ftype with + match follow (apply_params f2.cf_params (extract_param_types f.cf_params) t2), follow real_ftype with | TFun(a1,r1), TFun(a2,r2) when not implement_explicitly && not (type_iseq r1 r2) && Overloads.same_overload_args ~get_vmtype real_ftype t2 f f2 -> (* different return types are the trickiest cases to deal with *) (* check for covariant return type *) @@ -107,7 +107,7 @@ let run ~explicit_fn_name ~get_vmtype gen = (* we only have to worry about non-covariant issues *) if not is_covariant then begin (* override return type and cast implemented function *) - let args, newr = match follow t2, follow (apply_params f.cf_params (List.map snd f2.cf_params) real_ftype) with + let args, newr = match follow t2, follow (apply_params f.cf_params (extract_param_types f2.cf_params) real_ftype) with | TFun(a,_), TFun(_,r) -> a,r | _ -> Globals.die "" __LOC__ in @@ -133,7 +133,7 @@ let run ~explicit_fn_name ~get_vmtype gen = let vars = List.map (fun (n,_,t) -> alloc_var n t) a2 in let args = List.map2 (fun v (_,_,t) -> mk_cast t (mk_local v f2.cf_pos)) vars a1 in - let field = { eexpr = TField(this, FInstance(c,List.map snd c.cl_params,f2)); etype = TFun(a1,r1); epos = p } in + let field = { eexpr = TField(this, FInstance(c,extract_param_types c.cl_params,f2)); etype = TFun(a1,r1); epos = p } in let call = { eexpr = TCall(field, args); etype = r1; epos = p } in (* let call = gen.gparam_func_call call field (List.map snd f.cf_params) args in *) let is_void = ExtType.is_void r2 in @@ -221,8 +221,8 @@ let run ~explicit_fn_name ~get_vmtype gen = eexpr = TCall( { eexpr = TField( - { eexpr = TConst TThis; etype = TInst(c, List.map snd c.cl_params); epos = p }, - FInstance(c,List.map snd c.cl_params,f)); + { eexpr = TConst TThis; etype = TInst(c, extract_param_types c.cl_params); epos = p }, + FInstance(c,extract_param_types c.cl_params,f)); etype = f.cf_type; epos = p }, diff --git a/src/codegen/gencommon/gencommon.ml b/src/codegen/gencommon/gencommon.ml index e647b48b..9be3c25c 100644 --- a/src/codegen/gencommon/gencommon.ml +++ b/src/codegen/gencommon/gencommon.ml @@ -110,7 +110,7 @@ let follow_once t = | TLazy f -> lazy_type f | TType (t,tl) -> - apply_params t.t_params tl t.t_type + apply_typedef t tl | TAbstract({a_path = [],"Null"},[t]) -> t | _ -> @@ -384,6 +384,8 @@ type generator_ctx = gtools : gen_tools; + gwarning : Warning.warning -> string -> pos -> unit; + (* module filters run before module filters and they should generate valid haxe syntax as a result. Module filters shouldn't go through the expressions as it adds an unnecessary burden to the GC, @@ -573,6 +575,11 @@ let new_ctx con = let rec gen = { gcon = con; + gwarning = (fun w msg p -> + let options = Option.map_default (fun c -> Warning.from_meta c.cl_meta) [] gen.gcurrent_class in + let options = options @ Option.map_default (fun cf -> Warning.from_meta cf.cf_meta) [] gen.gcurrent_classfield in + con.warning w options msg p + ); gentry_point = get_entry_point con; gclasses = { cl_reflect = get_cl (get_type ([], "Reflect")); @@ -612,7 +619,7 @@ let new_ctx con = greal_field_types = Hashtbl.create 0; ghandle_cast = (fun to_t from_t e -> mk_cast to_t e); - gon_unsafe_cast = (fun t t2 pos -> (gen.gcon.warning ("Type " ^ (debug_type t2) ^ " is being cast to the unrelated type " ^ (s_type (print_context()) t)) pos)); + gon_unsafe_cast = (fun t t2 pos -> (gen.gwarning WGenerator ("Type " ^ (debug_type t2) ^ " is being cast to the unrelated type " ^ (s_type (print_context()) t)) pos)); gneeds_box = (fun t -> false); gspecial_needs_cast = (fun to_t from_t -> false); gsupported_conversions = Hashtbl.create 0; @@ -665,7 +672,7 @@ let init_ctx gen = | TLazy f -> follow_f (lazy_type f) | TType (t,tl) -> - follow_f (apply_params t.t_params tl t.t_type) + follow_f (apply_typedef t tl) | TAbstract({a_path = [],"Null"},[t]) -> follow_f t | _ -> Some t @@ -723,9 +730,9 @@ let run_filters_from gen t filters = () let run_filters gen = - let last_error = gen.gcon.error in + let last_error = gen.gcon.located_error in let has_errors = ref false in - gen.gcon.error <- (fun msg pos -> has_errors := true; last_error msg pos); + gen.gcon.located_error <- (fun ?(depth=0) msg -> has_errors := true; last_error ~depth msg); (* first of all, we have to make sure that the filters won't trigger a major Gc collection *) let t = Timer.timer ["gencommon_filters"] in (if Common.defined gen.gcon Define.GencommonDebug then debug_mode := true else debug_mode := false); @@ -813,7 +820,7 @@ let run_filters gen = reorder_modules gen; t(); - if !has_errors then raise (Abort("Compilation aborted with errors",null_pos)) + if !has_errors then abort "Compilation aborted with errors" null_pos (* ******************************************* *) (* basic generation module that source code compilation implementations can use *) @@ -997,6 +1004,31 @@ let mk_nativearray_decl gen t el pos = mk (TCall (mk (TIdent "__array__") t_dynamic pos, el)) (gen.gclasses.nativearray t) pos +let get_boxed gen t = + let get path = + try type_of_module_type (Hashtbl.find gen.gtypes path) + with Not_found -> t + in + match follow t with + | TAbstract({ a_path = ([],"Bool") }, []) -> + get (["java";"lang"], "Boolean") + | TAbstract({ a_path = ([],"Float") }, []) -> + get (["java";"lang"], "Double") + | TAbstract({ a_path = ([],"Int") }, []) -> + get (["java";"lang"], "Integer") + | TAbstract({ a_path = (["java"],"Int8") }, []) -> + get (["java";"lang"], "Byte") + | TAbstract({ a_path = (["java"],"Int16") }, []) -> + get (["java";"lang"], "Short") + | TAbstract({ a_path = (["java"],"Char16") }, []) -> + get (["java";"lang"], "Character") + | TAbstract({ a_path = ([],"Single") }, []) -> + get (["java";"lang"], "Float") + | TAbstract({ a_path = (["java"],"Int64") }, []) + | TAbstract({ a_path = (["haxe"],"Int64") }, []) -> + get (["java";"lang"], "Long") + | _ -> t + (** Wraps rest arguments into a native array. E.g. transforms params from `callee(param, rest1, rest2, ..., restN)` into @@ -1016,9 +1048,8 @@ let wrap_rest_args gen callee_type params p = | _ -> match Abstract.follow_with_abstracts t with | TInst ({ cl_path = _,"NativeArray" }, [t1]) -> - let pos = punion_el (List.map (fun e -> ((),e.epos)) params) in - let t1 = if Common.defined gen.gcon Define.EraseGenerics then t_dynamic else t1 in - [mk_nativearray_decl gen t1 params pos] + let t1 = if Common.defined gen.gcon Define.EraseGenerics then t_dynamic else get_boxed gen t1 in + [mk_nativearray_decl gen t1 params (punion_el p params)] | _ -> die ~p "Unexpected rest arguments type" __LOC__ ) @@ -1044,7 +1075,7 @@ let follow_module follow_func md = match md with | TClassDecl _ | TEnumDecl _ | TAbstractDecl _ -> md - | TTypeDecl tdecl -> match (follow_func (TType(tdecl, List.map snd tdecl.t_params))) with + | TTypeDecl tdecl -> match (follow_func (TType(tdecl, extract_param_types tdecl.t_params))) with | TInst(cl,_) -> TClassDecl cl | TEnum(e,_) -> TEnumDecl e | TType(t,_) -> TTypeDecl t @@ -1164,7 +1195,7 @@ let find_first_declared_field gen orig_cl ?get_vmtype ?exact_field field = loop_cl (depth+1) sup tl tlch ) c.cl_implements in - loop_cl 0 orig_cl (List.map snd orig_cl.cl_params) (List.map snd orig_cl.cl_params); + loop_cl 0 orig_cl (extract_param_types orig_cl.cl_params) (extract_param_types orig_cl.cl_params); match !chosen with | None -> None @@ -1260,7 +1291,7 @@ let rec field_access gen (t:t) (field:string) : (tfield_access) = | _ when PMap.mem field gen.gbase_class_fields -> let cf = PMap.find field gen.gbase_class_fields in FClassField(gen.gclasses.cl_dyn, [t_dynamic], gen.gclasses.cl_dyn, cf, false, cf.cf_type, cf.cf_type) - | TDynamic t -> FDynamicField t + | TDynamic t -> FDynamicField (match t with None -> t_dynamic | Some t -> t) | TMono _ -> FDynamicField t_dynamic | _ -> FNotFound @@ -1272,7 +1303,7 @@ let field_access_esp gen t field = match field with in let p = match follow (run_follow gen t) with | TInst(_,p) -> p - | _ -> List.map snd cl.cl_params + | _ -> extract_param_types cl.cl_params in FClassField(cl,p,cl,cf,static,cf.cf_type,cf.cf_type) | _ -> field_access gen t (field_name field) diff --git a/src/codegen/gencommon/hardNullableSynf.ml b/src/codegen/gencommon/hardNullableSynf.ml index be2467f7..53d94a8c 100644 --- a/src/codegen/gencommon/hardNullableSynf.ml +++ b/src/codegen/gencommon/hardNullableSynf.ml @@ -55,7 +55,7 @@ let rec is_null_t gen t = match gen.greal_type t with | TMono r -> (match r.tm_type with | Some t -> is_null_t gen t | None -> None) | TLazy f -> is_null_t gen (lazy_type f) | TType (t, tl) -> - is_null_t gen (apply_params t.t_params tl t.t_type) + is_null_t gen (apply_typedef t tl) | _ -> None let follow_addon gen t = diff --git a/src/codegen/gencommon/initFunction.ml b/src/codegen/gencommon/initFunction.ml index c31b6165..000d6300 100644 --- a/src/codegen/gencommon/initFunction.ml +++ b/src/codegen/gencommon/initFunction.ml @@ -70,7 +70,8 @@ let handle_override_dynfun acc e this field = | None -> e :: acc | Some add_expr -> add_expr :: e :: acc -let handle_class com cl = +let handle_class gen cl = + let com = gen.gcon in let init = match cl.cl_init with | None -> [] | Some i -> [i] @@ -78,10 +79,10 @@ let handle_class com cl = let init = List.fold_left (fun acc cf -> match cf.cf_kind with | Var v when Meta.has Meta.ReadOnly cf.cf_meta -> - if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then com.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos; + if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then gen.gwarning WGenerator "@:readOnly variable declared without `never` setter modifier" cf.cf_pos; (match cf.cf_expr with - | None -> com.warning "Uninitialized readonly variable" cf.cf_pos - | Some e -> ensure_simple_expr com e); + | None -> gen.gwarning WGenerator "Uninitialized readonly variable" cf.cf_pos + | Some e -> ensure_simple_expr gen.gcon e); acc | Var _ | Method MethDynamic when Type.is_physical_field cf -> @@ -115,7 +116,7 @@ let handle_class com cl = let vars, funs = List.fold_left (fun (acc_vars,acc_funs) cf -> match cf.cf_kind with | Var v when Meta.has Meta.ReadOnly cf.cf_meta -> - if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then com.warning "@:readOnly variable declared without `never` setter modifier" cf.cf_pos; + if v.v_write <> AccNever && not (Meta.has Meta.CoreApi cl.cl_meta) then gen.gwarning WGenerator "@:readOnly variable declared without `never` setter modifier" cf.cf_pos; Option.may (ensure_simple_expr com) cf.cf_expr; (acc_vars,acc_funs) | Var _ @@ -123,7 +124,7 @@ let handle_class com cl = let is_var = match cf.cf_kind with Var _ -> true | _ -> false in (match cf.cf_expr, cf.cf_params with | Some e, [] -> - let var = mk (TField ((mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) cf.cf_pos), FInstance(cl, List.map snd cl.cl_params, cf))) cf.cf_type cf.cf_pos in + let var = mk (TField ((mk (TConst TThis) (TInst (cl, extract_param_types cl.cl_params)) cf.cf_pos), FInstance(cl, extract_param_types cl.cl_params, cf))) cf.cf_type cf.cf_pos in let ret = binop Ast.OpAssign var e cf.cf_type cf.cf_pos in cf.cf_expr <- None; let is_override = has_class_field_flag cf CfOverride in @@ -139,7 +140,7 @@ let handle_class com cl = | Some e, _ -> let params = List.map (fun _ -> t_dynamic) cf.cf_params in let fn = apply_params cf.cf_params params in - let var = mk (TField ((mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) cf.cf_pos), FInstance(cl, List.map snd cl.cl_params, cf))) cf.cf_type cf.cf_pos in + let var = mk (TField ((mk (TConst TThis) (TInst (cl, extract_param_types cl.cl_params)) cf.cf_pos), FInstance(cl, extract_param_types cl.cl_params, cf))) cf.cf_type cf.cf_pos in let rec change_expr e = Type.map_expr_type (change_expr) fn (fun v -> v.v_type <- fn v.v_type; v) e in @@ -173,7 +174,7 @@ let handle_class com cl = ctor | None -> try - let sctor, sup, stl = OverloadingConstructor.prev_ctor cl (List.map snd cl.cl_params) in + let sctor, sup, stl = OverloadingConstructor.prev_ctor cl (extract_param_types cl.cl_params) in let ctor = OverloadingConstructor.clone_ctors com sctor sup stl cl in cl.cl_constructor <- Some ctor; ctor @@ -224,15 +225,15 @@ let handle_class com cl = List.iter process (ctors :: ctors.cf_overloads) ) -let mod_filter com md = +let mod_filter gen md = match md with | TClassDecl cl when not (has_class_flag cl CExtern) -> - handle_class com cl + handle_class gen cl | _ -> () let name = "init_funcs" let priority = solve_deps name [DBefore OverloadingConstructor.priority] let configure gen = - let run = (fun md -> mod_filter gen.gcon md; md) in + let run = (fun md -> mod_filter gen md; md) in gen.gmodule_filters#add name (PCustom priority) run diff --git a/src/codegen/gencommon/interfaceVarsDeleteModf.ml b/src/codegen/gencommon/interfaceVarsDeleteModf.ml index dd93cc5a..9c78af29 100644 --- a/src/codegen/gencommon/interfaceVarsDeleteModf.ml +++ b/src/codegen/gencommon/interfaceVarsDeleteModf.ml @@ -71,7 +71,7 @@ let configure gen = cl.cl_ordered_fields <- fields; List.iter (fun cf -> - match field_access gen (TInst(cl,List.map snd cl.cl_params)) cf.cf_name with + match field_access gen (TInst(cl,extract_param_types cl.cl_params)) cf.cf_name with | FNotFound | FDynamicField _ -> cl.cl_ordered_fields <- cf :: cl.cl_ordered_fields; cl.cl_fields <- PMap.add cf.cf_name cf cl.cl_fields diff --git a/src/codegen/gencommon/overloadingConstructor.ml b/src/codegen/gencommon/overloadingConstructor.ml index aa754f4c..ef99d9f7 100644 --- a/src/codegen/gencommon/overloadingConstructor.ml +++ b/src/codegen/gencommon/overloadingConstructor.ml @@ -114,9 +114,9 @@ let create_static_ctor com ~empty_ctor_expr cl ctor follow_type = | false -> let static_ctor_name = make_static_ctor_name cl in (* create the static constructor *) - let ctor_types = List.map (fun (s,t) -> (s, TInst(map_param (get_cl_t t), []))) cl.cl_params in - let ctor_type_params = List.map snd ctor_types in - List.iter (function (_,TInst(c,[])) -> ( + let ctor_types = List.map (fun tp -> {tp with ttp_type = TInst(map_param (get_cl_t tp.ttp_type), [])}) cl.cl_params in + let ctor_type_params = extract_param_types ctor_types in + List.iter (function {ttp_type=TInst(c,[])} -> ( match c.cl_kind with | KTypeParameter (hd :: tail) -> let before = hd :: tail in @@ -124,11 +124,11 @@ let create_static_ctor com ~empty_ctor_expr cl ctor follow_type = c.cl_kind <- KTypeParameter(after) | _ -> ()) | _ -> ()) ctor_types; - let me = alloc_var "__hx_this" (TInst(cl, List.map snd ctor_types)) in + let me = alloc_var "__hx_this" (TInst(cl, extract_param_types ctor_types)) in add_var_flag me VCaptured; let fn_args, _ = get_fun ctor.cf_type in - let ctor_params = List.map snd ctor_types in + let ctor_params = extract_param_types ctor_types in let fn_type = TFun((me.v_name,false, me.v_type) :: List.map (fun (n,o,t) -> (n,o,apply_params cl.cl_params ctor_params t)) fn_args, com.basic.tvoid) in let cur_tf_args = match ctor.cf_expr with | Some { eexpr = TFunction(tf) } -> tf.tf_args @@ -201,17 +201,33 @@ let create_static_ctor com ~empty_ctor_expr cl ctor follow_type = | None -> [], ctor.cf_pos | Some super -> [super], super.epos in + let el_args = + let rec loop fn_args cur_args = + match cur_args with + | [] -> [] + | (v,_) :: cur_args -> + let local = mk_local v p in + match fn_args, cur_args with + | [_,_,t], [] when ExtType.is_rest (follow t) -> + [mk (TUnop(Spread,Prefix,local)) v.v_type p] + | [], _ -> + local :: loop fn_args cur_args + | _ :: fn_args, _ -> + local :: loop fn_args cur_args + in + loop fn_args cur_tf_args + in let block_contents = block_contents @ [{ eexpr = TCall( { eexpr = TField( Texpr.Builder.make_static_this cl p, FStatic(cl, static_ctor)); - etype = apply_params static_ctor.cf_params (List.map snd cl.cl_params) static_ctor.cf_type; + etype = apply_params static_ctor.cf_params (extract_param_types cl.cl_params) static_ctor.cf_type; epos = p }, - [{ eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = p }] - @ List.map (fun (v,_) -> mk_local v p) cur_tf_args + [{ eexpr = TConst TThis; etype = TInst(cl, extract_param_types cl.cl_params); epos = p }] + @ el_args ); etype = com.basic.tvoid; epos = p @@ -234,7 +250,7 @@ let clone_ctors com ctor sup stl cl = let super_call = { eexpr = TCall( - { eexpr = TConst TSuper; etype = TInst(cl, List.map snd cl.cl_params); epos = ctor.cf_pos }, + { eexpr = TConst TSuper; etype = TInst(cl, extract_param_types cl.cl_params); epos = ctor.cf_pos }, List.map (fun (v,_) -> mk_local v ctor.cf_pos) tf_args); etype = com.basic.tvoid; epos = ctor.cf_pos; @@ -312,7 +328,7 @@ let init com (empty_ctor_type : t) (empty_ctor_expr : texpr) (follow_type : t -> ctor | None -> try - let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in + let sctor, sup, stl = prev_ctor cl (extract_param_types cl.cl_params) in (* we'll make constructors that will only call super() *) let ctor = clone_ctors com sctor sup stl cl in cl.cl_constructor <- Some ctor; @@ -367,12 +383,12 @@ let init com (empty_ctor_type : t) (empty_ctor_expr : texpr) (follow_type : t -> | Some (sup,_) -> try ignore (get_last_empty sup); - let esuper = mk (TConst TSuper) (TInst (cl, List.map snd cl.cl_params)) cl.cl_pos in + let esuper = mk (TConst TSuper) (TInst (cl, extract_param_types cl.cl_params)) cl.cl_pos in [mk (TCall (esuper, [empty_ctor_expr])) basic.tvoid cl.cl_pos] with Not_found -> try (* super type is native: find super constructor with least arguments *) - let sctor, sup, stl = prev_ctor cl (List.map snd cl.cl_params) in + let sctor, sup, stl = prev_ctor cl (extract_param_types cl.cl_params) in let rec loop remaining (best,n) = match remaining with | [] -> best diff --git a/src/codegen/gencommon/realTypeParams.ml b/src/codegen/gencommon/realTypeParams.ml index 26a259e0..f1d0ea9e 100644 --- a/src/codegen/gencommon/realTypeParams.ml +++ b/src/codegen/gencommon/realTypeParams.ml @@ -94,15 +94,15 @@ let rec has_type_params t = let rec follow_all_md md = let t = match md with | TClassDecl { cl_kind = KAbstractImpl a } -> - TAbstract(a, List.map snd a.a_params) + TAbstract(a, extract_param_types a.a_params) | TClassDecl c -> - TInst(c, List.map snd c.cl_params) + TInst(c, extract_param_types c.cl_params) | TEnumDecl e -> - TEnum(e, List.map snd e.e_params) + TEnum(e, extract_param_types e.e_params) | TTypeDecl t -> - TType(t, List.map snd t.t_params) + TType(t, extract_param_types t.t_params) | TAbstractDecl a -> - TAbstract(a, List.map snd a.a_params) + TAbstract(a, extract_param_types a.a_params) in Abstract.follow_with_abstracts t @@ -309,7 +309,7 @@ let set_hxgeneric gen md = if not ret then begin match md with | TClassDecl c -> - let set_hxgeneric (_,param) = match follow param with + let set_hxgeneric tp = match follow tp.ttp_type with | TInst(c,_) -> c.cl_meta <- (Meta.NativeGeneric, [], c.cl_pos) :: c.cl_meta | _ -> () @@ -397,16 +397,16 @@ struct let create_stub_casts gen cl cast_cfield = (* go through superclasses and interfaces *) let p = cl.cl_pos in - let this = { eexpr = TConst TThis; etype = (TInst(cl, List.map snd cl.cl_params)); epos = p } in + let this = { eexpr = TConst TThis; etype = (TInst(cl, extract_param_types cl.cl_params)); epos = p } in let rec loop curcls params level reverse_params = if (level <> 0 || (has_class_flag curcls CInterface) || (has_class_flag curcls CAbstract) ) && params <> [] && is_hxgeneric (TClassDecl curcls) then begin - let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) curcls.cl_params in + let cparams = List.map (fun tp -> {tp with ttp_type=TInst (map_param (get_cl_t tp.ttp_type), [])}) curcls.cl_params in let name = get_cast_name curcls in if not (PMap.mem name cl.cl_fields) then begin let reverse_params = List.map (apply_params curcls.cl_params params) reverse_params in let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in - let field = { eexpr = TField(this, FInstance(cl,List.map snd cl.cl_params, cast_cfield)); etype = apply_params cast_cfield.cf_params reverse_params cast_cfield.cf_type; epos = p } in + let field = { eexpr = TField(this, FInstance(cl,extract_param_types cl.cl_params, cast_cfield)); etype = apply_params cast_cfield.cf_params reverse_params cast_cfield.cf_type; epos = p } in let call = { eexpr = TCall(field, []); @@ -446,7 +446,7 @@ struct loop iface (iface_params) level (get_reverse iface iface_params); ) curcls.cl_implements in - loop cl (List.map snd cl.cl_params) 0 (List.map snd cl.cl_params) + loop cl (extract_param_types cl.cl_params) 0 (extract_param_types cl.cl_params) (* Creates a cast classfield, with the desired name @@ -460,11 +460,11 @@ struct let create_cast_cfield gen cl name = reset_temps(); let basic = gen.gcon.basic in - let cparams = List.map (fun (s,t) -> (s, TInst (map_param (get_cl_t t), []))) cl.cl_params in + let cparams = List.map (fun tp -> {tp with ttp_type = TInst (map_param (get_cl_t tp.ttp_type), [])}) cl.cl_params in let cfield = mk_class_field name (TFun([], t_dynamic)) false cl.cl_pos (Method MethNormal) cparams in - let params = List.map snd cparams in + let params = extract_param_types cparams in - let fields = get_fields gen cl (List.map snd cl.cl_params) params [] in + let fields = get_fields gen cl (extract_param_types cl.cl_params) params [] in let fields = List.filter (fun (cf,_,_) -> Type.is_physical_field cf) fields in (* now create the contents of the function *) @@ -490,7 +490,7 @@ struct let new_me_var = alloc_var "new_me" (TInst (cl, params)) in let local_new_me = mk_local new_me_var pos in - let this = mk (TConst TThis) (TInst (cl, List.map snd cl.cl_params)) pos in + let this = mk (TConst TThis) (TInst (cl, extract_param_types cl.cl_params)) pos in let field_var = alloc_var "field" basic.tstring in let local_field = mk_local field_var pos in let i_var = alloc_var "i" basic.tint in @@ -510,11 +510,11 @@ struct in List.map (fun (cf, t_cl, t_cf) -> let t_cf = follow (gen.greal_type t_cf) in - let this_field = mk (TField (this, FInstance (cl, List.map snd cl.cl_params, cf))) t_cl pos in + let this_field = mk (TField (this, FInstance (cl, extract_param_types cl.cl_params, cf))) t_cl pos in let expr = binop OpAssign - (mk (TField (local_new_me, FInstance(cl, List.map snd cl.cl_params, cf))) t_cf pos) + (mk (TField (local_new_me, FInstance(cl, extract_param_types cl.cl_params, cf))) t_cf pos) (try (Hashtbl.find gen.gtparam_cast (get_path t_cf)) this_field t_cf with Not_found -> (* if not found tparam cast, it shouldn't be a valid hxgeneric *) print_endline ("Could not find a gtparam_cast for " ^ (String.concat "." (fst (get_path t_cf)) ^ "." ^ (snd (get_path t_cf)))); @@ -551,7 +551,7 @@ struct tf_type = t_dynamic; tf_expr = mk (TBlock [ (* if (typeof(T) == typeof(T2)) return this *) - mk (TIf (mk_typehandle_cond (List.map snd cl.cl_params) params, mk_return this, None)) basic.tvoid pos; + mk (TIf (mk_typehandle_cond (extract_param_types cl.cl_params) params, mk_return this, None)) basic.tvoid pos; (* var new_me = /*special create empty with tparams construct*/ *) mk (TVar (new_me_var, Some (gen.gtools.r_create_empty cl params pos))) basic.tvoid pos; (* var fields = Reflect.fields(this); *) @@ -587,13 +587,13 @@ struct let create_static_cast_cf gen iface cf = let p = iface.cl_pos in let basic = gen.gcon.basic in - let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst (map_param (get_cl_t t), []))) cf.cf_params in + let cparams = List.map (fun tp -> {tp with ttp_name = "To_" ^ tp.ttp_name;ttp_type = TInst (map_param (get_cl_t tp.ttp_type), [])}) cf.cf_params in let me_type = TInst(iface,[]) in let cfield = mk_class_field ~static:true "__hx_cast" (TFun(["me",false,me_type], t_dynamic)) false iface.cl_pos (Method MethNormal) (cparams) in - let params = List.map snd cparams in + let params = extract_param_types cparams in let me = alloc_var "me" me_type in - let field = { eexpr = TField(mk_local me p, FInstance(iface, List.map snd iface.cl_params, cf)); etype = apply_params cf.cf_params params cf.cf_type; epos = p } in + let field = { eexpr = TField(mk_local me p, FInstance(iface, extract_param_types iface.cl_params, cf)); etype = apply_params cf.cf_params params cf.cf_type; epos = p } in let call = { eexpr = TCall(field, []); @@ -634,9 +634,9 @@ struct let implement_stub_cast cthis iface tl = let name = get_cast_name iface in if not (PMap.mem name cthis.cl_fields) then begin - let cparams = List.map (fun (s,t) -> ("To_" ^ s, TInst(map_param (get_cl_t t), []))) iface.cl_params in + let cparams = List.map (fun tp -> {tp with ttp_name = "To_" ^ tp.ttp_name;ttp_type = TInst(map_param (get_cl_t tp.ttp_type), [])}) iface.cl_params in let field = mk_class_field name (TFun([],t_dynamic)) false iface.cl_pos (Method MethNormal) cparams in - let this = { eexpr = TConst TThis; etype = TInst(cthis, List.map snd cthis.cl_params); epos = cthis.cl_pos } in + let this = { eexpr = TConst TThis; etype = TInst(cthis, extract_param_types cthis.cl_params); epos = cthis.cl_pos } in field.cf_expr <- Some { etype = TFun([],t_dynamic); epos = this.epos; @@ -672,7 +672,7 @@ struct iface.cl_meta <- (Meta.HxGen, [], cl.cl_pos) :: - (Meta.Custom "generic_iface", [(EConst(Int(string_of_int(List.length cl.cl_params))), cl.cl_pos)], cl.cl_pos) + (Meta.Custom "generic_iface", [(EConst(Int(string_of_int(List.length cl.cl_params), None)), cl.cl_pos)], cl.cl_pos) :: iface.cl_meta; Hashtbl.add ifaces cl.cl_path iface; @@ -766,7 +766,7 @@ let default_implementation gen (dyn_tparam_cast:texpr->t->texpr) ifaces = false) | _ -> false in - let unifies = unifies && not (PMap.mem "cs_safe_casts" gen.gcon.defines.Define.values) in + let unifies = unifies && not (Common.raw_defined gen.gcon "cs_safe_casts") in (match follow t with | TInst(cl, p1 :: pl) when is_hxgeneric (TClassDecl cl) && not unifies && not (Meta.has Meta.Enum cl.cl_meta) -> let iface = Hashtbl.find ifaces cl.cl_path in diff --git a/src/codegen/gencommon/reflectionCFs.ml b/src/codegen/gencommon/reflectionCFs.ml index 0e9457aa..b045494a 100644 --- a/src/codegen/gencommon/reflectionCFs.ml +++ b/src/codegen/gencommon/reflectionCFs.ml @@ -246,7 +246,7 @@ let switch_case ctx pos field_name = let call_super ctx fn_args ret_t cf cl this_t pos = { eexpr = TCall({ - eexpr = TField({ eexpr = TConst(TSuper); etype = this_t; epos = pos }, FInstance(cl,List.map snd cl.cl_params,cf)); + eexpr = TField({ eexpr = TConst(TSuper); etype = this_t; epos = pos }, FInstance(cl,extract_param_types cl.cl_params,cf)); etype = TFun(fun_args fn_args, ret_t); epos = pos; }, List.map (fun (v,_) -> mk_local v pos) fn_args); @@ -280,7 +280,7 @@ let enumerate_dynamic_fields ctx cl when_found base_arr = ] in - let this_t = TInst(cl, List.map snd cl.cl_params) in + let this_t = TInst(cl, extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in @@ -467,7 +467,7 @@ let abstract_dyn_lookup_implementation ctx this field_local hash_local may_value let get_delete_field ctx cl is_dynamic = let pos = cl.cl_pos in - let this_t = TInst(cl, List.map snd cl.cl_params) in + let this_t = TInst(cl, extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in let gen = ctx.rcf_gen in let basic = gen.gcon.basic in @@ -687,7 +687,7 @@ let implement_final_lookup ctx cl = let pos = cl.cl_pos in let is_override = is_override cl in - (* let this = { eexpr = TConst(TThis); etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in *) + (* let this = { eexpr = TConst(TThis); etype = TInst(cl, extract_param_types cl.cl_params); epos = pos } in *) let mk_throw str pos = let e = ctx.rcf_mk_exception str pos in @@ -806,7 +806,7 @@ let implement_get_set ctx cl = let handle_prop = alloc_var "handleProperties" basic.tbool in let handle_prop_local = mk_local handle_prop pos in - let this = { eexpr = TConst TThis; etype = TInst(cl, List.map snd cl.cl_params); epos = pos } in + let this = { eexpr = TConst TThis; etype = TInst(cl, extract_param_types cl.cl_params); epos = pos } in let mk_this_call_raw name fun_t params = { eexpr = TCall( { (mk_field_access gen this name pos) with etype = fun_t; }, params ); etype = snd (get_fun fun_t); epos = pos } in @@ -817,7 +817,7 @@ let implement_get_set ctx cl = let maybe_cast e = e in - let t = TInst(cl, List.map snd cl.cl_params) in + let t = TInst(cl, extract_param_types cl.cl_params) in (* if it's not latest hxgen class -> check super *) let mk_do_default args do_default = @@ -829,7 +829,7 @@ let implement_get_set ctx cl = fun () -> mk_return { eexpr = TCall( - { eexpr = TField({ eexpr = TConst TSuper; etype = t; epos = pos }, FInstance(cl, List.map snd cl.cl_params, cfield)); etype = !fun_type; epos = pos }, + { eexpr = TField({ eexpr = TConst TSuper; etype = t; epos = pos }, FInstance(cl, extract_param_types cl.cl_params, cfield)); etype = !fun_type; epos = pos }, (List.map (fun (v,_) -> mk_local v pos) args) ); etype = if is_float then basic.tfloat else t_dynamic; epos = pos; @@ -849,7 +849,7 @@ let implement_get_set ctx cl = in let do_field cf cf_type = - let get_field ethis = { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos } in + let get_field ethis = { eexpr = TField (ethis, FInstance(cl, extract_param_types cl.cl_params, cf)); etype = cf_type; epos = pos } in let this = { eexpr = TConst(TThis); etype = t; epos = pos } in let value_local = if is_float then match follow cf_type with | TInst({ cl_kind = KTypeParameter _ }, _) -> @@ -936,15 +936,15 @@ let implement_get_set ctx cl = eexpr = TIf( handle_prop_local, mk_this_call_raw ("get_" ^ cf.cf_name) (TFun(["value",false,cf.cf_type], cf.cf_type)) [], - Some { eexpr = TField (ethis, FInstance(cl, List.map snd cl.cl_params, cf)); etype = cf_type; epos = pos } + Some { eexpr = TField (ethis, FInstance(cl, extract_param_types cl.cl_params, cf)); etype = cf_type; epos = pos } ); etype = cf_type; epos = pos; } | Var _ - | Method MethDynamic -> { eexpr = TField (ethis, FInstance(cl,List.map snd cl.cl_params,cf)); etype = cf_type; epos = pos } + | Method MethDynamic -> { eexpr = TField (ethis, FInstance(cl,extract_param_types cl.cl_params,cf)); etype = cf_type; epos = pos } | _ -> - { eexpr = TField (this, FClosure(Some (cl,List.map snd cl.cl_params), cf)); etype = cf_type; epos = pos } + { eexpr = TField (this, FClosure(Some (cl,extract_param_types cl.cl_params), cf)); etype = cf_type; epos = pos } in let do_field cf cf_type = @@ -1077,7 +1077,7 @@ let implement_getFields ctx cl = *) let exprs = if is_override cl then - let tparams = List.map snd cl.cl_params in + let tparams = extract_param_types cl.cl_params in let esuper = mk (TConst TSuper) (TInst(cl, tparams)) pos in let efield = mk (TField (esuper, FInstance (cl, tparams, cf))) t pos in [mk (TCall (efield, [base_arr])) basic.tvoid pos] @@ -1140,7 +1140,7 @@ let implement_invokeField ctx slow_invoke cl = let all_args = field_args @ [ dynamic_arg, None ] in let fun_t = TFun(fun_args all_args, t_dynamic) in - let this_t = TInst(cl, List.map snd cl.cl_params) in + let this_t = TInst(cl, extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t; epos = pos } in let mk_this_call_raw name fun_t params = @@ -1280,7 +1280,7 @@ let implement_varargs_cl ctx cl = let pos = cl.cl_pos in let gen = ctx.rcf_gen in - let this_t = TInst(cl, List.map snd cl.cl_params) in + let this_t = TInst(cl, extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in @@ -1339,7 +1339,7 @@ let implement_closure_cl ctx cl = let field_args, _ = field_type_args ctx pos in let obj_arg = alloc_var "target" (TInst(ctx.rcf_object_iface, [])) in - let this_t = TInst(cl, List.map snd cl.cl_params) in + let this_t = TInst(cl, extract_param_types cl.cl_params) in let this = { eexpr = TConst(TThis); etype = this_t ; epos = pos } in let mk_this field t = { (mk_field_access gen this field pos) with etype = t } in diff --git a/src/codegen/gencommon/renameTypeParameters.ml b/src/codegen/gencommon/renameTypeParameters.ml index 2383da1d..3ec5b2f1 100644 --- a/src/codegen/gencommon/renameTypeParameters.ml +++ b/src/codegen/gencommon/renameTypeParameters.ml @@ -47,16 +47,16 @@ let run types = | _ -> Globals.die "" __LOC__ in - let iter_types (nt,t) = - let cls = get_cls t in + let iter_types tp = + let cls = get_cls tp.ttp_type in let orig = cls.cl_path in check_type (snd orig) (fun name -> cls.cl_path <- (fst orig, name)) in let save_params save params = - List.fold_left (fun save (_,t) -> - let cls = get_cls t in - (cls.cl_path,t) :: save) save params + List.fold_left (fun save tp -> + let cls = get_cls tp.ttp_type in + (cls.cl_path,tp.ttp_type) :: save) save params in List.iter (function diff --git a/src/codegen/gencommon/unreachableCodeEliminationSynf.ml b/src/codegen/gencommon/unreachableCodeEliminationSynf.ml index 71859948..a8913367 100644 --- a/src/codegen/gencommon/unreachableCodeEliminationSynf.ml +++ b/src/codegen/gencommon/unreachableCodeEliminationSynf.ml @@ -63,11 +63,11 @@ let rec get_constant_expr e = | TParenthesis(e) | TMeta(_,e) -> get_constant_expr e | _ -> None -let init com java_mode = +let init gen java_mode = let should_warn = false in let do_warn = - if should_warn then com.warning "Unreachable code" else (fun pos -> ()) + if should_warn then gen.gwarning WGenerator "Unreachable code" else (fun pos -> ()) in let return_loop expr kind = @@ -208,5 +208,5 @@ let init com java_mode = let priority = min_dep -. 100.0 let configure gen java_mode = - let run = init gen.gcon java_mode in + let run = init gen java_mode in gen.gsyntax_filters#add "unreachable_synf" (PCustom priority) run diff --git a/src/codegen/genxml.ml b/src/codegen/genxml.ml index 95824dcc..64c19c1e 100644 --- a/src/codegen/genxml.ml +++ b/src/codegen/genxml.ml @@ -122,7 +122,8 @@ let rec gen_type ?(values=None) t = ) args in node "f" (("a",names) :: values) (List.map gen_type (args @ [r])) | TAnon a -> node "a" [] (pmap (fun f -> gen_field [] { f with cf_flags = unset_flag f.cf_flags (int_of_class_field_flag CfPublic) }) a.a_fields) - | TDynamic t2 -> node "d" [] (if t == t2 then [] else [gen_type t2]) + | TDynamic None -> node "d" [] [] + | TDynamic (Some t2) -> node "d" [] [gen_type t2] | TLazy f -> gen_type (lazy_type f) and gen_type_decl n t pl = @@ -157,7 +158,7 @@ and gen_field att f = in att,get_value_meta f.cf_meta ) in - let att = (match f.cf_params with [] -> att | l -> ("params", String.concat ":" (List.map (fun (n,_) -> n) l)) :: att) in + let att = (match f.cf_params with [] -> att | l -> ("params", String.concat ":" (List.map extract_param_name l)) :: att) in let overloads = match List.map (gen_field []) f.cf_overloads with | [] -> [] | nl -> [node "overloads" [] nl] @@ -200,7 +201,7 @@ let gen_type_params ipos priv path params pos m = let mpriv = (if priv then [("private","1")] else []) in let mpath = (if m.m_path <> path then [("module",snd (gen_path m.m_path false))] else []) in let file = (if ipos && pos <> null_pos then [("file",pos.pfile)] else []) in - gen_path path priv :: ("params", String.concat ":" (List.map fst params)) :: (file @ mpriv @ mpath) + gen_path path priv :: ("params", String.concat ":" (List.map extract_param_name params)) :: (file @ mpriv @ mpath) let gen_class_path name (c,pl) = node name [("path",s_type_path (tpath (TClassDecl c)))] (List.map gen_type pl) @@ -216,7 +217,7 @@ let rec gen_type_decl com pos t = match t with | TClassDecl c -> let stats = List.filter (fun cf -> - cf.cf_name <> "__meta__" && not (Meta.has Meta.GenericInstance cf.cf_meta) + cf.cf_name <> "__meta__" && not (Meta.has Meta.GenericInstance cf.cf_meta) && not (Meta.has Meta.NoDoc cf.cf_meta) ) c.cl_ordered_statics in let stats = List.map (gen_field ["static","1"]) stats in let fields = List.filter (fun cf -> @@ -253,9 +254,9 @@ let rec gen_type_decl com pos t = let doc = gen_doc_opt a.a_doc in let meta = gen_meta a.a_meta in let mk_cast t = node "icast" [] [gen_type t] in - let mk_field_cast (t,cf) = node "icast" ["field",cf.cf_name] [gen_type t] in - let sub = (match a.a_from,a.a_from_field with [],[] -> [] | l1,l2 -> [node "from" [] ((List.map mk_cast l1) @ (List.map mk_field_cast l2))]) in - let super = (match a.a_to,a.a_to_field with [],[] -> [] | l1,l2 -> [node "to" [] ((List.map mk_cast l1) @ (List.map mk_field_cast l2))]) in + let mk_field_cast (t,cf) = if Meta.has Meta.NoDoc cf.cf_meta then None else Some (node "icast" ["field",cf.cf_name] [gen_type t]) in + let sub = (match a.a_from,a.a_from_field with [],[] -> [] | l1,l2 -> [node "from" [] ((List.map mk_cast l1) @ (ExtList.List.filter_map mk_field_cast l2))]) in + let super = (match a.a_to,a.a_to_field with [],[] -> [] | l1,l2 -> [node "to" [] ((List.map mk_cast l1) @ (ExtList.List.filter_map mk_field_cast l2))]) in let impl = (match a.a_impl with None -> [] | Some c -> [node "impl" [] [gen_type_decl com pos (TClassDecl c)]]) in let this = [node "this" [] [gen_type a.a_this]] in node "abstract" (gen_type_params pos a.a_private (tpath t) a.a_params a.a_pos m) (sub @ this @ super @ doc @ meta @ impl) diff --git a/src/codegen/java.ml b/src/codegen/java.ml index cc3736c2..ba86b4ee 100644 --- a/src/codegen/java.ml +++ b/src/codegen/java.ml @@ -35,9 +35,9 @@ type java_lib_ctx = { is_std : bool; } -exception ConversionError of string * pos +exception ConversionError of located -let error s p = raise (ConversionError (s, p)) +let error s p = raise (ConversionError (located s p)) let is_haxe_keyword = function | "cast" | "extern" | "function" | "in" | "typedef" | "using" | "var" | "untyped" | "inline" -> true @@ -172,8 +172,8 @@ and convert_signature ctx p jsig = let convert_constant ctx p const = Option.map_default (function | ConstString s -> Some (EConst (String(s,SDoubleQuotes)), p) - | ConstInt i -> Some (EConst (Int (Printf.sprintf "%ld" i)), p) - | ConstFloat f | ConstDouble f -> Some (EConst (Float (Printf.sprintf "%E" f)), p) + | ConstInt i -> Some (EConst (Int (Printf.sprintf "%ld" i, None)), p) + | ConstFloat f | ConstDouble f -> Some (EConst (Float (Printf.sprintf "%E" f, None)), p) | _ -> None) None const let convert_constraints ctx p tl = match tl with @@ -192,6 +192,7 @@ let convert_param ctx p parent param = tp_name = jname_to_hx name,null_pos; tp_params = []; tp_constraints = convert_constraints ctx p constraints; + tp_default = None; tp_meta = []; } @@ -215,6 +216,17 @@ let show_in_completion ctx jc = | ("java" | "javax" | "org") :: _ -> true | _ -> false +(** + `haxe.Rest` auto-boxes primitive types. + That means we can't use it as varargs for extern methods. + E.g externs with `int` varargs are represented as `int[]` at run time + while `haxe.Rest` is actually `java.lang.Integer[]`. +*) +let is_eligible_for_haxe_rest_args arg_type = + match arg_type with + | TByte | TChar | TDouble | TFloat | TInt | TLong | TShort | TBool -> false + | _ -> true + let convert_java_enum ctx p pe = let meta = ref (get_canonical ctx p (fst pe.cpath) (snd pe.cpath) :: [Meta.Native, [EConst (String (real_java_path ctx pe.cpath,SDoubleQuotes) ), p], p ]) in let data = ref [] in @@ -254,6 +266,7 @@ let convert_java_enum ctx p pe = in let jf_constant = ref field.jf_constant in let readonly = ref false in + let is_varargs = ref false in List.iter (function | JPublic -> cff_access := (APublic,null_pos) :: !cff_access @@ -274,7 +287,7 @@ let convert_java_enum ctx p pe = (* | JSynchronized -> cff_meta := (Meta.Synchronized, [], p) :: !cff_meta *) | JVolatile -> cff_meta := (Meta.Volatile, [], p) :: !cff_meta | JTransient -> cff_meta := (Meta.Transient, [], p) :: !cff_meta - (* | JVarArgs -> cff_meta := (Meta.VarArgs, [], p) :: !cff_meta *) + | JVarArgs -> is_varargs := true | JAbstract when not is_interface -> cff_access := (AAbstract, p) :: !cff_access | _ -> () @@ -341,9 +354,17 @@ let convert_java_enum ctx p pe = | c :: others -> ctx.jtparams <- (c @ field.jf_types) :: others | [] -> ctx.jtparams <- field.jf_types :: []); let i = ref 0 in + let args_count = List.length args in let args = List.map (fun s -> incr i; - (local_names !i,null_pos), false, [], Some(convert_signature ctx p s,null_pos), None + let hx_sig = + match s with + | TArray (s1,_) when !is_varargs && !i = args_count && is_eligible_for_haxe_rest_args s1 -> + mk_type_path ctx (["haxe"], "Rest") [TPType (convert_signature ctx p s1,null_pos)] + | _ -> + convert_signature ctx null_pos s + in + (local_names !i,null_pos), false, [], Some(hx_sig,null_pos), None ) args in let t = Option.map_default (convert_signature ctx p) (mk_type_path ctx ([], "Void") []) ret in cff_access := (AOverload,p) :: !cff_access; @@ -353,6 +374,7 @@ let convert_java_enum ctx p pe = tp_name = name,null_pos; tp_params = []; tp_constraints = convert_constraints ctx p (ext :: impl); + tp_default = None; tp_meta = []; } | (name, None, impl) -> @@ -360,6 +382,7 @@ let convert_java_enum ctx p pe = tp_name = name,null_pos; tp_params = []; tp_constraints = convert_constraints ctx p impl; + tp_default = None; tp_meta = []; } ) field.jf_types in @@ -373,6 +396,7 @@ let convert_java_enum ctx p pe = }) | _ -> error "Method signature was expected" p in + if field.jf_code <> None && is_interface then cff_meta := (Meta.JavaDefault,[],cff_pos) :: !cff_meta; let cff_name, cff_meta = match String.get cff_name 0 with | '%' -> @@ -391,7 +415,7 @@ let convert_java_enum ctx p pe = String.concat "_" parts, (Meta.Native, [EConst (String (cff_name,SDoubleQuotes) ), cff_pos], cff_pos) :: !cff_meta in - if PMap.mem "java_loader_debug" ctx.jcom.defines.Define.values then + if Common.raw_defined ctx.jcom "java_loader_debug" then Printf.printf "\t%s%sfield %s : %s\n" (if List.mem_assoc AStatic !cff_access then "static " else "") (if List.mem_assoc AOverride !cff_access then "override " else "") cff_name (s_sig field.jf_signature); { @@ -433,7 +457,7 @@ let convert_java_enum ctx p pe = [convert_java_enum ctx p jc] | false -> let flags = ref [HExtern] in - if PMap.mem "java_loader_debug" ctx.jcom.defines.Define.values then begin + if Common.raw_defined ctx.jcom "java_loader_debug" then begin let sup = jc.csuper :: jc.cinterfaces in print_endline ("converting " ^ (if List.mem JAbstract jc.cflags then "abstract " else "") ^ JData.path_s jc.cpath ^ " : " ^ (String.concat ", " (List.map s_sig sup))); end; @@ -1242,7 +1266,7 @@ let add_java_lib com name std extern modern = let before_generate con = let java_ver = try - int_of_string (PMap.find "java_ver" con.defines.Define.values) + int_of_string (Common.defined_value con Define.JavaVer) with | Not_found -> Common.define_value con Define.JavaVer "7"; 7 diff --git a/src/codegen/javaModern.ml b/src/codegen/javaModern.ml index ad5dbebb..5908c19a 100644 --- a/src/codegen/javaModern.ml +++ b/src/codegen/javaModern.ml @@ -196,33 +196,34 @@ module JReaderHoldovers = struct let parse_formal_type_params s = match s.[0] with | '<' -> - let rec read_id i = - match s.[i] with - | ':' | '>' -> i - | _ -> read_id (i + 1) + let rec read_id i = match s.[i] with + | ':' | '>' -> i + | _ -> read_id (i + 1) in let len = String.length s in let rec parse_params idx acc = - let idi = read_id (idx + 1) in - let id = String.sub s (idx + 1) (idi - idx - 1) in - (* next must be a : *) - (match s.[idi] with | ':' -> () | _ -> failwith ("Invalid formal type signature character: " ^ Char.escaped s.[idi] ^ " ; from " ^ s)); - let ext, l = match s.[idi + 1] with - | ':' | '>' -> None, idi + 1 - | _ -> - let sgn, l = parse_signature_part (String.sub s (idi + 1) (len - idi - 1)) in - Some sgn, l + idi + 1 - in - let rec loop idx acc = - match s.[idx] with - | ':' -> - let ifacesig, ifacei = parse_signature_part (String.sub s (idx + 1) (len - idx - 1)) in - loop (idx + ifacei + 1) (ifacesig :: acc) - | _ -> acc, idx - in - let ifaces, idx = loop l [] in - let acc = (id, ext, ifaces) :: acc in - if s.[idx] = '>' then List.rev acc, idx + 1 else parse_params (idx - 1) acc + let idi = read_id (idx + 1) in + let id = String.sub s (idx + 1) (idi - idx - 1) in + (* next must be a : *) + (match s.[idi] with | ':' -> () | _ -> failwith ("Invalid formal type signature character: " ^ Char.escaped s.[idi] ^ " ; from " ^ s)); + let ext, l = match s.[idi + 1] with + | ':' | '>' -> + None, idi + 1 + | _ -> + let sgn, l = parse_signature_part (String.sub s (idi + 1) (len - idi - 1)) in + Some sgn, l + idi + 1 + in + let rec loop idx acc = + match s.[idx] with + | ':' -> + let ifacesig, ifacei = parse_signature_part (String.sub s (idx + 1) (len - idx - 1)) in + loop (idx + ifacei + 1) (ifacesig :: acc) + | _ -> + acc, idx + in + let ifaces, idx = loop l [] in + let acc = (id, ext, ifaces) :: acc in + if s.[idx] = '>' then List.rev acc, idx + 1 else parse_params (idx - 1) acc in parse_params 0 [] | _ -> [], 0 @@ -720,9 +721,10 @@ module Converter = struct tp_name = (name,p); tp_params = []; tp_meta = []; + tp_default = None; tp_constraints = match constraints with | [] -> None - | _ -> Some (CTIntersection constraints,p) + | _ -> Some (CTIntersection constraints,p); } in tp @@ -758,6 +760,17 @@ module Converter = struct PMap.add s (ct_type_param s) acc ) acc params + (** + `haxe.Rest` auto-boxes primitive types. + That means we can't use it as varargs for extern methods. + E.g externs with `int` varargs are represented as `int[]` at run time + while `haxe.Rest` is actually `java.lang.Integer[]`. + *) + let is_eligible_for_haxe_rest_args arg_type = + match arg_type with + | TByte | TChar | TDouble | TFloat | TInt | TLong | TShort | TBool -> false + | _ -> true + let convert_field ctx is_method (jc : jclass) (is_interface : bool) (jf : jfield) p = let ctx = { type_params = type_param_lut ctx.type_params jf.jf_types; @@ -778,6 +791,8 @@ module Converter = struct add_access (AOverride,null_pos); | _ -> () ) ann + | AttrCode _ when is_interface -> + add_meta (Meta.JavaDefault,[],p) | _ -> () ) jf.jf_attributes; let add_native_meta () = @@ -845,9 +860,18 @@ module Converter = struct begin match jf.jf_descriptor with | TMethod(args,ret) -> let local_names = extract_local_names() in + let args_count = List.length args + and is_varargs = AccessFlags.has_flag jf.jf_flags MVarargs in let convert_arg i jsig = let name = local_names (i + 1) in - ((name,p),false,[],Some (convert_signature ctx p jsig,p),None) + let hx_sig = + match jsig with + | TArray (jsig1,_) when is_varargs && i + 1 = args_count && is_eligible_for_haxe_rest_args jsig1 -> + mk_type_path (["haxe"], "Rest") [TPType (convert_signature ctx p jsig1,p)] + | _ -> + convert_signature ctx p jsig + in + ((name,p),false,[],Some (hx_sig,p),None) in let f = { f_params = List.map (fun tp -> convert_type_parameter ctx tp p) jf.jf_types; diff --git a/src/codegen/overloads.ml b/src/codegen/overloads.ml index 11aeaaa3..9face49e 100644 --- a/src/codegen/overloads.ml +++ b/src/codegen/overloads.ml @@ -12,14 +12,14 @@ let same_overload_args ?(get_vmtype) t1 t2 f1 f2 = let rec loop params1 params2 = match params1,params2 with | [],[] -> true - | (n1,t1) :: params1,(n2,t2) :: params2 -> - let constraints_equal t1 t2 = match follow t1,t2 with + | tp1 :: params1,tp2 :: params2 -> + let constraints_equal t1 t2 = match follow t1,follow t2 with | TInst({cl_kind = KTypeParameter tl1},_),TInst({cl_kind = KTypeParameter tl2},_) -> Ast.safe_for_all2 f_eq tl1 tl2 | _ -> false in - n1 = n2 && constraints_equal t1 t2 && loop params1 params2 + tp1.ttp_name = tp2.ttp_name && constraints_equal tp1.ttp_type tp2.ttp_type && loop params1 params2 | [],_ | _,[] -> false @@ -39,7 +39,7 @@ let same_overload_args ?(get_vmtype) t1 t2 f1 f2 = loop tl1 tl2 in let compare_types () = - let t1 = follow (apply_params f1.cf_params (List.map (fun (_,t) -> t) f2.cf_params) t1) in + let t1 = follow (apply_params f1.cf_params (extract_param_types f2.cf_params) t1) in match t1,follow t2 with | TFun(tl1,_),TFun(tl2,_) -> compare_arguments tl1 tl2 @@ -84,10 +84,10 @@ let collect_overloads map c i = let get_overloads (com : Common.context) c i = try - Hashtbl.find com.overload_cache (c.cl_path,i) + com.overload_cache#find (c.cl_path,i) with Not_found -> let l = collect_overloads (fun t -> t) c i in - Hashtbl.add com.overload_cache (c.cl_path,i) l; + com.overload_cache#add (c.cl_path,i) l; l (** Overload resolution **) @@ -106,7 +106,7 @@ struct t | TAbstract(a,tl) -> simplify_t (Abstract.get_underlying_type a tl) | TType(t, tl) -> - simplify_t (apply_params t.t_params tl t.t_type) + simplify_t (apply_typedef t tl) | TMono r -> (match r.tm_type with | Some t -> simplify_t t | None -> t_dynamic) @@ -258,43 +258,59 @@ struct in (* convert compatible into ( rate * compatible_type ) list *) let rec mk_rate acc elist args = match elist, args with - | [], [] -> acc - | _ :: elist, (_,true,_) :: args -> mk_rate acc elist args + | [], [] -> + acc,false + | _ :: elist, (_,true,_) :: args -> + mk_rate acc elist args | elist, [n,o,t] when ExtType.is_rest (follow t) -> let t = match follow t with | TAbstract({a_path=["haxe"],"Rest"},[t]) -> t | _ -> die "" __LOC__ in let rates = List.map (rate_arg t) elist in - acc @ rates + acc @ rates,true | e :: elist, (n,o,t) :: args -> mk_rate ((rate_arg t e) :: acc) elist args | [],_ -> (* this can happen on pf_pad_nulls = false targets, see #10434 *) - acc + acc,false | _ -> die "" __LOC__ in - let rated = ref [] in - List.iter (fun fcc -> match fcc.fc_type with - | TFun(args,ret) -> (try - rated := ( fcc, mk_rate [] fcc.fc_args args ) :: !rated - with | Not_found -> ()) - | _ -> die "" __LOC__ - ) compatible; - - let rec loop best rem = match best, rem with - | _, [] -> best - | [], r1 :: rem -> loop [r1] rem - | (bover, bargs) :: b1, (rover, rargs) :: rem -> - if is_best bargs rargs then - loop best rem - else if is_best rargs bargs then - loop (loop b1 [rover,rargs]) rem - else (* equally specific *) - loop ( (rover,rargs) :: best ) rem + let rec loop best l = match l with + | [] -> + begin match best with + | Some(_,_,l) -> List.rev l + | None -> [] + end + | fcc :: l -> + let args,ret = match follow fcc.fc_type with + | TFun(args,ret) -> args,ret + | _ -> die "" __LOC__ + in + begin try + let (rate,is_rest) = mk_rate [] fcc.fc_args args in + let (best_rate,best_is_rest,best_l) = match best with + | None -> + (* If it's the first one, assume it's the best *) + (rate,is_rest,[fcc]) + | Some(best_rate,best_is_rest,best_l) -> + if is_best rate best_rate then + (rate,is_rest,[fcc]) + else if is_best best_rate rate then + (best_rate,best_is_rest,best_l) + (* If they are equal, we prefer the one without Rest (issue #10205) *) + else if is_rest && not best_is_rest then + (best_rate,best_is_rest,best_l) + else if not is_rest && best_is_rest then + (rate,is_rest,[fcc]) + else + (best_rate,best_is_rest,fcc :: best_l) + in + loop (Some(best_rate,best_is_rest,best_l)) l + with Not_found -> + loop best l + end in - - let r = loop [] !rated in - List.map fst r + loop None compatible end diff --git a/src/codegen/swfLoader.ml b/src/codegen/swfLoader.ml index 26ae20cd..6b7ef3b6 100644 --- a/src/codegen/swfLoader.ml +++ b/src/codegen/swfLoader.ml @@ -270,9 +270,9 @@ let build_class com c file = | HVBool b -> Some (Ident (if b then "true" else "false")) | HVInt i | HVUInt i -> - Some (Int (Int32.to_string i)) + Some (Int (Int32.to_string i, None)) | HVFloat f -> - Some (Float (Numeric.float_repres f)) + Some (Float (Numeric.float_repres f, None)) ) in match v with | None -> None @@ -545,7 +545,7 @@ let remove_debug_infos as3 = let parse_swf com file = let t = Timer.timer ["read";"swf"] in - let is_swc = file_extension file = "swc" || file_extension file = "ane" in + let is_swc = Path.file_extension file = "swc" || Path.file_extension file = "ane" in let ch = if is_swc then begin let zip = Zip.open_in file in try @@ -569,7 +569,7 @@ let parse_swf com file = IO.close_in ch; List.iter (fun t -> match t.tdata with - | TActionScript3 (id,as3) when not com.debug && not com.display.DisplayTypes.DisplayMode.dms_display -> + | TActionScript3 (id,as3) -> t.tdata <- TActionScript3 (id,remove_debug_infos as3) | _ -> () ) tags; diff --git a/src/compiler/args.ml b/src/compiler/args.ml new file mode 100644 index 00000000..b8e0dbfe --- /dev/null +++ b/src/compiler/args.ml @@ -0,0 +1,370 @@ +open Globals +open Common +open CompilationContext + +let limit_string s offset = + let rest = 80 - offset in + let words = ExtString.String.nsplit s " " in + let rec loop i words = match words with + | word :: words -> + if String.length word + i + 1 > rest then (Printf.sprintf "\n%*s" offset "") :: word :: loop (String.length word) words + else (if i = 0 then "" else " ") :: word :: loop (i + 1 + String.length word) words + | [] -> + [] + in + String.concat "" (loop 0 words) + +let usage_string ?(print_cat=true) arg_spec usage = + let make_label = fun names hint -> Printf.sprintf "%s %s" (String.concat ", " names) hint in + let args = (List.filter (fun (cat, ok, dep, spec, hint, doc) -> (List.length ok) > 0) arg_spec) in + let cat_order = ["Target";"Compilation";"Optimization";"Debug";"Batch";"Services";"Compilation Server";"Target-specific";"Miscellaneous"] in + let cats = List.filter (fun x -> List.mem x (List.map (fun (cat, _, _, _, _, _) -> cat) args)) cat_order in + let max_length = List.fold_left max 0 (List.map String.length (List.map (fun (_, ok, _, _, hint, _) -> make_label ok hint) args)) in + usage ^ (String.concat "\n" (List.flatten (List.map (fun cat -> (if print_cat then ["\n"^cat^":"] else []) @ (List.map (fun (cat, ok, dep, spec, hint, doc) -> + let label = make_label ok hint in + Printf.sprintf " %s%s %s" label (String.make (max_length - (String.length label)) ' ') doc + ) (List.filter (fun (cat', _, _, _, _, _) -> (if List.mem cat' cat_order then cat' else "Miscellaneous") = cat) args))) cats))) + +let process_args arg_spec = + List.flatten(List.map (fun (cat, ok, dep, spec, hint, doc) -> + (* official argument names *) + (List.map (fun (arg) -> (arg, spec, doc)) ok) @ + let dep_fun arg spec = () in + let dep_spec arg spec = match spec with + | Arg.String f -> Arg.String (fun x -> dep_fun arg spec; f x) + | Arg.Unit f -> Arg.Unit (fun x -> dep_fun arg spec; f x) + | Arg.Bool f -> Arg.Bool (fun x -> dep_fun arg spec; f x) + | _ -> spec in + (List.map (fun (arg) -> (arg, dep_spec arg spec, doc)) dep) + ) arg_spec) + +let parse_args com = + let usage = Printf.sprintf + "Haxe Compiler %s - (C)2005-2022 Haxe Foundation\nUsage: haxe%s [options] [hxml files and dot paths...]\n" + s_version_full (if Sys.os_type = "Win32" then ".exe" else "") + in + let actx = { + classes = [([],"Std")]; + xml_out = None; + json_out = None; + cmds = []; + config_macros = []; + no_output = false; + did_something = false; + force_typing = false; + pre_compilation = []; + interp = false; + jvm_flag = false; + swf_version = false; + native_libs = []; + raise_usage = (fun () -> ()); + display_arg = None; + deprecations = []; + } in + let add_deprecation s = + actx.deprecations <- s :: actx.deprecations + in + let add_native_lib file extern = actx.native_libs <- (file,extern) :: actx.native_libs in + let basic_args_spec = [ + ("Target",["--js"],["-js"],Arg.String (set_platform com Js),"","generate JavaScript code into target file"); + ("Target",["--lua"],["-lua"],Arg.String (set_platform com Lua),"","generate Lua code into target file"); + ("Target",["--swf"],["-swf"],Arg.String (set_platform com Flash),"","generate Flash SWF bytecode into target file"); + ("Target",["--neko"],["-neko"],Arg.String (set_platform com Neko),"","generate Neko bytecode into target file"); + ("Target",["--php"],["-php"],Arg.String (fun dir -> + actx.classes <- (["php"],"Boot") :: actx.classes; + set_platform com Php dir; + ),"","generate PHP code into target directory"); + ("Target",["--cpp"],["-cpp"],Arg.String (fun dir -> + set_platform com Cpp dir; + ),"","generate C++ code into target directory"); + ("Target",["--cppia"],["-cppia"],Arg.String (fun file -> + Common.define com Define.Cppia; + set_platform com Cpp file; + ),"","generate Cppia bytecode into target file"); + ("Target",["--cs"],["-cs"],Arg.String (fun dir -> + set_platform com Cs dir; + ),"","generate C# code into target directory"); + ("Target",["--java"],["-java"],Arg.String (fun dir -> + set_platform com Java dir; + ),"","generate Java code into target directory"); + ("Target",["--jvm"],[],Arg.String (fun dir -> + Common.define com Define.Jvm; + actx.jvm_flag <- true; + set_platform com Java dir; + ),"","generate JVM bytecode into target file"); + ("Target",["--python"],["-python"],Arg.String (fun dir -> + set_platform com Python dir; + ),"","generate Python code into target file"); + ("Target",["--hl"],["-hl"],Arg.String (fun file -> + set_platform com Hl file; + ),"","generate HashLink .hl bytecode or .c code into target file"); + ("Target",[],["-x"], Arg.String (fun cl -> + let cpath = Path.parse_type_path cl in + (match com.main_class with + | Some c -> if cpath <> c then raise (Arg.Bad "Multiple --main classes specified") + | None -> com.main_class <- Some cpath); + actx.classes <- cpath :: actx.classes; + Common.define com Define.Interp; + set_platform com (!Globals.macro_platform) ""; + actx.interp <- true; + ),"","interpret the program using internal macro system"); + ("Target",["--interp"],[], Arg.Unit (fun() -> + Common.define com Define.Interp; + set_platform com (!Globals.macro_platform) ""; + actx.interp <- true; + ),"","interpret the program using internal macro system"); + ("Target",["--run"],[], Arg.Unit (fun() -> + raise (Arg.Bad "--run requires an argument: a Haxe module name") + ), " [args...]","interpret a Haxe module with command line arguments"); + ("Compilation",["-p";"--class-path"],["-cp"],Arg.String (fun path -> + com.class_path <- Path.add_trailing_slash path :: com.class_path + ),"","add a directory to find source files"); + ("Compilation",["-m";"--main"],["-main"],Arg.String (fun cl -> + if com.main_class <> None then raise (Arg.Bad "Multiple --main classes specified"); + let cpath = Path.parse_type_path cl in + com.main_class <- Some cpath; + actx.classes <- cpath :: actx.classes + ),"","select startup class"); + ("Compilation",["-L";"--library"],["-lib"],Arg.String (fun _ -> ()),"","use a haxelib library"); + ("Compilation",["-D";"--define"],[],Arg.String (fun var -> + let flag, value = try let split = ExtString.String.split var "=" in (fst split, Some (snd split)) with _ -> var, None in + match value with + | Some value -> Common.external_define_value com flag value + | None -> Common.external_define com flag; + ),"","define a conditional compilation flag"); + ("Debug",["-v";"--verbose"],[],Arg.Unit (fun () -> + com.verbose <- true + ),"","turn on verbose mode"); + ("Debug",["--debug"],["-debug"], Arg.Unit (fun() -> + Common.define com Define.Debug; + com.debug <- true; + ),"","add debug information to the compiled code"); + ("Miscellaneous",["--version"],["-version"],Arg.Unit (fun() -> + com.info s_version_full null_pos; + actx.did_something <- true; + ),"","print version and exit"); + ("Miscellaneous", ["-h";"--help"], ["-help"], Arg.Unit (fun () -> + raise (Arg.Help "") + ),"","show extended help information"); + ("Miscellaneous",["--help-defines"],[], Arg.Unit (fun() -> + let all,max_length = Define.get_documentation_list com.user_defines in + let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in + List.iter (fun msg -> com.print (msg ^ "\n")) all; + actx.did_something <- true + ),"","print help for all compiler specific defines"); + ("Miscellaneous",["--help-user-defines"],[], Arg.Unit (fun() -> + actx.did_something <- true; + com.callbacks#add_after_init_macros (fun() -> + let all,max_length = Define.get_user_documentation_list com.user_defines in + let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in + List.iter (fun msg -> com.print (msg ^ "\n")) all; + exit 0 + ) + ),"","print help for all user defines"); + ("Miscellaneous",["--help-metas"],[], Arg.Unit (fun() -> + let all,max_length = Meta.get_documentation_list com.user_metas in + let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in + List.iter (fun msg -> com.print (msg ^ "\n")) all; + actx.did_something <- true + ),"","print help for all compiler metadatas"); + ("Miscellaneous",["--help-user-metas"],[], Arg.Unit (fun() -> + actx.did_something <- true; + com.callbacks#add_after_init_macros (fun() -> + let all,max_length = Meta.get_user_documentation_list com.user_metas in + let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in + List.iter (fun msg -> com.print (msg ^ "\n")) all; + exit 0 + ) + ),"","print help for all user metadatas"); + ] in + let adv_args_spec = [ + ("Optimization",["--dce"],["-dce"],Arg.String (fun mode -> + (match mode with + | "std" | "full" | "no" -> () + | _ -> raise (Arg.Bad "Invalid DCE mode, expected std | full | no")); + Common.define_value com Define.Dce mode + ),"[std|full|no]","set the dead code elimination mode (default std)"); + ("Target-specific",["--swf-version"],["-swf-version"],Arg.Float (fun v -> + if not actx.swf_version || com.flash_version < v then com.flash_version <- v; + actx.swf_version <- true; + ),"","change the SWF version"); + ("Target-specific",["--swf-header"],["-swf-header"],Arg.String (fun h -> + add_deprecation "-swf-header has been deprecated, use -D swf-header instead"; + define_value com Define.SwfHeader h + ),"

","define SWF header (width:height:fps:color)"); + ("Target-specific",["--flash-strict"],[],Arg.Unit (fun () -> + add_deprecation "--flash-strict has been deprecated, use -D flash-strict instead"; + Common.define com Define.FlashStrict + ), "","more type strict flash API"); + ("Target-specific",["--swf-lib"],["-swf-lib"],Arg.String (fun file -> + add_native_lib file false; + ),"","add the SWF library to the compiled SWF"); + ("Target-specific",[],["--neko-lib-path"],Arg.String (fun dir -> + com.neko_lib_paths <- dir :: com.neko_lib_paths + ),"","add the neko library path"); + ("Target-specific",["--swf-lib-extern"],["-swf-lib-extern"],Arg.String (fun file -> + add_native_lib file true; + ),"","use the SWF library for type checking"); + ("Target-specific",["--java-lib"],["-java-lib"],Arg.String (fun file -> + add_native_lib file false; + ),"","add an external JAR or directory of JAR files"); + ("Target-specific",["--java-lib-extern"],[],Arg.String (fun file -> + add_native_lib file true; + ),"","use an external JAR or directory of JAR files for type checking"); + ("Target-specific",["--net-lib"],["-net-lib"],Arg.String (fun file -> + add_native_lib file false; + ),"[@std]","add an external .NET DLL file"); + ("Target-specific",["--net-std"],["-net-std"],Arg.String (fun file -> + Dotnet.add_net_std com file + ),"","add a root std .NET DLL search path"); + ("Target-specific",["--c-arg"],["-c-arg"],Arg.String (fun arg -> + com.c_args <- arg :: com.c_args + ),"","pass option to the native Java/C# compiler"); + ("Compilation",["-r";"--resource"],["-resource"],Arg.String (fun res -> + let file, name = (match ExtString.String.nsplit res "@" with + | [file; name] -> file, name + | [file] -> file, file + | _ -> raise (Arg.Bad "Invalid Resource format, expected file@name") + ) in + let file = (try Common.find_file com file with Not_found -> file) in + let data = (try + let s = Std.input_file ~bin:true file in + if String.length s > 12000000 then raise Exit; + s; + with + | Sys_error _ -> failwith ("Resource file not found: " ^ file) + | _ -> failwith ("Resource '" ^ file ^ "' excess the maximum size of 12MB") + ) in + if Hashtbl.mem com.resources name then failwith ("Duplicate resource name " ^ name); + Hashtbl.add com.resources name data + ),"[@name]","add a named resource file"); + ("Debug",["--prompt"],["-prompt"], Arg.Unit (fun() -> Helper.prompt := true),"","prompt on error"); + ("Compilation",["--cmd"],["-cmd"], Arg.String (fun cmd -> + actx.cmds <- Helper.unquote cmd :: actx.cmds + ),"","run the specified command after successful compilation"); + ("Optimization",["--no-traces"],[], Arg.Unit (fun () -> + add_deprecation "--no-traces has been deprecated, use -D no-traces instead"; + Common.define com Define.NoTraces + ), "","don't compile trace calls in the program"); + ("Batch",["--next"],[], Arg.Unit (fun() -> die "" __LOC__), "","separate several haxe compilations"); + ("Batch",["--each"],[], Arg.Unit (fun() -> die "" __LOC__), "","append preceding parameters to all Haxe compilations separated by --next"); + ("Services",["--display"],[], Arg.String (fun input -> + actx.display_arg <- Some input; + ),"","display code tips"); + ("Services",["--xml"],["-xml"],Arg.String (fun file -> + actx.xml_out <- Some file + ),"","generate XML types description"); + ("Services",["--json"],[],Arg.String (fun file -> + actx.json_out <- Some file + ),"","generate JSON types description"); + ("Optimization",["--no-output"],[], Arg.Unit (fun() -> actx.no_output <- true),"","compiles but does not generate any file"); + ("Debug",["--times"],[], Arg.Unit (fun() -> Timer.measure_times := true),"","measure compilation times"); + ("Optimization",["--no-inline"],[],Arg.Unit (fun () -> + add_deprecation "--no-inline has been deprecated, use -D no-inline instead"; + Common.define com Define.NoInline + ), "","disable inlining"); + ("Optimization",["--no-opt"],[], Arg.Unit (fun() -> + com.foptimize <- false; + Common.define com Define.NoOpt; + ), "","disable code optimizations"); + ("Compilation",["--remap"],[], Arg.String (fun s -> + let pack, target = (try ExtString.String.split s ":" with _ -> raise (Arg.Bad "Invalid remap format, expected source:target")) in + com.package_rules <- PMap.add pack (Remap target) com.package_rules; + ),"","remap a package to another one"); + ("Compilation",["--macro"],[], Arg.String (fun e -> + actx.force_typing <- true; + actx.config_macros <- e :: actx.config_macros + ),"","call the given macro before typing anything else"); + ("Compilation Server",["--server-listen"],["--wait"], Arg.String (fun hp -> + die "" __LOC__ + ),"[[host:]port]|stdio]","wait on the given port (or use standard i/o) for commands to run"); + ("Compilation Server",["--server-connect"],[], Arg.String (fun hp -> + die "" __LOC__ + ),"[host:]port]","connect to the given port and wait for commands to run"); + ("Compilation Server",["--connect"],[],Arg.String (fun _ -> + die "" __LOC__ + ),"<[host:]port>","connect on the given port and run commands there"); + ("Compilation",["-C";"--cwd"],[], Arg.String (fun dir -> + (try Unix.chdir dir with _ -> raise (Arg.Bad ("Invalid directory: " ^ dir))); + actx.did_something <- true; + ),"","set current working directory"); + ("Compilation",["--haxelib-global"],[], Arg.Unit (fun () -> ()),"","pass --global argument to haxelib"); + ("Compilation",["-w"],[], Arg.String (fun s -> + let p = { pfile = "-w " ^ s; pmin = 0; pmax = 0 } in + let l = Warning.parse_options s p in + com.warning_options <- l :: com.warning_options + ),"","enable or disable specific warnings"); + ] in + let args_callback cl = + begin try + let path,name = Path.parse_path cl in + if StringHelper.starts_uppercase_identifier name then + actx.classes <- (path,name) :: actx.classes + else begin + actx.force_typing <- true; + actx.config_macros <- (Printf.sprintf "include('%s', true, null, null, true)" cl) :: actx.config_macros; + end + with Failure _ when ignore_error com -> + () + end + in + let all_args = (basic_args_spec @ adv_args_spec) in + let all_args_spec = process_args all_args in + let process args = + let current = ref 0 in + (try + let rec loop acc args = match args with + | "--display" :: arg :: args -> + loop (arg :: "--display" :: acc) args + | arg :: args -> + loop (Helper.expand_env arg :: acc) args + | [] -> + List.rev acc + in + let args = loop [] args in + Arg.parse_argv ~current (Array.of_list ("" :: args)) all_args_spec args_callback ""; + with + | Arg.Help _ -> + raise (Helper.HelpMessage (usage_string all_args usage)) + | Arg.Bad msg -> + let first_line = List.nth (Str.split (Str.regexp "\n") msg) 0 in + let new_msg = (Printf.sprintf "%s" first_line) in + let r = Str.regexp "unknown option [`']?\\([-A-Za-z]+\\)[`']?" in + try + ignore(Str.search_forward r msg 0); + let s = Str.matched_group 1 msg in + let sl = List.map (fun (s,_,_) -> s) all_args_spec in + let sl = StringError.get_similar s sl in + begin match sl with + | [] -> raise Not_found + | _ -> + let spec = List.filter (fun (_,sl',sl'',_,_,_) -> + List.exists (fun s -> List.mem s sl) (sl' @ sl'') + ) all_args in + let new_msg = (Printf.sprintf "%s\nDid you mean:\n%s" first_line (usage_string ~print_cat:false spec "")) in + raise (Arg.Bad new_msg) + end; + with Not_found -> + raise (Arg.Bad new_msg)); + if com.platform = Globals.Cpp && not (Define.defined com.defines DisableUnicodeStrings) && not (Define.defined com.defines HxcppSmartStings) then begin + Define.define com.defines HxcppSmartStings; + end; + if Define.raw_defined com.defines "gen_hx_classes" then begin + (* TODO: this is something we're gonna remove once we have something nicer for generating flash externs *) + actx.force_typing <- true; + actx.pre_compilation <- (fun() -> + let process_lib lib = + if not (lib#has_flag NativeLibraries.FlagIsStd) then + List.iter (fun path -> if path <> (["java";"lang"],"String") then actx.classes <- path :: actx.classes) lib#list_modules + in + List.iter process_lib com.native_libs.net_libs; + List.iter process_lib com.native_libs.swf_libs; + List.iter process_lib com.native_libs.java_libs; + ) :: actx.pre_compilation; + actx.xml_out <- Some "hx" + end; + in + actx.raise_usage <- (fun () -> raise (Helper.HelpMessage (usage_string basic_args_spec usage))); + (* Handle CLI arguments *) + process com.args; + actx \ No newline at end of file diff --git a/src/context/compilationServer.ml b/src/compiler/compilationCache.ml similarity index 95% rename from src/context/compilationServer.ml rename to src/compiler/compilationCache.ml index b9df0d71..77ca6b1f 100644 --- a/src/context/compilationServer.ml +++ b/src/compiler/compilationCache.ml @@ -54,6 +54,9 @@ class context_cache (index : int) = object(self) method find_module path = Hashtbl.find modules path + method find_module_opt path = + Hashtbl.find_opt modules path + method cache_module path value = Hashtbl.replace modules path value @@ -91,6 +94,13 @@ class virtual server_task (id : string list) (priority : int) = object(self) method get_id = id end +class arbitrary_task (id : string list) (priority : int) (f : unit -> unit) = object(self) + inherit server_task id priority + + method private execute = + f () +end + class cache = object(self) val contexts : (string,context_cache) Hashtbl.t = Hashtbl.create 0 val mutable context_list = [] @@ -160,10 +170,10 @@ class cache = object(self) ) cc#get_modules acc ) contexts [] - method taint_modules file_key = + method taint_modules file_key reason = Hashtbl.iter (fun _ cc -> Hashtbl.iter (fun _ m -> - if Path.UniqueKey.lazy_key m.m_extra.m_file = file_key then m.m_extra.m_dirty <- Some m.m_path + if Path.UniqueKey.lazy_key m.m_extra.m_file = file_key then m.m_extra.m_cache_state <- MSBad (Tainted reason) ) cc#get_modules ) contexts @@ -258,21 +268,6 @@ type context_options = | MacroContext | NormalAndMacroContext -let instance : t option ref = ref None - -let create () = - let cs = new cache in - instance := Some cs; - cs - -let get () = - !instance - -let runs () = - !instance <> None - -let force () = match !instance with None -> die "" __LOC__ | Some i -> i - let get_module_name_of_cfile file cfile = match cfile.c_module_name with | None -> let name = Path.module_name_of_file file in diff --git a/src/compiler/compilationContext.ml b/src/compiler/compilationContext.ml new file mode 100644 index 00000000..edf949b8 --- /dev/null +++ b/src/compiler/compilationContext.ml @@ -0,0 +1,75 @@ +open Globals + +exception Abort + +type server_mode = + | SMNone + | SMListen of string + | SMConnect of string + +type arg_context = { + mutable classes : Globals.path list; + mutable xml_out : string option; + mutable json_out : string option; + mutable cmds : string list; + mutable config_macros : string list; + mutable no_output : bool; + mutable did_something : bool; + mutable force_typing : bool; + mutable pre_compilation : (unit -> unit) list; + mutable interp : bool; + mutable jvm_flag : bool; + mutable swf_version : bool; + mutable native_libs : (string * bool) list; + mutable raise_usage : unit -> unit; + mutable display_arg : string option; + mutable deprecations : string list; +} + +type communication = { + write_out : string -> unit; + write_err : string -> unit; + flush : compilation_context -> unit; + exit : int -> unit; + is_server : bool; +} + +and compilation_context = { + com : Common.context; + mutable messages : compiler_message list; + mutable has_next : bool; + mutable has_error : bool; + comm : communication; +} + +type compilation_callbacks = { + before_anything : compilation_context -> unit; + after_arg_parsing : compilation_context -> unit; + after_compilation : compilation_context -> unit; +} + +type server_accept = unit -> (bool * (bool -> string option) * (string -> unit) * (unit -> unit)) + +type server_api = { + cache : CompilationCache.t; + callbacks : compilation_callbacks; + on_context_create : unit -> int; + init_wait_socket : string -> int -> server_accept; + init_wait_connect : string -> int -> server_accept; + init_wait_stdio : unit -> server_accept; + wait_loop : bool -> server_accept -> int; + do_connect : string -> int -> string list -> unit; +} + +let message ctx msg = + ctx.messages <- msg :: ctx.messages + +let error ctx ?(depth=0) msg p = + message ctx (make_compiler_message msg p depth DKCompilerMessage Error); + ctx.has_error <- true + +let located_error ctx ?(depth=0) msg = match (extract_located msg) with + | [] -> () + | (msg,p) :: tl -> + error ~depth ctx msg p; + List.iter (fun (msg,p) -> error ~depth:(depth+1) ctx msg p) tl diff --git a/src/compiler/compiler.ml b/src/compiler/compiler.ml new file mode 100644 index 00000000..efcea9b9 --- /dev/null +++ b/src/compiler/compiler.ml @@ -0,0 +1,626 @@ +open Globals +open Common +open CompilationContext + +let run_or_diagnose ctx f arg = + let com = ctx.com in + let handle_diagnostics ?(depth = 0) msg kind = + ctx.has_error <- true; + add_diagnostics_message ~depth com msg kind Error; + DisplayOutput.emit_diagnostics ctx.com + in + if is_diagnostics com then begin try + f arg + with + | Error.Error(msg,p,depth) -> + handle_diagnostics ~depth (Error.error_msg p msg) DKCompilerMessage + | Parser.Error(msg,p) -> + handle_diagnostics (located (Parser.error_msg msg) p) DKParserError + | Lexer.Error(msg,p) -> + handle_diagnostics (located (Lexer.error_msg msg) p) DKParserError + end + else + f arg + +let run_command ctx cmd = + let t = Timer.timer ["command";cmd] in + (* TODO: this is a hack *) + let cmd = if ctx.comm.is_server then begin + let h = Hashtbl.create 0 in + Hashtbl.add h "__file__" ctx.com.file; + Hashtbl.add h "__platform__" (platform_name ctx.com.platform); + Helper.expand_env ~h:(Some h) cmd + end else + cmd + in + let len = String.length cmd in + let result = + if len > 3 && String.sub cmd 0 3 = "cd " then begin + Sys.chdir (String.sub cmd 3 (len - 3)); + 0 + (* Emit stderr as a server message in server mode *) + end else begin + let pout, pin, perr = Unix.open_process_full cmd (Unix.environment()) in + let bout = Bytes.create 1024 in + let berr = Bytes.create 1024 in + let rec read_content channel buf f = + begin try + let i = input channel buf 0 1024 in + if i > 0 then begin + f (Bytes.unsafe_to_string (Bytes.sub buf 0 i)); + read_content channel buf f + end + with Unix.Unix_error _ -> + () + end + in + let tout = Thread.create (fun() -> read_content pout bout ctx.comm.write_out) () in + let terr = Thread.create (fun() -> read_content perr berr ctx.comm.write_err) () in + Thread.join tout; + Thread.join terr; + let result = (match Unix.close_process_full (pout,pin,perr) with Unix.WEXITED c | Unix.WSIGNALED c | Unix.WSTOPPED c -> c) in + result + end + in + t(); + result + +module Setup = struct + let initialize_target ctx com actx = + let add_std dir = + com.class_path <- List.filter (fun s -> not (List.mem s com.std_path)) com.class_path @ List.map (fun p -> p ^ dir ^ "/_std/") com.std_path @ com.std_path + in + match com.platform with + | Cross -> + (* no platform selected *) + set_platform com Cross ""; + "?" + | Flash -> + let rec loop = function + | [] -> () + | (v,_) :: _ when v > com.flash_version -> () + | (v,def) :: l -> + Common.raw_define com ("flash" ^ def); + loop l + in + loop Common.flash_versions; + com.package_rules <- PMap.remove "flash" com.package_rules; + add_std "flash"; + "swf" + | Neko -> + add_std "neko"; + "n" + | Js -> + let es_version = + try + int_of_string (Common.defined_value com Define.JsEs) + with + | Not_found -> + (Common.define_value com Define.JsEs "5"; 5) + | _ -> + 0 + in + + if es_version < 3 || es_version = 4 then (* we don't support ancient and there's no 4th *) + failwith "Invalid -D js-es value"; + + if es_version >= 5 then Common.raw_define com "js_es5"; (* backward-compatibility *) + + add_std "js"; + "js" + | Lua -> + add_std "lua"; + "lua" + | Php -> + add_std "php"; + "php" + | Cpp -> + Common.define_value com Define.HxcppApiLevel "430"; + add_std "cpp"; + if Common.defined com Define.Cppia then + actx.classes <- (Path.parse_path "cpp.cppia.HostClasses" ) :: actx.classes; + "cpp" + | Cs -> + Dotnet.before_generate com; + add_std "cs"; "cs" + | Java -> + Java.before_generate com; + if defined com Define.Jvm then begin + add_std "jvm"; + com.package_rules <- PMap.remove "jvm" com.package_rules; + end; + add_std "java"; + "java" + | Python -> + add_std "python"; + if not (Common.defined com Define.PythonVersion) then + Common.define_value com Define.PythonVersion "3.3"; + "python" + | Hl -> + add_std "hl"; + if not (Common.defined com Define.HlVer) then Define.define_value com.defines Define.HlVer (try Std.input_file (Common.find_file com "hl/hl_version") with Not_found -> die "" __LOC__); + "hl" + | Eval -> + add_std "eval"; + "eval" + + let create_typer_context ctx native_libs = + let com = ctx.com in + Common.log com ("Classpath: " ^ (String.concat ";" com.class_path)); + let buffer = Buffer.create 64 in + Buffer.add_string buffer "Defines: "; + PMap.iter (fun k v -> match v with + | "1" -> Printf.bprintf buffer "%s;" k + | _ -> Printf.bprintf buffer "%s=%s;" k v + ) com.defines.values; + Buffer.truncate buffer (Buffer.length buffer - 1); + Common.log com (Buffer.contents buffer); + Typecore.type_expr_ref := (fun ?(mode=MGet) ctx e with_type -> Typer.type_expr ~mode ctx e with_type); + com.callbacks#run com.callbacks#get_before_typer_create; + (* Native lib pass 1: Register *) + let fl = List.map (fun (file,extern) -> NativeLibraryHandler.add_native_lib com file extern) (List.rev native_libs) in + (* Native lib pass 2: Initialize *) + List.iter (fun f -> f()) fl; + Typer.create com + + let executable_path() = + Extc.executable_path() + + let get_std_class_paths () = + try + let p = Sys.getenv "HAXE_STD_PATH" in + let rec loop = function + | drive :: path :: l -> + if String.length drive = 1 && ((drive.[0] >= 'a' && drive.[0] <= 'z') || (drive.[0] >= 'A' && drive.[0] <= 'Z')) then + (drive ^ ":" ^ path) :: loop l + else + drive :: loop (path :: l) + | l -> + l + in + let parts = Str.split_delim (Str.regexp "[;:]") p in + "" :: List.map Path.add_trailing_slash (loop parts) + with Not_found -> + let base_path = Path.get_real_path (try executable_path() with _ -> "./") in + if Sys.os_type = "Unix" then + let prefix_path = Filename.dirname base_path in + let lib_path = Filename.concat prefix_path "lib" in + let share_path = Filename.concat prefix_path "share" in + [ + ""; + Path.add_trailing_slash (Filename.concat lib_path "haxe/std"); + Path.add_trailing_slash (Filename.concat lib_path "haxe/extraLibs"); + Path.add_trailing_slash (Filename.concat share_path "haxe/std"); + Path.add_trailing_slash (Filename.concat share_path "haxe/extraLibs"); + Path.add_trailing_slash (Filename.concat base_path "std"); + Path.add_trailing_slash (Filename.concat base_path "extraLibs") + ] + else + [ + ""; + Path.add_trailing_slash (Filename.concat base_path "std"); + Path.add_trailing_slash (Filename.concat base_path "extraLibs") + ] + + let setup_common_context ctx = + let com = ctx.com in + ctx.com.print <- ctx.comm.write_out; + Common.define_value com Define.HaxeVer (Printf.sprintf "%.3f" (float_of_int Globals.version /. 1000.)); + Common.raw_define com "haxe3"; + Common.raw_define com "haxe4"; + Common.define_value com Define.Haxe s_version; + Common.raw_define com "true"; + Common.define_value com Define.Dce "std"; + com.info <- (fun ?(depth=0) msg p -> message ctx (make_compiler_message msg p depth DKCompilerMessage Information)); + com.warning <- (fun ?(depth=0) w options msg p -> + match Warning.get_mode w (com.warning_options @ options) with + | WMEnable -> + let wobj = Warning.warning_obj w in + let msg = if wobj.w_generic then + msg + else + Printf.sprintf "(%s) %s" wobj.w_name msg + in + message ctx (make_compiler_message msg p depth DKCompilerMessage Warning) + | WMDisable -> + () + ); + com.located_error <- located_error ctx; + com.error <- (fun ?(depth = 0) msg p -> com.located_error ~depth (located msg p)); + let filter_messages = (fun keep_errors predicate -> (List.filter (fun cm -> + (match cm.cm_severity with + | MessageSeverity.Error -> keep_errors; + | Information | Warning | Hint -> predicate cm;) + ) (List.rev ctx.messages))) in + com.get_messages <- (fun () -> (List.map (fun cm -> + (match cm.cm_severity with + | MessageSeverity.Error -> die "" __LOC__; + | Information | Warning | Hint -> cm;) + ) (filter_messages false (fun _ -> true)))); + com.filter_messages <- (fun predicate -> (ctx.messages <- (List.rev (filter_messages true predicate)))); + com.run_command <- run_command ctx; + com.class_path <- get_std_class_paths (); + com.std_path <- List.filter (fun p -> ExtString.String.ends_with p "std/" || ExtString.String.ends_with p "std\\") com.class_path + +end + +(** Creates the typer context and types [classes] into it. *) +let do_type ctx tctx actx = + let com = tctx.Typecore.com in + let t = Timer.timer ["typing"] in + let cs = com.cs in + CommonCache.maybe_add_context_sign cs com "before_init_macros"; + com.stage <- CInitMacrosStart; + List.iter (MacroContext.call_init_macro tctx) (List.rev actx.config_macros); + com.stage <- CInitMacrosDone; + CommonCache.lock_signature com "after_init_macros"; + com.callbacks#run com.callbacks#get_after_init_macros; + run_or_diagnose ctx (fun () -> + if com.display.dms_kind <> DMNone then DisplayTexpr.check_display_file tctx cs; + List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev actx.classes); + Finalization.finalize tctx; + ) (); + com.stage <- CTypingDone; + (* If we are trying to find references, let's syntax-explore everything we know to check for the + identifier we are interested in. We then type only those modules that contain the identifier. *) + begin match com.display.dms_kind with + | (DMUsage _ | DMImplementation) -> FindReferences.find_possible_references tctx cs; + | _ -> () + end; + t() + +let finalize_typing ctx tctx = + let t = Timer.timer ["finalize"] in + let com = ctx.com in + com.stage <- CFilteringStart; + let main, types, modules = run_or_diagnose ctx Finalization.generate tctx in + com.main <- main; + com.types <- types; + com.modules <- modules; + t() + +let filter ctx tctx = + let t = Timer.timer ["filters"] in + DeprecationCheck.run ctx.com; + Filters.run ctx.com tctx ctx.com.main; + t() + +let compile ctx actx = + let com = ctx.com in + (* Set up display configuration *) + DisplayProcessing.process_display_configuration ctx; + let display_file_dot_path = DisplayProcessing.process_display_file com actx in + (* Initialize target: This allows access to the appropriate std packages and sets the -D defines. *) + let ext = Setup.initialize_target ctx com actx in + com.config <- get_config com; (* make sure to adapt all flags changes defined after platform *) + let t = Timer.timer ["init"] in + List.iter (fun f -> f()) (List.rev (actx.pre_compilation)); + t(); + com.stage <- CInitialized; + if actx.classes = [([],"Std")] && not actx.force_typing then begin + if actx.cmds = [] && not actx.did_something then actx.raise_usage(); + end else begin + (* Actual compilation starts here *) + let tctx = Setup.create_typer_context ctx actx.native_libs in + com.stage <- CTyperCreated; + let display_file_dot_path = DisplayProcessing.maybe_load_display_file_before_typing tctx display_file_dot_path in + begin try + do_type ctx tctx actx + with TypeloadParse.DisplayInMacroBlock -> + ignore(DisplayProcessing.load_display_module_in_macro tctx display_file_dot_path true); + end; + DisplayProcessing.handle_display_after_typing ctx tctx display_file_dot_path; + finalize_typing ctx tctx; + DisplayProcessing.handle_display_after_finalization ctx tctx display_file_dot_path; + filter ctx tctx; + if ctx.has_error then raise Abort; + Generate.check_auxiliary_output com actx; + com.stage <- CGenerationStart; + if not actx.no_output then Generate.generate ctx tctx ext actx; + com.stage <- CGenerationDone; + end; + Sys.catch_break false; + com.callbacks#run com.callbacks#get_after_generation; + if not actx.no_output then begin + List.iter (fun c -> + let r = run_command ctx c in + if r <> 0 then failwith ("Command failed with error " ^ string_of_int r) + ) (List.rev actx.cmds) + end + +let compile_safe ctx f = + let com = ctx.com in +try + f () +with + | Abort -> + () + | Error.Fatal_error (m,depth) -> + located_error ~depth ctx m + | Common.Abort msg -> + located_error ctx msg + | Lexer.Error (m,p) -> + error ctx (Lexer.error_msg m) p + | Parser.Error (m,p) -> + error ctx (Parser.error_msg m) p + | Typecore.Forbid_package ((pack,m,p),pl,pf) -> + if !Parser.display_mode <> DMNone && ctx.has_next then begin + ctx.has_error <- false; + ctx.messages <- []; + end else begin + error ctx (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (s_type_path m) ) p; + List.iter (error ~depth:1 ctx (Error.compl_msg "referenced here")) (List.rev pl); + end + | Error.Error (Stack stack,_,depth) -> (match stack with + | [] -> () + | (e,p) :: stack -> begin + located_error ~depth ctx (Error.error_msg p e); + List.iter (fun (e,p) -> located_error ~depth:(depth+1) ctx (Error.error_msg p e)) stack; + end) + | Error.Error (m,p,depth) -> + located_error ~depth ctx (Error.error_msg p m) + | Generic.Generic_Exception(m,p) -> + error ctx m p + | Arg.Bad msg -> + error ctx ("Error: " ^ msg) null_pos + | Failure msg when not Helper.is_debug_run -> + error ctx ("Error: " ^ msg) null_pos + | Helper.HelpMessage msg -> + com.info msg null_pos + | Parser.TypePath (p,c,is_import,pos) -> + DisplayOutput.handle_type_path_exception ctx p c is_import pos + | Parser.SyntaxCompletion(kind,subj) -> + DisplayOutput.handle_syntax_completion com kind subj; + error ctx ("Error: No completion point was found") null_pos + | DisplayException.DisplayException dex -> + DisplayOutput.handle_display_exception ctx dex + | Out_of_memory | EvalExceptions.Sys_exit _ | Hlinterp.Sys_exit _ | DisplayProcessingGlobals.Completion _ as exc -> + (* We don't want these to be caught by the catchall below *) + raise exc + | e when (try Sys.getenv "OCAMLRUNPARAM" <> "b" with _ -> true) && not Helper.is_debug_run -> + error ctx (Printexc.to_string e) null_pos + +let finalize ctx = + ctx.comm.flush ctx; + (* In server mode any open libs are closed by the lib_build_task. In offline mode + we should do it here to be safe. *) + if not ctx.comm.is_server then begin + List.iter (fun lib -> lib#close) ctx.com.native_libs.java_libs; + List.iter (fun lib -> lib#close) ctx.com.native_libs.net_libs; + List.iter (fun lib -> lib#close) ctx.com.native_libs.swf_libs; + end + +let catch_completion_and_exit ctx callbacks run = + try + run ctx; + if ctx.has_error then 1 else 0 + with + | DisplayProcessingGlobals.Completion str -> + callbacks.after_compilation ctx; + ServerMessage.completion str; + ctx.comm.write_err str; + 0 + | EvalExceptions.Sys_exit i | Hlinterp.Sys_exit i -> + if i <> 0 then ctx.has_error <- true; + finalize ctx; + i + +let process_actx ctx actx = + DisplayProcessing.process_display_arg ctx actx; + List.iter (fun s -> + ctx.com.warning WDeprecated [] s null_pos + ) actx.deprecations + +let compile_ctx callbacks ctx = + let run ctx = + callbacks.before_anything ctx; + Setup.setup_common_context ctx; + compile_safe ctx (fun () -> + let actx = Args.parse_args ctx.com in + process_actx ctx actx; + callbacks.after_arg_parsing ctx; + compile ctx actx; + ); + finalize ctx; + callbacks.after_compilation ctx; + in + if ctx.has_error then begin + finalize ctx; + 1 (* can happen if process_params fails already *) + end else + catch_completion_and_exit ctx callbacks run + +let create_context comm cs compilation_step params = { + com = Common.create compilation_step cs version params; + messages = []; + has_next = false; + has_error = false; + comm = comm; +} + +module HighLevel = struct + let add_libs libs args cs has_display = + let global_repo = List.exists (fun a -> a = "--haxelib-global") args in + let fail msg = + raise (Arg.Bad msg) + in + let call_haxelib() = + let t = Timer.timer ["haxelib"] in + let cmd = "haxelib" ^ (if global_repo then " --global" else "") ^ " path " ^ String.concat " " libs in + let pin, pout, perr = Unix.open_process_full cmd (Unix.environment()) in + let lines = Std.input_list pin in + let err = Std.input_list perr in + let ret = Unix.close_process_full (pin,pout,perr) in + if ret <> Unix.WEXITED 0 then fail (match lines, err with + | [], [] -> "Failed to call haxelib (command not found ?)" + | [], [s] when ExtString.String.ends_with (ExtString.String.strip s) "Module not found: path" -> "The haxelib command has been strip'ed, please install it again" + | _ -> String.concat "\n" (lines@err)); + t(); + lines + in + match libs with + | [] -> + [] + | _ -> + let lines = + try + (* if we are compiling, really call haxelib since library path might have changed *) + if not has_display then raise Not_found; + cs#find_haxelib libs + with Not_found -> try + let lines = call_haxelib() in + cs#cache_haxelib libs lines; + lines + with Unix.Unix_error(code,msg,arg) -> + fail ((Printf.sprintf "%s (%s)" (Unix.error_message code) arg)) + in + let lines = List.fold_left (fun acc l -> + let l = ExtString.String.strip l in + if l = "" then + acc + else if l.[0] <> '-' then + "-cp" :: l :: acc + else match (try ExtString.String.split l " " with _ -> l, "") with + | ("-L",dir) -> + "--neko-lib-path" :: (String.sub l 3 (String.length l - 3)) :: acc + | param, value -> + let acc = if value <> "" then value :: acc else acc in + let acc = param :: acc in + acc + ) [] (List.rev lines) in + lines + + (* Returns a list of contexts, but doesn't do anything yet *) + let process_params server_api create each_params has_display is_server pl = + let curdir = Unix.getcwd () in + let added_libs = Hashtbl.create 0 in + let server_mode = ref SMNone in + let create_context args = + let ctx = create (server_api.on_context_create()) args in + (* --cwd triggers immediately, so let's reset *) + Unix.chdir curdir; + ctx + in + let rec find_subsequent_libs acc args = match args with + | ("-L" | "--library" | "-lib") :: name :: args -> + find_subsequent_libs (name :: acc) args + | _ -> + List.rev acc,args + in + let rec loop acc = function + | [] -> + [],Some (create_context (!each_params @ (List.rev acc))) + | "--next" :: l when acc = [] -> (* skip empty --next *) + loop [] l + | "--next" :: l -> + let ctx = create_context (!each_params @ (List.rev acc)) in + ctx.has_next <- true; + l,Some ctx + | "--each" :: l -> + each_params := List.rev acc; + loop [] l + | "--cwd" :: dir :: l | "-C" :: dir :: l -> + (* we need to change it immediately since it will affect hxml loading *) + (try Unix.chdir dir with _ -> raise (Arg.Bad ("Invalid directory: " ^ dir))); + (* Push the --cwd arg so the arg processor know we did something. *) + loop (dir :: "--cwd" :: acc) l + | "--connect" :: hp :: l -> + if is_server then + (* If we are already connected, ignore (issue #10813) *) + loop acc l + else begin + let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in + server_api.do_connect host (try int_of_string port with _ -> raise (Arg.Bad "Invalid port")) ((List.rev acc) @ l); + [],None + end + | "--server-connect" :: hp :: l -> + server_mode := SMConnect hp; + loop acc l + | ("--server-listen" | "--wait") :: hp :: l -> + server_mode := SMListen hp; + loop acc l + | "--run" :: cl :: args -> + let acc = cl :: "-x" :: acc in + let ctx = create_context (!each_params @ (List.rev acc)) in + ctx.com.sys_args <- args; + [],Some ctx + | ("-L" | "--library" | "-lib") :: name :: args -> + let libs,args = find_subsequent_libs [name] args in + let libs = List.filter (fun l -> not (Hashtbl.mem added_libs l)) libs in + List.iter (fun l -> Hashtbl.add added_libs l ()) libs; + let lines = add_libs libs pl server_api.cache has_display in + loop acc (lines @ args) + | ("--jvm" | "--java" | "-java" as arg) :: dir :: args -> + loop_lib arg dir "hxjava" acc args + | ("--cs" | "-cs" as arg) :: dir :: args -> + loop_lib arg dir "hxcs" acc args + | arg :: l -> + match List.rev (ExtString.String.nsplit arg ".") with + | "hxml" :: _ :: _ when (match acc with "-cmd" :: _ | "--cmd" :: _ -> false | _ -> true) -> + let acc, l = (try acc, Helper.parse_hxml arg @ l with Not_found -> (arg ^ " (file not found)") :: acc, l) in + loop acc l + | _ -> + loop (arg :: acc) l + and loop_lib arg dir lib acc args = + loop (dir :: arg :: acc) ("-lib" :: lib :: args) + in + let args,ctx = loop [] pl in + args,!server_mode,ctx + + let execute_ctx server_api ctx server_mode = + begin match server_mode with + | SMListen hp -> + (* parse for com.verbose *) + ignore(Args.parse_args ctx.com); + let accept = match hp with + | "stdio" -> + server_api.init_wait_stdio() + | _ -> + let host, port = Helper.parse_host_port hp in + server_api.init_wait_socket host port + in + server_api.wait_loop ctx.com.verbose accept + | SMConnect hp -> + ignore(Args.parse_args ctx.com); + let host, port = Helper.parse_host_port hp in + let accept = server_api.init_wait_connect host port in + server_api.wait_loop ctx.com.verbose accept + | SMNone -> + compile_ctx server_api.callbacks ctx + end + + let entry server_api comm args = + let create = create_context comm server_api.cache in + let each_args = ref [] in + let has_display = ref false in + (* put --display in front if it was last parameter *) + let args = match List.rev args with + | file :: "--display" :: pl when file <> "memory" -> + has_display := true; + "--display" :: file :: List.rev pl + | _ -> + args + in + let rec loop args = + let args,server_mode,ctx = try + process_params server_api create each_args !has_display comm.is_server args + with Arg.Bad msg -> + let ctx = create 0 args in + error ctx ("Error: " ^ msg) null_pos; + [],SMNone,Some ctx + in + let code = match ctx with + | Some ctx -> + execute_ctx server_api ctx server_mode + | None -> + (* caused by --connect *) + 0 + in + if code = 0 && args <> [] && not !has_display then + loop args + else + code + in + let code = loop args in + comm.exit code +end diff --git a/src/compiler/displayOutput.ml b/src/compiler/displayOutput.ml index 8d9f66b5..89951d65 100644 --- a/src/compiler/displayOutput.ml +++ b/src/compiler/displayOutput.ml @@ -2,7 +2,6 @@ open Globals open Ast open Common open Filename -open CompilationServer open Timer open DisplayTypes.DisplayMode open DisplayTypes.CompletionResultKind @@ -17,6 +16,8 @@ open DisplayTypes open CompletionModuleType open Typecore open Genjson +open CompilationContext +open DisplayProcessingGlobals (* Old XML stuff *) @@ -70,7 +71,7 @@ let print_fields fields = | ITPackage(path,_) -> "package",snd path,"",None | ITModule path -> "type",snd path,"",None | ITMetadata meta -> - let s,(doc,_) = Meta.get_info meta in + let s,(doc,_),_ = Meta.get_info meta in "metadata","@" ^ s,"",doc_from_string doc | ITTimer(name,value) -> "timer",name,"",doc_from_string value | ITLiteral s -> @@ -208,227 +209,6 @@ let print_signature tl display_arg = (* Mode processing *) -exception Completion of string - -let unquote v = - let len = String.length v in - if len > 0 then - match v.[0], v.[len - 1] with - | '"', '"' - | '\'', '\'' -> String.sub v 1 (len - 2) - | _ -> v - else v - -let handle_display_argument com file_pos pre_compilation did_something = - match file_pos with - | "classes" -> - pre_compilation := (fun() -> raise (Parser.TypePath (["."],None,true,null_pos))) :: !pre_compilation; - | "keywords" -> - raise (Completion (print_keywords ())) - | "memory" -> - did_something := true; - (try Memory.display_memory com with e -> prerr_endline (Printexc.get_backtrace ())); - | "diagnostics" -> - com.display <- DisplayMode.create (DMDiagnostics []); - Parser.display_mode := DMDiagnostics []; - | _ -> - let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format: " ^ file_pos) in - let file = unquote file in - let file_unique = com.file_keys#get file in - let pos, smode = try ExtString.String.split pos "@" with _ -> pos,"" in - let mode = match smode with - | "position" -> - DMDefinition - | "usage" -> - DMUsage (false,false,false) - (*| "rename" -> - DMUsage true*) - | "package" -> - DMPackage - | "type" -> - DMHover - | "toplevel" -> - DMDefault - | "module-symbols" -> - DMModuleSymbols None; - | "diagnostics" -> - DMDiagnostics [file_unique]; - | "statistics" -> - DMStatistics - | "signature" -> - DMSignature - | "" -> - DMDefault - | _ -> - let smode,arg = try ExtString.String.split smode "@" with _ -> pos,"" in - match smode with - | "resolve" -> - DMResolve arg - | "workspace-symbols" -> - DMModuleSymbols (Some arg) - | _ -> - DMDefault - in - let pos = try int_of_string pos with _ -> failwith ("Invalid format: " ^ pos) in - com.display <- DisplayMode.create mode; - Parser.display_mode := mode; - if not com.display.dms_full_typing then Common.define_value com Define.Display (if smode <> "" then smode else "1"); - DisplayPosition.display_position#set { - pfile = Path.get_full_path file; - pmin = pos; - pmax = pos; - } - -let file_input_marker = Path.get_full_path "? input" - -type display_path_kind = - | DPKNormal of path - | DPKMacro of path - | DPKDirect of string - | DPKInput of string - | DPKNone - -let process_display_file com classes = - let get_module_path_from_file_path com spath = - let rec loop = function - | [] -> None - | cp :: l -> - let cp = (if cp = "" then "./" else cp) in - let c = Path.add_trailing_slash (Path.get_real_path cp) in - let clen = String.length c in - if clen < String.length spath && String.sub spath 0 clen = c then begin - let path = String.sub spath clen (String.length spath - clen) in - (try - let path = Path.parse_path path in - (match loop l with - | Some x as r when String.length (s_type_path x) < String.length (s_type_path path) -> r - | _ -> Some path) - with _ -> loop l) - end else - loop l - in - loop com.class_path - in - match com.display.dms_display_file_policy with - | DFPNo -> - DPKNone - | DFPOnly when (DisplayPosition.display_position#get).pfile = file_input_marker -> - classes := []; - com.main_class <- None; - begin match !TypeloadParse.current_stdin with - | Some input -> - TypeloadParse.current_stdin := None; - DPKInput input - | None -> - DPKNone - end - | dfp -> - if dfp = DFPOnly then begin - classes := []; - com.main_class <- None; - end; - let real = Path.get_real_path (DisplayPosition.display_position#get).pfile in - let path = match get_module_path_from_file_path com real with - | Some path -> - if com.display.dms_kind = DMPackage then raise_package (fst path); - let path = match ExtString.String.nsplit (snd path) "." with - | [name;"macro"] -> - (* If we have a .macro.hx path, don't add the file to classes because the compiler won't find it. - This can happen if we're completing in such a file. *) - DPKMacro (fst path,name) - | [name] -> - classes := path :: !classes; - DPKNormal path - | [name;target] -> - let path = fst path, name in - classes := path :: !classes; - DPKNormal path - | e -> - die "" __LOC__ - in - path - | None -> - if not (Sys.file_exists real) then failwith "Display file does not exist"; - (match List.rev (ExtString.String.nsplit real Path.path_sep) with - | file :: _ when file.[0] >= 'a' && file.[0] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter") - | _ -> ()); - DPKDirect real - in - Common.log com ("Display file : " ^ real); - Common.log com ("Classes found : [" ^ (String.concat "," (List.map s_type_path !classes)) ^ "]"); - path - -let load_display_file_standalone ctx file = - let com = ctx.com in - let pack,decls = TypeloadParse.parse_module_file com file null_pos in - let path = Path.FilePath.parse file in - let name = match path.file_name with - | None -> "?DISPLAY" - | Some name -> name - in - begin match path.directory with - | None -> () - | Some dir -> - (* Chop off number of package parts from the dir and use that as class path. *) - let parts = ExtString.String.nsplit dir (if path.backslash then "\\" else "/") in - let parts = List.rev (ExtList.List.drop (List.length pack) (List.rev parts)) in - let dir = ExtString.String.join (if path.backslash then "\\" else "/") parts in - com.class_path <- dir :: com.class_path - end; - ignore(TypeloadModule.type_module ctx (pack,name) file ~dont_check_path:true decls null_pos) - -let load_display_content_standalone ctx input = - let com = ctx.com in - let file = file_input_marker in - let p = {pfile = file; pmin = 0; pmax = 0} in - let parsed = TypeloadParse.parse_file_from_string com file p input in - let pack,decls = TypeloadParse.handle_parser_result com p parsed in - ignore(TypeloadModule.type_module ctx (pack,"?DISPLAY") file ~dont_check_path:true decls p) - -let promote_type_hints tctx = - let rec explore_type_hint (md,p,t) = - match t with - | TMono r -> (match r.tm_type with None -> () | Some t -> explore_type_hint (md,p,t)) - | TLazy f -> explore_type_hint (md,p,lazy_type f) - | TInst(({cl_name_pos = pn;cl_path = (_,name)}),_) - | TEnum(({e_name_pos = pn;e_path = (_,name)}),_) - | TType(({t_name_pos = pn;t_path = (_,name)}),_) - | TAbstract(({a_name_pos = pn;a_path = (_,name)}),_) -> - md.m_type_hints <- (p,pn) :: md.m_type_hints; - | TDynamic _ -> () - | TFun _ | TAnon _ -> () - in - List.iter explore_type_hint tctx.g.type_hints - -let process_global_display_mode com tctx = - promote_type_hints tctx; - match com.display.dms_kind with - | DMUsage (with_definition,_,_) -> - FindReferences.find_references tctx com with_definition - | DMImplementation -> - FindReferences.find_implementations tctx com - | DMDiagnostics _ -> - Diagnostics.run com - | DMStatistics -> - let stats = Statistics.collect_statistics tctx [SFFile (DisplayPosition.display_position#get).pfile] true in - raise_statistics (Statistics.Printer.print_statistics stats) - | DMModuleSymbols (Some "") -> () - | DMModuleSymbols filter -> - let symbols = match CompilationServer.get() with - | None -> [] - | Some cs -> - let l = cs#get_context_files ((Define.get_signature com.defines) :: (match com.get_macros() with None -> [] | Some com -> [Define.get_signature com.defines])) in - List.fold_left (fun acc (file_key,cfile) -> - let file = cfile.CompilationServer.c_file_path in - if (filter <> None || DisplayPosition.display_position#is_in_file (com.file_keys#get file)) then - (file,DocumentSymbols.collect_module_symbols (Some (file,get_module_name_of_cfile file cfile)) (filter = None) (cfile.c_package,cfile.c_decls)) :: acc - else - acc - ) [] l - in - raise_module_symbols (DocumentSymbols.Printer.print_module_symbols com symbols filter) - | _ -> () - let find_doc t = let doc = match follow t with | TAnon an -> @@ -475,7 +255,7 @@ let handle_syntax_completion com kind subj = () | _ -> let l = List.map make_ci_keyword l in - match com.json_out with + match com.Common.json_out with | None -> let b = Buffer.create 0 in Buffer.add_string b "\n"; @@ -488,4 +268,117 @@ let handle_syntax_completion com kind subj = raise (Completion s) | Some api -> let ctx = Genjson.create_context ~jsonrpc:api.jsonrpc GMFull in - api.send_result(fields_to_json ctx l kind subj) \ No newline at end of file + api.send_result(fields_to_json ctx l kind subj) + +let handle_display_exception_old ctx dex = match dex with + | DisplayPackage pack -> + DisplayPosition.display_position#reset; + raise (Completion (String.concat "." pack)) + | DisplayFields r -> + DisplayPosition.display_position#reset; + let fields = if !Timer.measure_times then begin + Timer.close_times(); + (List.map (fun (name,value) -> + CompletionItem.make_ci_timer ("@TIME " ^ name) value + ) (get_timer_fields !Helper.start_time)) @ r.fitems + end else + r.fitems + in + let s = match r.fkind with + | CRToplevel _ + | CRTypeHint + | CRExtends + | CRImplements + | CRStructExtension _ + | CRImport + | CRUsing + | CRNew + | CRPattern _ + | CRTypeRelation + | CRTypeDecl -> + print_toplevel fields + | CRField _ + | CRStructureField + | CRMetadata + | CROverride -> + print_fields fields + in + raise (Completion s) + | DisplayHover ({hitem = {CompletionItem.ci_type = Some (t,_)}} as hover) -> + DisplayPosition.display_position#reset; + let doc = CompletionItem.get_documentation hover.hitem in + raise (Completion (print_type t hover.hpos doc)) + | DisplaySignatures (signatures,_,display_arg,_) -> + DisplayPosition.display_position#reset; + if ctx.com.display.dms_kind = DMSignature then + raise (Completion (print_signature signatures display_arg)) + else + raise (Completion (print_signatures signatures)) + | DisplayPositions pl -> + DisplayPosition.display_position#reset; + raise (Completion (print_positions pl)) + | ModuleSymbols s | Metadata s -> + DisplayPosition.display_position#reset; + raise (Completion s) + | DisplayHover _ | DisplayNoResult -> + raise (Completion "") + +let handle_display_exception_json ctx dex api = + match dex with + | DisplayHover _ | DisplayPositions _ | DisplayFields _ | DisplayPackage _ | DisplaySignatures _ -> + DisplayPosition.display_position#reset; + let ctx = DisplayJson.create_json_context api.jsonrpc (match dex with DisplayFields _ -> true | _ -> false) in + api.send_result (DisplayException.to_json ctx dex) + | DisplayNoResult -> + api.send_result JNull + | _ -> + handle_display_exception_old ctx dex + +let handle_display_exception ctx dex = match ctx.com.json_out with + | Some api -> + handle_display_exception_json ctx dex api + | None -> + handle_display_exception_old ctx dex + +let handle_type_path_exception ctx p c is_import pos = + let open DisplayTypes.CompletionResultKind in + let com = ctx.com in + let fields = + try begin match c with + | None -> + DisplayPath.TypePathHandler.complete_type_path com p + | Some (c,cur_package) -> + let ctx = Typer.create com in + DisplayPath.TypePathHandler.complete_type_path_inner ctx p c cur_package is_import + end with Common.Abort msg -> + located_error ctx msg; + None + in + begin match ctx.com.json_out,fields with + | None,None -> + () + | None,Some fields -> + raise (Completion (print_fields fields)) + | Some api,None when is_legacy_completion com -> + api.send_result JNull + | Some api,fields -> + let fields = Option.default [] fields in + let ctx = DisplayJson.create_json_context api.jsonrpc false in + let path = match List.rev p with + | name :: pack -> List.rev pack,name + | [] -> [],"" + in + let kind = CRField ((CompletionItem.make_ci_module path,pos,None,None)) in + api.send_result (DisplayException.fields_to_json ctx fields kind (DisplayTypes.make_subject None pos)); + end + +let emit_diagnostics com = + let dctx = Diagnostics.run com in + let s = Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics com dctx) in + DisplayPosition.display_position#reset; + raise (Completion s) + +let emit_statistics tctx = + let stats = Statistics.collect_statistics tctx [SFFile (DisplayPosition.display_position#get).pfile] true in + let s = Statistics.Printer.print_statistics stats in + raise (Completion s) diff --git a/src/compiler/displayProcessing.ml b/src/compiler/displayProcessing.ml new file mode 100644 index 00000000..7cec1d9d --- /dev/null +++ b/src/compiler/displayProcessing.ml @@ -0,0 +1,357 @@ +open Globals +open Common +open CompilationContext +open DisplayProcessingGlobals + +type display_path_kind = + | DPKNormal of path + | DPKMacro of path + | DPKDirect of string + | DPKInput of string + | DPKNone + +(* 1. Argument processing from --display *) + +let handle_display_argument_old com file_pos actx = + match file_pos with + | "classes" -> + actx.pre_compilation <- (fun() -> raise (Parser.TypePath (["."],None,true,null_pos))) :: actx.pre_compilation; + | "keywords" -> + raise (Completion (DisplayOutput.print_keywords ())) + | "memory" -> + actx.did_something <- true; + (try Memory.display_memory com with e -> prerr_endline (Printexc.get_backtrace ())); + | "diagnostics" -> + com.report_mode <- RMDiagnostics [] + | _ -> + let file, pos = try ExtString.String.split file_pos "@" with _ -> failwith ("Invalid format: " ^ file_pos) in + let file = Helper.unquote file in + let file_unique = com.file_keys#get file in + let pos, smode = try ExtString.String.split pos "@" with _ -> pos,"" in + let create mode = + Parser.display_mode := mode; + DisplayTypes.DisplayMode.create mode + in + let dm = match smode with + | "position" -> + create DMDefinition + | "usage" -> + create (DMUsage (false,false,false)) + | "package" -> + create DMPackage + | "type" -> + create DMHover + | "toplevel" -> + create DMDefault + | "module-symbols" -> + create (DMModuleSymbols None) + | "diagnostics" -> + com.report_mode <- RMDiagnostics [file_unique]; + let dm = create DMNone in + {dm with dms_display_file_policy = DFPAlso; dms_per_file = true} + | "statistics" -> + com.report_mode <- RMStatistics; + let dm = create DMNone in + {dm with dms_display_file_policy = DFPAlso; dms_error_policy = EPIgnore; dms_per_file = true} + | "signature" -> + create DMSignature + | "" -> + create DMDefault + | _ -> + let smode,arg = try ExtString.String.split smode "@" with _ -> pos,"" in + match smode with + | "workspace-symbols" -> + create (DMModuleSymbols (Some arg)) + | _ -> + create DMDefault + in + let pos = try int_of_string pos with _ -> failwith ("Invalid format: " ^ pos) in + com.display <- dm; + if not com.display.dms_full_typing then Common.define_value com Define.Display (if smode <> "" then smode else "1"); + DisplayPosition.display_position#set { + pfile = Path.get_full_path file; + pmin = pos; + pmax = pos; + } + +let process_display_arg ctx actx = + match actx.display_arg with + | Some input -> + let input = String.trim input in + if String.length input > 0 && (input.[0] = '[' || input.[0] = '{') then begin + actx.did_something <- true; + actx.force_typing <- true; + DisplayJson.parse_input ctx.com input Timer.measure_times + end else + handle_display_argument_old ctx.com input actx; + | None -> + () + +(* 2. Compilation start, setup display configuration in context *) + +let process_display_configuration ctx = + let com = ctx.com in + if is_diagnostics com then begin + com.info <- (fun ?depth s p -> + add_diagnostics_message ?depth com (located s p) DKCompilerMessage Information + ); + com.warning <- (fun ?depth w options s p -> + match Warning.get_mode w (com.warning_options @ options) with + | WMEnable -> + add_diagnostics_message ?depth com (located s p) DKCompilerMessage Warning + | WMDisable -> + () + ); + end; + Lexer.old_format := Common.defined com Define.OldErrorFormat; + if !Lexer.old_format && !Parser.in_display then begin + let p = DisplayPosition.display_position#get in + (* convert byte position to utf8 position *) + try + let content = Std.input_file ~bin:true (Path.get_real_path p.pfile) in + let pos = Extlib_leftovers.UTF8.length (String.sub content 0 p.pmin) in + DisplayPosition.display_position#set { p with pmin = pos; pmax = pos } + with _ -> + () (* ignore *) + end + +let process_display_file com actx = + let get_module_path_from_file_path com spath = + let rec loop = function + | [] -> None + | cp :: l -> + let cp = (if cp = "" then "./" else cp) in + let c = Path.add_trailing_slash (Path.get_real_path cp) in + let clen = String.length c in + if clen < String.length spath && String.sub spath 0 clen = c then begin + let path = String.sub spath clen (String.length spath - clen) in + (try + let path = Path.parse_path path in + (match loop l with + | Some x as r when String.length (s_type_path x) < String.length (s_type_path path) -> r + | _ -> Some path) + with _ -> loop l) + end else + loop l + in + loop com.class_path + in + match com.display.dms_display_file_policy with + | DFPNo -> + DPKNone + | DFPOnly when (DisplayPosition.display_position#get).pfile = file_input_marker -> + actx.classes <- []; + com.main_class <- None; + begin match !TypeloadParse.current_stdin with + | Some input -> + TypeloadParse.current_stdin := None; + DPKInput input + | None -> + DPKNone + end + | dfp -> + if dfp = DFPOnly then begin + actx.classes <- []; + com.main_class <- None; + end; + let real = Path.get_real_path (DisplayPosition.display_position#get).pfile in + let path = match get_module_path_from_file_path com real with + | Some path -> + if com.display.dms_kind = DMPackage then DisplayException.raise_package (fst path); + let path = match ExtString.String.nsplit (snd path) "." with + | [name;"macro"] -> + (* If we have a .macro.hx path, don't add the file to classes because the compiler won't find it. + This can happen if we're completing in such a file. *) + DPKMacro (fst path,name) + | [name] -> + actx.classes <- path :: actx.classes; + DPKNormal path + | [name;target] -> + let path = fst path, name in + actx.classes <- path :: actx.classes; + DPKNormal path + | e -> + die "" __LOC__ + in + path + | None -> + if not (Sys.file_exists real) then failwith "Display file does not exist"; + (match List.rev (ExtString.String.nsplit real Path.path_sep) with + | file :: _ when file.[0] >= 'a' && file.[0] <= 'z' -> failwith ("Display file '" ^ file ^ "' should not start with a lowercase letter") + | _ -> ()); + DPKDirect real + in + Common.log com ("Display file : " ^ real); + Common.log com ("Classes found : [" ^ (String.concat "," (List.map s_type_path actx.classes)) ^ "]"); + path + +(* 3. Loaders for display file that might be called *) + +let load_display_module_in_macro tctx display_file_dot_path clear = match display_file_dot_path with + | Some cpath -> + let p = null_pos in + begin try + let open Typecore in + let _, mctx = MacroContext.get_macro_context tctx p in + (* Tricky stuff: We want to remove the module from our lookups and load it again in + display mode. This covers some cases like --macro typing it in non-display mode (issue #7017). *) + if clear then begin + begin try + let m = mctx.com.module_lut#find cpath in + mctx.com.module_lut#remove cpath; + mctx.com.type_to_module#remove cpath; + List.iter (fun mt -> + let ti = Type.t_infos mt in + mctx.com.module_lut#remove ti.mt_path; + mctx.com.type_to_module#remove ti.mt_path; + ) m.m_types + with Not_found -> + () + end; + end; + let _ = MacroContext.load_macro_module tctx cpath true p in + Finalization.finalize mctx; + Some mctx + with DisplayException.DisplayException _ | Parser.TypePath _ as exc -> + raise exc + | _ -> + None + end + | None -> + None + +let load_display_file_standalone (ctx : Typecore.typer) file = + let com = ctx.com in + let pack,decls = TypeloadParse.parse_module_file com file null_pos in + let path = Path.FilePath.parse file in + let name = match path.file_name with + | None -> "?DISPLAY" + | Some name -> name + in + begin match path.directory with + | None -> () + | Some dir -> + (* Chop off number of package parts from the dir and use that as class path. *) + let parts = ExtString.String.nsplit dir (if path.backslash then "\\" else "/") in + let parts = List.rev (ExtList.List.drop (List.length pack) (List.rev parts)) in + let dir = ExtString.String.join (if path.backslash then "\\" else "/") parts in + com.class_path <- dir :: com.class_path + end; + ignore(TypeloadModule.type_module ctx (pack,name) file ~dont_check_path:true decls null_pos) + +let load_display_content_standalone (ctx : Typecore.typer) input = + let com = ctx.com in + let file = file_input_marker in + let p = {pfile = file; pmin = 0; pmax = 0} in + let parsed = TypeloadParse.parse_file_from_string com file p input in + let pack,decls = TypeloadParse.handle_parser_result com p parsed in + ignore(TypeloadModule.type_module ctx (pack,"?DISPLAY") file ~dont_check_path:true decls p) + +(* 4. Display processing before typing *) + +let maybe_load_display_file_before_typing tctx display_file_dot_path = match display_file_dot_path with + | DPKMacro path -> + ignore(load_display_module_in_macro tctx (Some path) true); + Some path + | DPKNormal path -> + Some path + | DPKNone -> + None + | DPKDirect file -> + load_display_file_standalone tctx file; + None + | DPKInput input -> + load_display_content_standalone tctx input; + None + +(* 5. Display processing after typing *) + +let handle_display_after_typing ctx tctx display_file_dot_path = + let com = ctx.com in + if ctx.com.display.dms_kind = DMNone & ctx.has_error then raise Abort; + begin match ctx.com.display.dms_kind,!Parser.delayed_syntax_completion with + | DMDefault,Some(kind,subj) -> DisplayOutput.handle_syntax_completion com kind subj + | _ -> () + end; + if ctx.com.display.dms_exit_during_typing then begin + if ctx.has_next || ctx.has_error then raise Abort; + (* If we didn't find a completion point, load the display file in macro mode. *) + if com.display_information.display_module_has_macro_defines then + ignore(load_display_module_in_macro tctx display_file_dot_path true); + let no_completion_point_found = "No completion point was found" in + match com.json_out with + | Some _ -> + raise (DisplayException.DisplayException DisplayNoResult) + | None -> + failwith no_completion_point_found; + end + +(* 6. Display processing after finalization *) + +let promote_type_hints (tctx : Typecore.typer) = + let open Type in + let rec explore_type_hint (md,p,t) = + match t with + | TMono r -> (match r.tm_type with None -> () | Some t -> explore_type_hint (md,p,t)) + | TLazy f -> explore_type_hint (md,p,lazy_type f) + | TInst(({cl_name_pos = pn;cl_path = (_,name)}),_) + | TEnum(({e_name_pos = pn;e_path = (_,name)}),_) + | TType(({t_name_pos = pn;t_path = (_,name)}),_) + | TAbstract(({a_name_pos = pn;a_path = (_,name)}),_) -> + md.m_type_hints <- (p,pn) :: md.m_type_hints; + | TDynamic _ -> () + | TFun _ | TAnon _ -> () + in + List.iter explore_type_hint tctx.g.type_hints + +let process_global_display_mode com tctx = + promote_type_hints tctx; + match com.display.dms_kind with + | DMUsage (with_definition,_,_) -> + FindReferences.find_references tctx com with_definition + | DMImplementation -> + FindReferences.find_implementations tctx com + | DMModuleSymbols (Some "") -> () + | DMModuleSymbols filter -> + let open CompilationCache in + let cs = com.cs in + let symbols = + let l = cs#get_context_files ((Define.get_signature com.defines) :: (match com.get_macros() with None -> [] | Some com -> [Define.get_signature com.defines])) in + List.fold_left (fun acc (file_key,cfile) -> + let file = cfile.c_file_path in + if (filter <> None || DisplayPosition.display_position#is_in_file (com.file_keys#get file)) then + (file,DocumentSymbols.collect_module_symbols (Some (file,get_module_name_of_cfile file cfile)) (filter = None) (cfile.c_package,cfile.c_decls)) :: acc + else + acc + ) [] l + in + DisplayException.raise_module_symbols (DocumentSymbols.Printer.print_module_symbols com symbols filter) + | _ -> () + +let handle_display_after_finalization ctx tctx display_file_dot_path = + let com = ctx.com in + (* Special case for diagnostics: We don't want to load the display file in macro mode because there's a chance it might not be + macro-compatible. This means that we might some macro-specific diagnostics, but I don't see what we could do about that. *) + let should_load_in_macro = + (* Special case for the special case: If the display file has a block which becomes active if `macro` is defined, we can safely + type the module in macro context. (#8682). *) + not (is_diagnostics com) || com.display_information.display_module_has_macro_defines + in + if ctx.com.display.dms_force_macro_typing && should_load_in_macro then begin + match load_display_module_in_macro tctx display_file_dot_path false with + | None -> () + | Some mctx -> + (* We don't need a full macro flush here because we're not going to run any macros. *) + let _, types, modules = Finalization.generate mctx in + mctx.Typecore.com.types <- types; + mctx.Typecore.com.Common.modules <- modules + end; + process_global_display_mode com tctx; + begin match com.report_mode with + | RMDiagnostics _ -> + DisplayOutput.emit_diagnostics com + | RMStatistics -> + DisplayOutput.emit_statistics tctx + | RMNone -> + () + end \ No newline at end of file diff --git a/src/compiler/displayProcessingGlobals.ml b/src/compiler/displayProcessingGlobals.ml new file mode 100644 index 00000000..92ab2ebd --- /dev/null +++ b/src/compiler/displayProcessingGlobals.ml @@ -0,0 +1,3 @@ +exception Completion of string + +let file_input_marker = Path.get_full_path "? input" diff --git a/src/compiler/generate.ml b/src/compiler/generate.ml new file mode 100644 index 00000000..76e9f256 --- /dev/null +++ b/src/compiler/generate.ml @@ -0,0 +1,101 @@ +open Globals +open CompilationContext + +let check_auxiliary_output com actx = + begin match actx.xml_out with + | None -> () + | Some "hx" -> + Genhxold.generate com + | Some file -> + Common.log com ("Generating xml: " ^ file); + Path.mkdir_from_path file; + Genxml.generate com file + end; + begin match actx.json_out with + | None -> () + | Some file -> + Common.log com ("Generating json : " ^ file); + Path.mkdir_from_path file; + Genjson.generate com.types file + end + + +let parse_swf_header ctx h = match ExtString.String.nsplit h ":" with + | [width; height; fps] -> + Some (int_of_string width,int_of_string height,float_of_string fps,0xFFFFFF) + | [width; height; fps; color] -> + let color = if ExtString.String.starts_with color "0x" then color else "0x" ^ color in + Some (int_of_string width, int_of_string height, float_of_string fps, int_of_string color) + | _ -> + error ctx "Invalid SWF header format, expected width:height:fps[:color]" null_pos; + None + +let delete_file f = try Sys.remove f with _ -> () + +let generate ctx tctx ext actx = + let com = tctx.Typecore.com in + (* check file extension. In case of wrong commandline, we don't want + to accidentaly delete a source file. *) + if Path.file_extension com.file = ext then delete_file com.file; + if com.platform = Flash || com.platform = Cpp || com.platform = Hl then List.iter (Codegen.fix_overrides com) com.types; + if Common.defined com Define.Dump then begin + Codegen.Dump.dump_types com; + Option.may Codegen.Dump.dump_types (com.get_macros()) + end; + if Common.defined com Define.DumpDependencies then begin + Codegen.Dump.dump_dependencies com; + if not com.is_macro_context then match tctx.Typecore.g.Typecore.macros with + | None -> () + | Some(_,ctx) -> Codegen.Dump.dump_dependencies ~target_override:(Some "macro") ctx.Typecore.com + end; + begin match com.platform with + | Neko | Hl | Eval when actx.interp -> () + | Cpp when Common.defined com Define.Cppia -> () + | Cpp | Cs | Php -> Path.mkdir_from_path (com.file ^ "/.") + | Java when not actx.jvm_flag -> Path.mkdir_from_path (com.file ^ "/.") + | _ -> Path.mkdir_from_path com.file + end; + if actx.interp then + Std.finally (Timer.timer ["interp"]) MacroContext.interpret tctx + else if com.platform = Cross then + () + else begin + let generate,name = match com.platform with + | Flash -> + let header = try + parse_swf_header ctx (Common.defined_value com Define.SwfHeader) + with Not_found -> + None + in + Genswf.generate header,"swf" + | Neko -> + Genneko.generate,"neko" + | Js -> + Genjs.generate,"js" + | Lua -> + Genlua.generate,"lua" + | Php -> + Genphp7.generate,"php" + | Cpp -> + Gencpp.generate,"cpp" + | Cs -> + Gencs.generate,"cs" + | Java -> + if Common.defined com Jvm then + Genjvm.generate actx.jvm_flag,"java" + else + Genjava.generate,"java" + | Python -> + Genpy.generate,"python" + | Hl -> + Genhl.generate,"hl" + | Eval -> + (fun _ -> MacroContext.interpret tctx),"eval" + | Cross -> + die "" __LOC__ + in + Common.log com ("Generating " ^ name ^ ": " ^ com.file); + let t = Timer.timer ["generate";name] in + generate com; + t() + end \ No newline at end of file diff --git a/src/compiler/haxe.ml b/src/compiler/haxe.ml index 34c31a54..4d609104 100644 --- a/src/compiler/haxe.ml +++ b/src/compiler/haxe.ml @@ -41,1216 +41,14 @@ trailing l means list (but we also use natural plurals such as "metas") semantic suffixes may be used freely (e.g. e1, e_if, e') *) -open Extlib_leftovers -open Printf -open Common -open DisplayTypes.DisplayMode -open DisplayTypes.CompletionResultKind -open DisplayException -open Type open Server -open Globals -open Filename -exception Abort -exception HelpMessage of string - -let executable_path() = - Extc.executable_path() - -let message ctx msg = - ctx.messages <- msg :: ctx.messages - -let deprecated = [] - -let limit_string s offset = - let rest = 80 - offset in - let words = ExtString.String.nsplit s " " in - let rec loop i words = match words with - | word :: words -> - if String.length word + i + 1 > rest then (Printf.sprintf "\n%*s" offset "") :: word :: loop (String.length word) words - else (if i = 0 then "" else " ") :: word :: loop (i + 1 + String.length word) words - | [] -> - [] - in - String.concat "" (loop 0 words) - -let error ctx msg p = - let msg = try List.assoc msg deprecated with Not_found -> msg in - message ctx (CMError(msg,p)); - ctx.has_error <- true - -let reserved_flags = [ - "true";"false";"null";"cross";"js";"lua";"neko";"flash";"php";"cpp";"cs";"java";"python"; - "swc";"macro";"sys";"static";"utf16";"haxe";"haxe_ver" - ] - -let reserved_flag_namespaces = ["target"] - -let delete_file f = try Sys.remove f with _ -> () - -let expand_env ?(h=None) path = - let r = Str.regexp "%\\([A-Za-z0-9_]+\\)%" in - Str.global_substitute r (fun s -> - let key = Str.matched_group 1 s in - try - Sys.getenv key - with Not_found -> try - match h with - | None -> raise Not_found - | Some h -> Hashtbl.find h key - with Not_found -> - "%" ^ key ^ "%" - ) path - -let add_libs com libs = - let global_repo = List.exists (fun a -> a = "--haxelib-global") com.args in - let call_haxelib() = - let t = Timer.timer ["haxelib"] in - let cmd = "haxelib" ^ (if global_repo then " --global" else "") ^ " path " ^ String.concat " " libs in - let pin, pout, perr = Unix.open_process_full cmd (Unix.environment()) in - let lines = Std.input_list pin in - let err = Std.input_list perr in - let ret = Unix.close_process_full (pin,pout,perr) in - if ret <> Unix.WEXITED 0 then failwith (match lines, err with - | [], [] -> "Failed to call haxelib (command not found ?)" - | [], [s] when ExtString.String.ends_with (ExtString.String.strip s) "Module not found: path" -> "The haxelib command has been strip'ed, please install it again" - | _ -> String.concat "\n" (lines@err)); - t(); - lines - in - match libs with - | [] -> [] - | _ -> - let lines = match CompilationServer.get() with - | Some cs -> - (try - (* if we are compiling, really call haxelib since library path might have changed *) - if not com.display.dms_display then raise Not_found; - cs#find_haxelib libs - with Not_found -> - let lines = call_haxelib() in - cs#cache_haxelib libs lines; - lines) - | _ -> call_haxelib() - in - let extra_args = ref [] in - let lines = List.fold_left (fun acc l -> - let l = ExtString.String.strip l in - if l = "" then acc else - if l.[0] <> '-' then l :: acc else - match (try ExtString.String.split l " " with _ -> l, "") with - | ("-L",dir) -> - com.neko_libs <- String.sub l 3 (String.length l - 3) :: com.neko_libs; - acc - | param, value -> - extra_args := param :: !extra_args; - if value <> "" then extra_args := value :: !extra_args; - acc - ) [] lines in - com.class_path <- lines @ com.class_path; - List.rev !extra_args - -let run_command ctx cmd = - let h = Hashtbl.create 0 in - Hashtbl.add h "__file__" ctx.com.file; - Hashtbl.add h "__platform__" (platform_name ctx.com.platform); - let t = Timer.timer ["command"] in - let cmd = expand_env ~h:(Some h) cmd in - let len = String.length cmd in - let result = - if len > 3 && String.sub cmd 0 3 = "cd " then begin - Sys.chdir (String.sub cmd 3 (len - 3)); - 0 - (* Emit stderr as a server message in server mode *) - end else if CompilationServer.runs() then begin - let binary_string s = - if not Globals.is_windows then s else String.concat "\n" (Str.split (Str.regexp "\r\n") s) - in - let pout, pin, perr = Unix.open_process_full cmd (Unix.environment()) in - let bout = Buffer.create 0 in - let berr = Buffer.create 0 in - let read_content channel buf = - Buffer.add_string buf (IO.read_all (IO.input_channel channel)); - in - let tout = Thread.create (fun() -> read_content pout bout) () in - read_content perr berr; - Thread.join tout; - let result = (match Unix.close_process_full (pout,pin,perr) with Unix.WEXITED c | Unix.WSIGNALED c | Unix.WSTOPPED c -> c) in - let serr = binary_string (Buffer.contents berr) in - let sout = binary_string (Buffer.contents bout) in - if serr <> "" then ctx.messages <- CMError((if serr.[String.length serr - 1] = '\n' then String.sub serr 0 (String.length serr - 1) else serr),null_pos) :: ctx.messages; - if sout <> "" then ctx.com.print (sout ^ "\n"); - result - (* Direct pass-through of std streams for normal compilation *) - end else begin - match Unix.system cmd with - | WEXITED c | WSIGNALED c | WSTOPPED c -> c - end - in - t(); - result - -module Initialize = struct - let set_platform com pf file = - if com.platform <> Cross then failwith "Multiple targets"; - Common.init_platform com pf; - com.file <- file; - if (pf = Flash) && file_extension file = "swc" then Common.define com Define.Swc - - let initialize_target ctx com classes = - let add_std dir = - com.class_path <- List.filter (fun s -> not (List.mem s com.std_path)) com.class_path @ List.map (fun p -> p ^ dir ^ "/_std/") com.std_path @ com.std_path - in - match com.platform with - | Cross -> - (* no platform selected *) - set_platform com Cross ""; - "?" - | Flash -> - let rec loop = function - | [] -> () - | (v,_) :: _ when v > com.flash_version -> () - | (v,def) :: l -> - Common.raw_define com ("flash" ^ def); - loop l - in - loop Common.flash_versions; - Common.raw_define com "flash"; - com.package_rules <- PMap.remove "flash" com.package_rules; - add_std "flash"; - "swf" - | Neko -> - add_std "neko"; - "n" - | Js -> - let es_version = - try - int_of_string (Common.defined_value com Define.JsEs) - with - | Not_found -> - (Common.define_value com Define.JsEs "5"; 5) - | _ -> - 0 - in - - if es_version < 3 || es_version = 4 then (* we don't support ancient and there's no 4th *) - failwith "Invalid -D js-es value"; - - if es_version >= 5 then Common.raw_define com "js-es5"; (* backward-compatibility *) - - add_std "js"; - "js" - | Lua -> - add_std "lua"; - "lua" - | Php -> - add_std "php"; - "php" - | Cpp -> - Common.define_value com Define.HxcppApiLevel "400"; - add_std "cpp"; - if Common.defined com Define.Cppia then - classes := (Path.parse_path "cpp.cppia.HostClasses" ) :: !classes; - "cpp" - | Cs -> - let old_flush = ctx.flush in - ctx.flush <- (fun () -> - com.native_libs.net_libs <- []; - old_flush() - ); - Dotnet.before_generate com; - add_std "cs"; "cs" - | Java -> - let old_flush = ctx.flush in - ctx.flush <- (fun () -> - List.iter (fun java_lib -> java_lib#close) com.native_libs.java_libs; - com.native_libs.java_libs <- []; - old_flush() - ); - Java.before_generate com; - if defined com Define.Jvm then begin - add_std "jvm"; - com.package_rules <- PMap.remove "jvm" com.package_rules; - end; - add_std "java"; - "java" - | Python -> - add_std "python"; - if not (Common.defined com Define.PythonVersion) then - Common.define_value com Define.PythonVersion "3.3"; - "python" - | Hl -> - add_std "hl"; - if not (Common.defined com Define.HlVer) then Define.define_value com.defines Define.HlVer (try Std.input_file (Common.find_file com "hl/hl_version") with Not_found -> die "" __LOC__); - "hl" - | Eval -> - add_std "eval"; - "eval" -end - -let generate tctx ext interp jvm_flag swf_header = - let com = tctx.Typecore.com in - (* check file extension. In case of wrong commandline, we don't want - to accidentaly delete a source file. *) - if file_extension com.file = ext then delete_file com.file; - if com.platform = Flash || com.platform = Cpp || com.platform = Hl then List.iter (Codegen.fix_overrides com) com.types; - if Common.defined com Define.Dump then begin - Codegen.Dump.dump_types com; - Option.may Codegen.Dump.dump_types (com.get_macros()) - end; - if Common.defined com Define.DumpDependencies then begin - Codegen.Dump.dump_dependencies com; - if not tctx.Typecore.in_macro then match tctx.Typecore.g.Typecore.macros with - | None -> () - | Some(_,ctx) -> Codegen.Dump.dump_dependencies ~target_override:(Some "macro") ctx.Typecore.com - end; - begin match com.platform with - | Neko | Hl | Eval when interp -> () - | Cpp when Common.defined com Define.Cppia -> () - | Cpp | Cs | Php -> Path.mkdir_from_path (com.file ^ "/.") - | Java when not jvm_flag -> Path.mkdir_from_path (com.file ^ "/.") - | _ -> Path.mkdir_from_path com.file - end; - if interp then - Std.finally (Timer.timer ["interp"]) MacroContext.interpret tctx - else if com.platform = Cross then - () - else begin - let generate,name = match com.platform with - | Flash -> - Genswf.generate swf_header,"swf" - | Neko -> - Genneko.generate,"neko" - | Js -> - Genjs.generate,"js" - | Lua -> - Genlua.generate,"lua" - | Php -> - Genphp7.generate,"php" - | Cpp -> - Gencpp.generate,"cpp" - | Cs -> - Gencs.generate,"cs" - | Java -> - if Common.defined com Jvm then - Genjvm.generate jvm_flag,"java" - else - Genjava.generate,"java" - | Python -> - Genpy.generate,"python" - | Hl -> - Genhl.generate,"hl" - | Eval -> - (fun _ -> MacroContext.interpret tctx),"eval" - | Cross -> - die "" __LOC__ - in - Common.log com ("Generating " ^ name ^ ": " ^ com.file); - let t = Timer.timer ["generate";name] in - generate com; - t() - end - -let get_std_class_paths () = - try - let p = Sys.getenv "HAXE_STD_PATH" in - let rec loop = function - | drive :: path :: l -> - if String.length drive = 1 && ((drive.[0] >= 'a' && drive.[0] <= 'z') || (drive.[0] >= 'A' && drive.[0] <= 'Z')) then - (drive ^ ":" ^ path) :: loop l - else - drive :: loop (path :: l) - | l -> - l - in - let parts = Str.split_delim (Str.regexp "[;:]") p in - "" :: List.map Path.add_trailing_slash (loop parts) - with Not_found -> - let base_path = Path.get_real_path (try executable_path() with _ -> "./") in - if Sys.os_type = "Unix" then - let prefix_path = Filename.dirname base_path in - let lib_path = Filename.concat prefix_path "lib" in - let share_path = Filename.concat prefix_path "share" in - [ - ""; - Path.add_trailing_slash (Filename.concat lib_path "haxe/std"); - Path.add_trailing_slash (Filename.concat lib_path "haxe/extraLibs"); - Path.add_trailing_slash (Filename.concat share_path "haxe/std"); - Path.add_trailing_slash (Filename.concat share_path "haxe/extraLibs"); - Path.add_trailing_slash (Filename.concat base_path "std"); - Path.add_trailing_slash (Filename.concat base_path "extraLibs") - ] - else - [ - ""; - Path.add_trailing_slash (Filename.concat base_path "std"); - Path.add_trailing_slash (Filename.concat base_path "extraLibs") - ] - -let setup_common_context ctx com = - Common.define_value com Define.HaxeVer (Printf.sprintf "%.3f" (float_of_int Globals.version /. 1000.)); - Common.raw_define com "haxe3"; - Common.raw_define com "haxe4"; - Common.define_value com Define.Haxe (s_version false); - Common.define_value com Define.Dce "std"; - com.info <- (fun msg p -> message ctx (CMInfo(msg,p))); - com.warning <- (fun msg p -> message ctx (CMWarning(msg,p))); - com.error <- error ctx; - let filter_messages = (fun keep_errors predicate -> (List.filter (fun msg -> - (match msg with - | CMError(_,_) -> keep_errors; - | CMInfo(_,_) | CMWarning(_,_) -> predicate msg;) - ) (List.rev ctx.messages))) in - com.get_messages <- (fun () -> (List.map (fun msg -> - (match msg with - | CMError(_,_) -> die "" __LOC__; - | CMInfo(_,_) | CMWarning(_,_) -> msg;) - ) (filter_messages false (fun _ -> true)))); - com.filter_messages <- (fun predicate -> (ctx.messages <- (List.rev (filter_messages true predicate)))); - if CompilationServer.runs() then com.run_command <- run_command ctx; - com.class_path <- get_std_class_paths (); - com.std_path <- List.filter (fun p -> ExtString.String.ends_with p "std/" || ExtString.String.ends_with p "std\\") com.class_path - -let process_args arg_spec = - (* Takes a list of arg specs including some custom info, and generates a - list in the format Arg.parse_argv wants. Handles multiple official or - deprecated names for the same arg; deprecated versions will display a - warning. *) - List.flatten(List.map (fun (cat, ok, dep, spec, hint, doc) -> - (* official argument names *) - (List.map (fun (arg) -> (arg, spec, doc)) ok) @ - (* deprecated argument names *) - (* let dep_msg arg = (Printf.sprintf "WARNING: %s is deprecated" arg) ^ (if List.length ok > 0 then (Printf.sprintf ". Use %s instead" (String.concat "/" ok)) else "") in *) - (* For now, these warnings are a noop. Can replace this function to - enable error output: *) - (* let dep_fun = prerr_endline (dep_msg arg) in *) - let dep_fun arg spec = () in - let dep_spec arg spec = match spec with - | Arg.String f -> Arg.String (fun x -> dep_fun arg spec; f x) - | Arg.Unit f -> Arg.Unit (fun x -> dep_fun arg spec; f x) - | Arg.Bool f -> Arg.Bool (fun x -> dep_fun arg spec; f x) - | _ -> spec in - (List.map (fun (arg) -> (arg, dep_spec arg spec, doc)) dep) - ) arg_spec) - -let usage_string ?(print_cat=true) arg_spec usage = - let make_label = fun names hint -> Printf.sprintf "%s %s" (String.concat ", " names) hint in - let args = (List.filter (fun (cat, ok, dep, spec, hint, doc) -> (List.length ok) > 0) arg_spec) in - let cat_order = ["Target";"Compilation";"Optimization";"Debug";"Batch";"Services";"Compilation Server";"Target-specific";"Miscellaneous"] in - let cats = List.filter (fun x -> List.mem x (List.map (fun (cat, _, _, _, _, _) -> cat) args)) cat_order in - let max_length = List.fold_left max 0 (List.map String.length (List.map (fun (_, ok, _, _, hint, _) -> make_label ok hint) args)) in - usage ^ (String.concat "\n" (List.flatten (List.map (fun cat -> (if print_cat then ["\n"^cat^":"] else []) @ (List.map (fun (cat, ok, dep, spec, hint, doc) -> - let label = make_label ok hint in - Printf.sprintf " %s%s %s" label (String.make (max_length - (String.length label)) ' ') doc - ) (List.filter (fun (cat', _, _, _, _, _) -> (if List.mem cat' cat_order then cat' else "Miscellaneous") = cat) args))) cats))) - -let process_display_configuration ctx = - let com = ctx.com in - if com.display.dms_kind <> DMNone then begin - com.warning <- - if com.display.dms_error_policy = EPCollect then - (fun s p -> add_diagnostics_message com s p DKCompilerError DisplayTypes.DiagnosticsSeverity.Warning) - else - (fun msg p -> message ctx (CMWarning(msg,p))); - com.error <- error ctx; - end; - Lexer.old_format := Common.defined com Define.OldErrorFormat; - if !Lexer.old_format && !Parser.in_display then begin - let p = DisplayPosition.display_position#get in - (* convert byte position to utf8 position *) - try - let content = Std.input_file ~bin:true (Path.get_real_path p.pfile) in - let pos = UTF8.length (String.sub content 0 p.pmin) in - DisplayPosition.display_position#set { p with pmin = pos; pmax = pos } - with _ -> - () (* ignore *) - end - -let run_or_diagnose com f arg = - let handle_diagnostics msg p kind = - add_diagnostics_message com msg p kind DisplayTypes.DiagnosticsSeverity.Error; - Diagnostics.run com; - in - match com.display.dms_kind with - | DMDiagnostics _ -> - begin try - f arg - with - | Error.Error(msg,p) -> - handle_diagnostics (Error.error_msg msg) p DisplayTypes.DiagnosticsKind.DKCompilerError - | Parser.Error(msg,p) -> - handle_diagnostics (Parser.error_msg msg) p DisplayTypes.DiagnosticsKind.DKParserError - | Lexer.Error(msg,p) -> - handle_diagnostics (Lexer.error_msg msg) p DisplayTypes.DiagnosticsKind.DKParserError - end - | _ -> - f arg - -let create_typer_context ctx native_libs = - let com = ctx.com in - ctx.setup(); - Common.log com ("Classpath: " ^ (String.concat ";" com.class_path)); - Common.log com ("Defines: " ^ (String.concat ";" (PMap.foldi (fun k v acc -> (match v with "1" -> k | _ -> k ^ "=" ^ v) :: acc) com.defines.Define.values []))); - Typecore.type_expr_ref := (fun ?(mode=MGet) ctx e with_type -> Typer.type_expr ~mode ctx e with_type); - List.iter (fun f -> f ()) (List.rev com.callbacks#get_before_typer_create); - (* Native lib pass 1: Register *) - let fl = List.map (fun (file,extern) -> NativeLibraryHandler.add_native_lib com file extern) (List.rev native_libs) in - (* Native lib pass 2: Initialize *) - List.iter (fun f -> f()) fl; - Typer.create com - -(** Creates the typer context and types [classes] into it. *) -let do_type tctx config_macros classes = - let com = tctx.Typecore.com in - let t = Timer.timer ["typing"] in - Option.may (fun cs -> CommonCache.maybe_add_context_sign cs com "before_init_macros") (CompilationServer.get ()); - com.stage <- CInitMacrosStart; - List.iter (MacroContext.call_init_macro tctx) (List.rev config_macros); - com.stage <- CInitMacrosDone; - CommonCache.lock_signature com "after_init_macros"; - List.iter (fun f -> f ()) (List.rev com.callbacks#get_after_init_macros); - run_or_diagnose com (fun () -> - if com.display.dms_kind <> DMNone then Option.may (DisplayTexpr.check_display_file tctx) (CompilationServer.get ()); - List.iter (fun cpath -> ignore(tctx.Typecore.g.Typecore.do_load_module tctx cpath null_pos)) (List.rev classes); - Finalization.finalize tctx; - ) (); - com.stage <- CTypingDone; - (* If we are trying to find references, let's syntax-explore everything we know to check for the - identifier we are interested in. We then type only those modules that contain the identifier. *) - begin match !CompilationServer.instance,com.display.dms_kind with - | Some cs,(DMUsage _ | DMImplementation) -> FindReferences.find_possible_references tctx cs; - | _ -> () - end; - t() - -let load_display_module_in_macro tctx display_file_dot_path clear = match display_file_dot_path with - | Some cpath -> - let p = null_pos in - begin try - let open Typecore in - let _, mctx = MacroContext.get_macro_context tctx p in - (* Tricky stuff: We want to remove the module from our lookups and load it again in - display mode. This covers some cases like --macro typing it in non-display mode (issue #7017). *) - if clear then begin - begin try - let m = Hashtbl.find mctx.g.modules cpath in - Hashtbl.remove mctx.g.modules cpath; - Hashtbl.remove mctx.g.types_module cpath; - List.iter (fun mt -> - let ti = t_infos mt in - Hashtbl.remove mctx.g.modules ti.mt_path; - Hashtbl.remove mctx.g.types_module ti.mt_path; - ) m.m_types - with Not_found -> - () - end; - end; - let _ = MacroContext.load_macro_module tctx cpath true p in - Finalization.finalize mctx; - Some mctx - with DisplayException _ | Parser.TypePath _ as exc -> - raise exc - | _ -> - None - end - | None -> - None - -let handle_display ctx tctx display_file_dot_path = - let com = ctx.com in - if not ctx.com.display.dms_display && ctx.has_error then raise Abort; - begin match ctx.com.display.dms_kind,!Parser.delayed_syntax_completion with - | DMDefault,Some(kind,subj) -> DisplayOutput.handle_syntax_completion com kind subj - | _ -> () - end; - if ctx.com.display.dms_exit_during_typing then begin - if ctx.has_next || ctx.has_error then raise Abort; - (* If we didn't find a completion point, load the display file in macro mode. *) - if com.display_information.display_module_has_macro_defines then - ignore(load_display_module_in_macro tctx display_file_dot_path true); - let no_completion_point_found = "No completion point was found" in - match com.json_out with - | Some _ -> (match ctx.com.display.dms_kind with - | DMDefault -> raise (DisplayException(DisplayFields None)) - | DMSignature -> raise (DisplayException(DisplaySignatures None)) - | DMHover -> raise (DisplayException(DisplayHover None)) - | DMDefinition | DMTypeDefinition -> raise_positions [] - | _ -> failwith no_completion_point_found) - | None -> - failwith no_completion_point_found; - end - -let filter ctx tctx display_file_dot_path = - let com = ctx.com in - com.stage <- CFilteringStart; - let t = Timer.timer ["filters"] in - let main, types, modules = run_or_diagnose com Finalization.generate tctx in - com.main <- main; - com.types <- types; - com.modules <- modules; - (* Special case for diagnostics: We don't want to load the display file in macro mode because there's a chance it might not be - macro-compatible. This means that we might some macro-specific diagnostics, but I don't see what we could do about that. *) - let should_load_in_macro = match ctx.com.display.dms_kind with - (* Special case for the special case: If the display file has a block which becomes active if `macro` is defined, we can safely - type the module in macro context. (#8682). *) - | DMDiagnostics _ -> com.display_information.display_module_has_macro_defines - | _ -> true - in - if ctx.com.display.dms_force_macro_typing && should_load_in_macro then begin - match load_display_module_in_macro tctx display_file_dot_path false with - | None -> () - | Some mctx -> - (* We don't need a full macro flush here because we're not going to run any macros. *) - let _, types, modules = Finalization.generate mctx in - mctx.Typecore.com.types <- types; - mctx.Typecore.com.Common.modules <- modules - end; - DisplayOutput.process_global_display_mode com tctx; - DeprecationCheck.run com; - Filters.run com tctx main; - t() - -let check_auxiliary_output com xml_out json_out = - begin match xml_out with - | None -> () - | Some "hx" -> - Genhxold.generate com - | Some file -> - Common.log com ("Generating xml: " ^ file); - Path.mkdir_from_path file; - Genxml.generate com file - end; - begin match json_out with - | None -> () - | Some file -> - Common.log com ("Generating json : " ^ file); - Path.mkdir_from_path file; - Genjson.generate com.types file - end - -let parse_host_port hp = - let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in - let port = try int_of_string port with _ -> raise (Arg.Bad "Invalid port") in - host, port - -let rec process_params create pl = - let each_params = ref [] in - let rec loop acc = function - | [] -> - let ctx = create (!each_params @ (List.rev acc)) in - init ctx; - ctx.flush() - | "--next" :: l when acc = [] -> (* skip empty --next *) - loop [] l - | "--next" :: l -> - let ctx = create (!each_params @ (List.rev acc)) in - ctx.has_next <- true; - init ctx; - ctx.flush(); - loop [] l - | "--each" :: l -> - each_params := List.rev acc; - loop [] l - | "--cwd" :: dir :: l | "-C" :: dir :: l -> - (* we need to change it immediately since it will affect hxml loading *) - (try Unix.chdir dir with _ -> raise (Arg.Bad ("Invalid directory: " ^ dir))); - (* Push the --cwd arg so the arg processor know we did something. *) - loop (dir :: "--cwd" :: acc) l - | "--connect" :: hp :: l -> - (match CompilationServer.get() with - | None -> - let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in - do_connect host (try int_of_string port with _ -> raise (Arg.Bad "Invalid port")) ((List.rev acc) @ l) - | Some _ -> - (* already connected : skip *) - loop acc l) - | "--run" :: cl :: args -> - let acc = cl :: "-x" :: acc in - let ctx = create (!each_params @ (List.rev acc)) in - ctx.com.sys_args <- args; - init ctx; - ctx.flush() - | arg :: l -> - match List.rev (ExtString.String.nsplit arg ".") with - | "hxml" :: _ when (match acc with "-cmd" :: _ | "--cmd" :: _ -> false | _ -> true) -> - let acc, l = (try acc, parse_hxml arg @ l with Not_found -> (arg ^ " (file not found)") :: acc, l) in - loop acc l - | _ -> loop (arg :: acc) l - in - (* put --display in front if it was last parameter *) - let pl = (match List.rev pl with - | file :: "--display" :: pl when file <> "memory" -> "--display" :: file :: List.rev pl - | _ -> pl - ) in - loop [] pl - -and init ctx = - let usage = Printf.sprintf - "Haxe Compiler %s - (C)2005-2020 Haxe Foundation\nUsage: haxe%s [options] [hxml files and dot paths...]\n" - (s_version true) (if Sys.os_type = "Win32" then ".exe" else "") - in - let com = ctx.com in - let classes = ref [([],"Std")] in -try - set_binary_mode_out stdout true; - set_binary_mode_out stderr true; - let xml_out = ref None in - let json_out = ref None in - let swf_header = ref None in - let cmds = ref [] in - let config_macros = ref [] in - let cp_libs = ref [] in - let added_libs = Hashtbl.create 0 in - let no_output = ref false in - let did_something = ref false in - let force_typing = ref false in - let pre_compilation = ref [] in - let interp = ref false in - let jvm_flag = ref false in - let swf_version = ref false in - let native_libs = ref [] in - let add_native_lib file extern = native_libs := (file,extern) :: !native_libs in - setup_common_context ctx com; - let define f = Arg.Unit (fun () -> Common.define com f) in - let process_ref = ref (fun args -> ()) in - let process_libs() = - let libs = List.filter (fun l -> not (Hashtbl.mem added_libs l)) (List.rev !cp_libs) in - cp_libs := []; - List.iter (fun l -> Hashtbl.add added_libs l ()) libs; - (* immediately process the arguments to insert them at the place -lib was defined *) - match add_libs com libs with - | [] -> () - | args -> (!process_ref) args - in - (* category, official names, deprecated names, arg spec, usage hint, doc *) - let basic_args_spec = [ - ("Target",["--js"],["-js"],Arg.String (Initialize.set_platform com Js),"","generate JavaScript code into target file"); - ("Target",["--lua"],["-lua"],Arg.String (Initialize.set_platform com Lua),"","generate Lua code into target file"); - ("Target",["--swf"],["-swf"],Arg.String (Initialize.set_platform com Flash),"","generate Flash SWF bytecode into target file"); - ("Target",["--neko"],["-neko"],Arg.String (Initialize.set_platform com Neko),"","generate Neko bytecode into target file"); - ("Target",["--php"],["-php"],Arg.String (fun dir -> - classes := (["php"],"Boot") :: !classes; - Initialize.set_platform com Php dir; - ),"","generate PHP code into target directory"); - ("Target",["--cpp"],["-cpp"],Arg.String (fun dir -> - Initialize.set_platform com Cpp dir; - ),"","generate C++ code into target directory"); - ("Target",["--cppia"],["-cppia"],Arg.String (fun file -> - Common.define com Define.Cppia; - Initialize.set_platform com Cpp file; - ),"","generate Cppia bytecode into target file"); - ("Target",["--cs"],["-cs"],Arg.String (fun dir -> - cp_libs := "hxcs" :: !cp_libs; - Initialize.set_platform com Cs dir; - ),"","generate C# code into target directory"); - ("Target",["--java"],["-java"],Arg.String (fun dir -> - cp_libs := "hxjava" :: !cp_libs; - Initialize.set_platform com Java dir; - ),"","generate Java code into target directory"); - ("Target",["--jvm"],[],Arg.String (fun dir -> - cp_libs := "hxjava" :: !cp_libs; - Common.define com Define.Jvm; - jvm_flag := true; - Initialize.set_platform com Java dir; - ),"","generate JVM bytecode into target file"); - ("Target",["--python"],["-python"],Arg.String (fun dir -> - Initialize.set_platform com Python dir; - ),"","generate Python code into target file"); - ("Target",["--hl"],["-hl"],Arg.String (fun file -> - Initialize.set_platform com Hl file; - ),"","generate HashLink .hl bytecode or .c code into target file"); - ("Target",[],["-x"], Arg.String (fun cl -> - let cpath = Path.parse_type_path cl in - (match com.main_class with - | Some c -> if cpath <> c then raise (Arg.Bad "Multiple --main classes specified") - | None -> com.main_class <- Some cpath); - classes := cpath :: !classes; - Common.define com Define.Interp; - Initialize.set_platform com (!Globals.macro_platform) ""; - interp := true; - ),"","interpret the program using internal macro system"); - ("Target",["--interp"],[], Arg.Unit (fun() -> - Common.define com Define.Interp; - Initialize.set_platform com (!Globals.macro_platform) ""; - interp := true; - ),"","interpret the program using internal macro system"); - ("Target",["--run"],[], Arg.Unit (fun() -> - raise (Arg.Bad "--run requires an argument: a Haxe module name") - ), " [args...]","interpret a Haxe module with command line arguments"); - ("Compilation",["-p";"--class-path"],["-cp"],Arg.String (fun path -> - process_libs(); - com.class_path <- Path.add_trailing_slash path :: com.class_path - ),"","add a directory to find source files"); - ("Compilation",["-m";"--main"],["-main"],Arg.String (fun cl -> - if com.main_class <> None then raise (Arg.Bad "Multiple --main classes specified"); - let cpath = Path.parse_type_path cl in - com.main_class <- Some cpath; - classes := cpath :: !classes - ),"","select startup class"); - ("Compilation",["-L";"--library"],["-lib"],Arg.String (fun l -> - cp_libs := l :: !cp_libs; - Common.raw_define com l; - ),"","use a haxelib library"); - ("Compilation",["-D";"--define"],[],Arg.String (fun var -> - let flag = try fst (ExtString.String.split var "=") with _ -> var in - let raise_reserved description = - raise (Arg.Bad (description ^ " and cannot be defined from the command line")) - in - if List.mem flag reserved_flags then raise_reserved (Printf.sprintf "`%s` is a reserved compiler flag" flag); - List.iter (fun ns -> - if ExtString.String.starts_with flag (ns ^ ".") then raise_reserved (Printf.sprintf "`%s` uses the reserved compiler flag namespace `%s.*`" flag ns) - ) reserved_flag_namespaces; - Common.raw_define com var; - ),"","define a conditional compilation flag"); - ("Debug",["-v";"--verbose"],[],Arg.Unit (fun () -> - com.verbose <- true - ),"","turn on verbose mode"); - ("Debug",["--debug"],["-debug"], Arg.Unit (fun() -> - Common.define com Define.Debug; - com.debug <- true; - ),"","add debug information to the compiled code"); - ("Miscellaneous",["--version"],["-version"],Arg.Unit (fun() -> - com.info (s_version true) null_pos; - did_something := true; - ),"","print version and exit"); - ("Miscellaneous", ["-h";"--help"], ["-help"], Arg.Unit (fun () -> - raise (Arg.Help "") - ),"","show extended help information"); - ("Miscellaneous",["--help-defines"],[], Arg.Unit (fun() -> - let all,max_length = Define.get_documentation_list() in - let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in - List.iter (fun msg -> ctx.com.print (msg ^ "\n")) all; - did_something := true - ),"","print help for all compiler specific defines"); - ("Miscellaneous",["--help-metas"],[], Arg.Unit (fun() -> - let all,max_length = Meta.get_documentation_list() in - let all = List.map (fun (n,doc) -> Printf.sprintf " %-*s: %s" max_length n (limit_string doc (max_length + 3))) all in - List.iter (fun msg -> ctx.com.print (msg ^ "\n")) all; - did_something := true - ),"","print help for all compiler metadatas"); - ] in - let adv_args_spec = [ - ("Optimization",["--dce"],["-dce"],Arg.String (fun mode -> - (match mode with - | "std" | "full" | "no" -> () - | _ -> raise (Arg.Bad "Invalid DCE mode, expected std | full | no")); - Common.define_value com Define.Dce mode - ),"[std|full|no]","set the dead code elimination mode (default std)"); - ("Target-specific",["--swf-version"],["-swf-version"],Arg.Float (fun v -> - if not !swf_version || com.flash_version < v then com.flash_version <- v; - swf_version := true; - ),"","change the SWF version"); - (* FIXME: replace with -D define *) - ("Target-specific",["--swf-header"],["-swf-header"],Arg.String (fun h -> - try - swf_header := Some (match ExtString.String.nsplit h ":" with - | [width; height; fps] -> - (int_of_string width,int_of_string height,float_of_string fps,0xFFFFFF) - | [width; height; fps; color] -> - let color = if ExtString.String.starts_with color "0x" then color else "0x" ^ color in - (int_of_string width, int_of_string height, float_of_string fps, int_of_string color) - | _ -> raise Exit) - with - _ -> raise (Arg.Bad "Invalid SWF header format, expected width:height:fps[:color]") - ),"
","define SWF header (width:height:fps:color)"); - ("Target-specific",["--flash-strict"],[], define Define.FlashStrict, "","more type strict flash API"); - ("Target-specific",["--swf-lib"],["-swf-lib"],Arg.String (fun file -> - process_libs(); (* linked swf order matters, and lib might reference swf as well *) - add_native_lib file false; - ),"","add the SWF library to the compiled SWF"); - (* FIXME: replace with -D define *) - ("Target-specific",["--swf-lib-extern"],["-swf-lib-extern"],Arg.String (fun file -> - add_native_lib file true; - ),"","use the SWF library for type checking"); - ("Target-specific",["--java-lib"],["-java-lib"],Arg.String (fun file -> - add_native_lib file false; - ),"","add an external JAR or directory of JAR files"); - ("Target-specific",["--java-lib-extern"],[],Arg.String (fun file -> - add_native_lib file true; - ),"","use an external JAR or directory of JAR files for type checking"); - ("Target-specific",["--net-lib"],["-net-lib"],Arg.String (fun file -> - add_native_lib file false; - ),"[@std]","add an external .NET DLL file"); - ("Target-specific",["--net-std"],["-net-std"],Arg.String (fun file -> - Dotnet.add_net_std com file - ),"","add a root std .NET DLL search path"); - (* FIXME: replace with -D define *) - ("Target-specific",["--c-arg"],["-c-arg"],Arg.String (fun arg -> - com.c_args <- arg :: com.c_args - ),"","pass option to the native Java/C# compiler"); - ("Compilation",["-r";"--resource"],["-resource"],Arg.String (fun res -> - let file, name = (match ExtString.String.nsplit res "@" with - | [file; name] -> file, name - | [file] -> file, file - | _ -> raise (Arg.Bad "Invalid Resource format, expected file@name") - ) in - let file = (try Common.find_file com file with Not_found -> file) in - let data = (try - let s = Std.input_file ~bin:true file in - if String.length s > 12000000 then raise Exit; - s; - with - | Sys_error _ -> failwith ("Resource file not found: " ^ file) - | _ -> failwith ("Resource '" ^ file ^ "' excess the maximum size of 12MB") - ) in - if Hashtbl.mem com.resources name then failwith ("Duplicate resource name " ^ name); - Hashtbl.add com.resources name data - ),"[@name]","add a named resource file"); - ("Debug",["--prompt"],["-prompt"], Arg.Unit (fun() -> prompt := true),"","prompt on error"); - ("Compilation",["--cmd"],["-cmd"], Arg.String (fun cmd -> - cmds := DisplayOutput.unquote cmd :: !cmds - ),"","run the specified command after successful compilation"); - (* FIXME: replace with -D define *) - ("Optimization",["--no-traces"],[], define Define.NoTraces, "","don't compile trace calls in the program"); - ("Batch",["--next"],[], Arg.Unit (fun() -> die "" __LOC__), "","separate several haxe compilations"); - ("Batch",["--each"],[], Arg.Unit (fun() -> die "" __LOC__), "","append preceding parameters to all Haxe compilations separated by --next"); - ("Services",["--display"],[], Arg.String (fun input -> - let input = String.trim input in - if String.length input > 0 && (input.[0] = '[' || input.[0] = '{') then begin - did_something := true; - force_typing := true; - DisplayJson.parse_input com input Timer.measure_times - end else - DisplayOutput.handle_display_argument com input pre_compilation did_something; - ),"","display code tips"); - ("Services",["--xml"],["-xml"],Arg.String (fun file -> - xml_out := Some file - ),"","generate XML types description"); - ("Services",["--json"],[],Arg.String (fun file -> - json_out := Some file - ),"","generate JSON types description"); - ("Optimization",["--no-output"],[], Arg.Unit (fun() -> no_output := true),"","compiles but does not generate any file"); - ("Debug",["--times"],[], Arg.Unit (fun() -> Timer.measure_times := true),"","measure compilation times"); - ("Optimization",["--no-inline"],[], define Define.NoInline, "","disable inlining"); - ("Optimization",["--no-opt"],[], Arg.Unit (fun() -> - com.foptimize <- false; - Common.define com Define.NoOpt; - ), "","disable code optimizations"); - ("Compilation",["--remap"],[], Arg.String (fun s -> - let pack, target = (try ExtString.String.split s ":" with _ -> raise (Arg.Bad "Invalid remap format, expected source:target")) in - com.package_rules <- PMap.add pack (Remap target) com.package_rules; - ),"","remap a package to another one"); - ("Compilation",["--macro"],[], Arg.String (fun e -> - force_typing := true; - config_macros := e :: !config_macros - ),"","call the given macro before typing anything else"); - ("Compilation Server",["--server-listen"],["--wait"], Arg.String (fun hp -> - let accept = match hp with - | "stdio" -> - Server.init_wait_stdio() - | _ -> - let host, port = parse_host_port hp in - init_wait_socket host port - in - wait_loop process_params com.verbose accept - ),"[[host:]port]|stdio]","wait on the given port (or use standard i/o) for commands to run"); - ("Compilation Server",["--server-connect"],[], Arg.String (fun hp -> - let host, port = parse_host_port hp in - let accept = Server.init_wait_connect host port in - wait_loop process_params com.verbose accept - ),"[host:]port]","connect to the given port and wait for commands to run"); - ("Compilation Server",["--connect"],[],Arg.String (fun _ -> - die "" __LOC__ - ),"<[host:]port>","connect on the given port and run commands there"); - ("Compilation",["-C";"--cwd"],[], Arg.String (fun dir -> - (* This is handled by process_params, but passed through so we know we did something. *) - did_something := true; - ),"","set current working directory"); - ("Compilation",["--haxelib-global"],[], Arg.Unit (fun () -> ()),"","pass --global argument to haxelib"); - ] in - let args_callback cl = - begin try - let path,name = Path.parse_path cl in - if StringHelper.starts_uppercase_identifier name then - classes := (path,name) :: !classes - else begin - force_typing := true; - config_macros := (Printf.sprintf "include('%s', true, null, null, true)" cl) :: !config_macros; - end - with Failure _ when ctx.com.display.dms_display -> - () - end - in - let all_args = (basic_args_spec @ adv_args_spec) in - let all_args_spec = process_args all_args in - let process args = - let current = ref 0 in - (try - let rec loop acc args = match args with - | "--display" :: arg :: args -> - loop (arg :: "--display" :: acc) args - | arg :: args -> - loop (expand_env arg :: acc) args - | [] -> - List.rev acc - in - let args = loop [] args in - Arg.parse_argv ~current (Array.of_list ("" :: args)) all_args_spec args_callback ""; - with - | Arg.Help _ -> - raise (HelpMessage (usage_string all_args usage)) - | Arg.Bad msg -> - let first_line = List.nth (Str.split (Str.regexp "\n") msg) 0 in - let new_msg = (Printf.sprintf "%s" first_line) in - let r = Str.regexp "unknown option [`']?\\([-A-Za-z]+\\)[`']?" in - try - ignore(Str.search_forward r msg 0); - let s = Str.matched_group 1 msg in - let sl = List.map (fun (s,_,_) -> s) all_args_spec in - let sl = StringError.get_similar s sl in - begin match sl with - | [] -> raise Not_found - | _ -> - let spec = List.filter (fun (_,sl',sl'',_,_,_) -> - List.exists (fun s -> List.mem s sl) (sl' @ sl'') - ) all_args in - let new_msg = (Printf.sprintf "%s\nDid you mean:\n%s" first_line (usage_string ~print_cat:false spec "")) in - raise (Arg.Bad new_msg) - end; - with Not_found -> - raise (Arg.Bad new_msg)); - if com.platform = Globals.Cpp && not (Define.defined com.defines DisableUnicodeStrings) && not (Define.defined com.defines HxcppSmartStings) then begin - Define.define com.defines HxcppSmartStings; - end; - if Define.raw_defined com.defines "gen_hx_classes" then begin - (* TODO: this is something we're gonna remove once we have something nicer for generating flash externs *) - force_typing := true; - pre_compilation := (fun() -> - let process_lib lib = - if not (lib#has_flag NativeLibraries.FlagIsStd) then - List.iter (fun path -> if path <> (["java";"lang"],"String") then classes := path :: !classes) lib#list_modules - in - List.iter process_lib com.native_libs.net_libs; - List.iter process_lib com.native_libs.swf_libs; - List.iter process_lib com.native_libs.java_libs; - ) :: !pre_compilation; - xml_out := Some "hx" - end; - in - process_ref := process; - (* Handle CLI arguments *) - process ctx.com.args; - (* Process haxelibs *) - process_libs(); - (* Set up display configuration *) - process_display_configuration ctx; - let display_file_dot_path = DisplayOutput.process_display_file com classes in - (* Initialize target: This allows access to the appropriate std packages and sets the -D defines. *) - let ext = Initialize.initialize_target ctx com classes in - (* if we are at the last compilation step, allow all packages accesses - in case of macros or opening another project file *) - if com.display.dms_display then begin match com.display.dms_kind with - | DMDefault | DMUsage _ -> () - | _ -> if not ctx.has_next then com.package_rules <- PMap.foldi (fun p r acc -> match r with Forbidden -> acc | _ -> PMap.add p r acc) com.package_rules PMap.empty; - end; - com.config <- get_config com; (* make sure to adapt all flags changes defined after platform *) - let t = Timer.timer ["init"] in - List.iter (fun f -> f()) (List.rev (!pre_compilation)); - t(); - com.stage <- CInitialized; - if !classes = [([],"Std")] && not !force_typing then begin - if !cmds = [] && not !did_something then raise (HelpMessage (usage_string basic_args_spec usage)); - end else begin - (* Actual compilation starts here *) - let tctx = create_typer_context ctx !native_libs in - com.stage <- CTyperCreated; - let display_file_dot_path = match display_file_dot_path with - | DPKMacro path -> - ignore(load_display_module_in_macro tctx (Some path) true); - Some path - | DPKNormal path -> - Some path - | DPKNone -> - None - | DPKDirect file -> - DisplayOutput.load_display_file_standalone tctx file; - None - | DPKInput input -> - DisplayOutput.load_display_content_standalone tctx input; - None - in - begin try - do_type tctx !config_macros !classes; - with TypeloadParse.DisplayInMacroBlock -> - ignore(load_display_module_in_macro tctx display_file_dot_path true); - end; - handle_display ctx tctx display_file_dot_path; - filter ctx tctx display_file_dot_path; - if ctx.has_error then raise Abort; - check_auxiliary_output com !xml_out !json_out; - com.stage <- CGenerationStart; - if not !no_output then generate tctx ext !interp !jvm_flag !swf_header; - com.stage <- CGenerationDone; - end; - Sys.catch_break false; - List.iter (fun f -> f()) (List.rev com.callbacks#get_after_generation); - if not !no_output then begin - List.iter (fun c -> - let r = run_command ctx c in - if r <> 0 then failwith ("Command failed with error " ^ string_of_int r) - ) (List.rev !cmds) - end -with - | Abort -> - () - | Error.Fatal_error (m,p) -> - error ctx m p - | Common.Abort (m,p) -> - error ctx m p - | Lexer.Error (m,p) -> - error ctx (Lexer.error_msg m) p - | Parser.Error (m,p) -> - error ctx (Parser.error_msg m) p - | Typecore.Forbid_package ((pack,m,p),pl,pf) -> - if !Parser.display_mode <> DMNone && ctx.has_next then begin - ctx.has_error <- false; - ctx.messages <- []; - end else begin - error ctx (Printf.sprintf "You cannot access the %s package while %s (for %s)" pack (if pf = "macro" then "in a macro" else "targeting " ^ pf) (s_type_path m) ) p; - List.iter (error ctx (Error.compl_msg "referenced here")) (List.rev pl); - end - | Error.Error (m,p) -> - error ctx (Error.error_msg m) p - | Generic.Generic_Exception(m,p) -> - error ctx m p - | Arg.Bad msg -> - error ctx ("Error: " ^ msg) null_pos - | Failure msg when not is_debug_run -> - error ctx ("Error: " ^ msg) null_pos - | HelpMessage msg -> - com.info msg null_pos - | DisplayException(DisplayHover _ | DisplayPositions _ | DisplayFields _ | DisplayPackage _ | DisplaySignatures _ as de) when ctx.com.json_out <> None -> - begin - DisplayPosition.display_position#reset; - match ctx.com.json_out with - | Some api -> - let ctx = DisplayJson.create_json_context api.jsonrpc (match de with DisplayFields _ -> true | _ -> false) in - api.send_result (DisplayException.to_json ctx de) - | _ -> die "" __LOC__ - end - (* | Parser.TypePath (_,_,_,p) when ctx.com.json_out <> None -> - begin match com.json_out with - | Some (f,_) -> - let tctx = Typer.create ctx.com in - let fields = DisplayToplevel.collect tctx true Typecore.NoValue in - let jctx = Genjson.create_context Genjson.GMMinimum in - f (DisplayException.fields_to_json jctx fields CRImport (Some (Parser.cut_pos_at_display p)) false) - | _ -> die "" __LOC__ - end *) - | DisplayException(DisplayPackage pack) -> - DisplayPosition.display_position#reset; - raise (DisplayOutput.Completion (String.concat "." pack)) - | DisplayException(DisplayFields Some r) -> - DisplayPosition.display_position#reset; - let fields = if !Timer.measure_times then begin - Timer.close_times(); - (List.map (fun (name,value) -> - CompletionItem.make_ci_timer ("@TIME " ^ name) value - ) (DisplayOutput.get_timer_fields !start_time)) @ r.fitems - end else - r.fitems - in - let s = match r.fkind with - | CRToplevel _ - | CRTypeHint - | CRExtends - | CRImplements - | CRStructExtension _ - | CRImport - | CRUsing - | CRNew - | CRPattern _ - | CRTypeRelation - | CRTypeDecl -> - DisplayOutput.print_toplevel fields - | CRField _ - | CRStructureField - | CRMetadata - | CROverride -> - DisplayOutput.print_fields fields - in - raise (DisplayOutput.Completion s) - | DisplayException(DisplayHover Some ({hitem = {CompletionItem.ci_type = Some (t,_)}} as hover)) -> - DisplayPosition.display_position#reset; - let doc = CompletionItem.get_documentation hover.hitem in - raise (DisplayOutput.Completion (DisplayOutput.print_type t hover.hpos doc)) - | DisplayException(DisplaySignatures Some (signatures,_,display_arg,_)) -> - DisplayPosition.display_position#reset; - if ctx.com.display.dms_kind = DMSignature then - raise (DisplayOutput.Completion (DisplayOutput.print_signature signatures display_arg)) - else - raise (DisplayOutput.Completion (DisplayOutput.print_signatures signatures)) - | DisplayException(DisplayPositions pl) -> - DisplayPosition.display_position#reset; - raise (DisplayOutput.Completion (DisplayOutput.print_positions pl)) - | Parser.TypePath (p,c,is_import,pos) -> - let fields = - try begin match c with - | None -> - DisplayPath.TypePathHandler.complete_type_path com p - | Some (c,cur_package) -> - let ctx = Typer.create com in - DisplayPath.TypePathHandler.complete_type_path_inner ctx p c cur_package is_import - end with Common.Abort(msg,p) -> - error ctx msg p; - None - in - begin match ctx.com.json_out,fields with - | None,None -> - () - | None,Some fields -> - raise (DisplayOutput.Completion (DisplayOutput.print_fields fields)) - | Some api,None when is_legacy_completion com -> - api.send_result JNull - | Some api,fields -> - let fields = Option.default [] fields in - let ctx = DisplayJson.create_json_context api.jsonrpc false in - let path = match List.rev p with - | name :: pack -> List.rev pack,name - | [] -> [],"" - in - let kind = CRField ((CompletionItem.make_ci_module path,pos,None,None)) in - api.send_result (DisplayException.fields_to_json ctx fields kind (DisplayTypes.make_subject None pos)); - end - | Parser.SyntaxCompletion(kind,subj) -> - DisplayOutput.handle_syntax_completion com kind subj; - error ctx ("Error: No completion point was found") null_pos - | DisplayException(DisplayDiagnostics dctx) -> - let s = Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics dctx) in - DisplayPosition.display_position#reset; - raise (DisplayOutput.Completion s) - | DisplayException(ModuleSymbols s | Statistics s | Metadata s) -> - DisplayPosition.display_position#reset; - raise (DisplayOutput.Completion s) - | EvalExceptions.Sys_exit i | Hlinterp.Sys_exit i -> - ctx.flush(); - if !Timer.measure_times then Timer.report_times prerr_endline; - exit i - | DisplayOutput.Completion _ as exc -> - raise exc - | Out_of_memory as exc -> - raise exc - | e when (try Sys.getenv "OCAMLRUNPARAM" <> "b" || CompilationServer.runs() with _ -> true) && not is_debug_run -> - error ctx (Printexc.to_string e) null_pos - -;; -let other = Timer.timer ["other"] in +let other = Timer.timer ["other"];; Sys.catch_break true; -MacroContext.setup(); let args = List.tl (Array.to_list Sys.argv) in -(try - let server = Sys.getenv "HAXE_COMPILATION_SERVER" in - let host, port = (try ExtString.String.split server ":" with _ -> "127.0.0.1", server) in - do_connect host (try int_of_string port with _ -> failwith "Invalid HAXE_COMPILATION_SERVER port") args -with Not_found -> try - process_params create_context args -with DisplayOutput.Completion c -> - prerr_endline c; - exit 0 -| Arg.Bad msg -> - prerr_endline ("Error: " ^ msg); - exit 1 -); -other(); -if !Timer.measure_times then Timer.report_times prerr_endline +set_binary_mode_out stdout true; +set_binary_mode_out stderr true; +let sctx = ServerCompilationContext.create false in +Server.process sctx (Communication.create_stdio ()) args; +other() diff --git a/src/compiler/helper.ml b/src/compiler/helper.ml new file mode 100644 index 00000000..26e482ed --- /dev/null +++ b/src/compiler/helper.ml @@ -0,0 +1,57 @@ +exception HelpMessage of string + +let is_debug_run = try Sys.getenv "HAXEDEBUG" = "1" with _ -> false + +let start_time = ref (Timer.get_time()) + +let prompt = ref false + +let expand_env ?(h=None) path = + let r = Str.regexp "%\\([A-Za-z0-9_]+\\)%" in + Str.global_substitute r (fun s -> + let key = Str.matched_group 1 s in + try + Sys.getenv key + with Not_found -> try + match h with + | None -> raise Not_found + | Some h -> Hashtbl.find h key + with Not_found -> + "%" ^ key ^ "%" + ) path + +let unquote v = + let len = String.length v in + if len > 0 then + match v.[0], v.[len - 1] with + | '"', '"' + | '\'', '\'' -> String.sub v 1 (len - 2) + | _ -> v + else v + +let parse_hxml_data data = + let lines = Str.split (Str.regexp "[\r\n]+") data in + List.concat (List.map (fun l -> + let l = unquote (ExtString.String.strip l) in + if l = "" || l.[0] = '#' then + [] + else if l.[0] = '-' then + try + let a, b = ExtString.String.split l " " in + [unquote a; unquote (ExtString.String.strip b)] + with + _ -> [l] + else + [l] + ) lines) + +let parse_hxml file = + let ch = IO.input_channel (try open_in_bin file with _ -> raise Not_found) in + let data = IO.read_all ch in + IO.close_in ch; + parse_hxml_data data + +let parse_host_port hp = + let host, port = (try ExtString.String.split hp ":" with _ -> "127.0.0.1", hp) in + let port = try int_of_string port with _ -> raise (Arg.Bad "Invalid port") in + host, port \ No newline at end of file diff --git a/src/compiler/process_helper.ml b/src/compiler/process_helper.ml new file mode 100644 index 00000000..57074b93 --- /dev/null +++ b/src/compiler/process_helper.ml @@ -0,0 +1,151 @@ +open Unix + +(* path helpers *) +let as_exe name = + if Sys.unix then name else name ^ ".exe" + +let find_program name = + let name = as_exe name in + let pathKey = try Sys.getenv "Path" with Not_found -> "PATH" in + let path = try Sys.getenv pathKey with Not_found -> "" in + let pathComponents = Str.split (Str.regexp (if Sys.unix then ":" else ";")) path in + let sep = if Sys.unix then "/" else "\\" in + if Sys.file_exists (Sys.getcwd() ^ sep ^ name) then + Sys.getcwd() ^ sep ^ name + else + let indir = List.find (fun dir -> Sys.file_exists (dir ^ sep ^ name)) pathComponents in + indir ^ sep ^ name +(* end path helpers *) + +(* + ocaml<4.08/4.12 compat + https://github.com/ocaml/ocaml/blob/4.08/otherlibs/unix/unix.ml + open_process_args_in + open_process_args_out + open_process_args + open_process_args_full + The _pid part of the function names, as well as the pid argument, + are not needed in the real Unix functions present in 4.08 + If ocaml >=4.08 but <4.12 is used, the path lookup should still be + performed, as this isn't performed by the ocaml function until 4.12. +*) +let open_process_args_in_pid prog args = + let prog = try find_program prog with Not_found -> prog in + let (in_read, in_write) = pipe ~cloexec:true () in + let inchan = in_channel_of_descr in_read in + let pid = + begin + try + create_process prog args stdin in_write stderr + with e -> + close_in inchan; + close in_write; + raise e + end in + close in_write; + (inchan, pid) + +let open_process_args_out_pid prog args = + let prog = try find_program prog with Not_found -> prog in + let (out_read, out_write) = pipe ~cloexec:true () in + let outchan = out_channel_of_descr out_write in + let pid = + begin + try + create_process prog args out_read stdout stderr + with e -> + close_out outchan; + close out_read; + raise e + end in + close out_read; + (outchan, pid) + +let open_process_args_pid prog args = + let prog = try find_program prog with Not_found -> prog in + let (in_read, in_write) = pipe ~cloexec:true () in + let (out_read, out_write) = + try pipe ~cloexec:true () + with e -> + close in_read; close in_write; + raise e in + let inchan = in_channel_of_descr in_read in + let outchan = out_channel_of_descr out_write in + let pid = + begin + try + create_process prog args out_read in_write stderr + with e -> + close out_read; close out_write; + close in_read; close in_write; + raise e + end in + close out_read; + close in_write; + (inchan, outchan, pid) + +let open_process_args_full_pid prog args env = + let prog = try find_program prog with Not_found -> prog in + let (in_read, in_write) = pipe ~cloexec:true () in + let (out_read, out_write) = + try pipe ~cloexec:true () + with e -> + close in_read; close in_write; + raise e in + let (err_read, err_write) = + try pipe ~cloexec:true () + with e -> + close in_read; close in_write; + close out_read; close out_write; + raise e in + let inchan = in_channel_of_descr in_read in + let outchan = out_channel_of_descr out_write in + let errchan = in_channel_of_descr err_read in + let pid = + begin + try + create_process_env prog args env out_read in_write err_write + with e -> + close out_read; close out_write; + close in_read; close in_write; + close err_read; close err_write; + raise e + end in + close out_read; + close in_write; + close err_write; + (inchan, outchan, errchan, pid) + +let rec waitpid_non_intr pid = + try waitpid [] pid + with Unix_error (EINTR, _, _) -> waitpid_non_intr pid + +let close_process_in_pid (inchan, pid) = + close_in inchan; + snd(waitpid_non_intr pid) + +let close_process_out_pid (outchan, pid) = + (* The application may have closed [outchan] already to signal + end-of-input to the process. *) + begin try close_out outchan with Sys_error _ -> () end; + snd(waitpid_non_intr pid) + +let close_process_pid (inchan, outchan, pid) = + close_in inchan; + begin try close_out outchan with Sys_error _ -> () end; + snd(waitpid_non_intr pid) + +let close_process_full_pid (inchan, outchan, errchan, pid) = + close_in inchan; + begin try close_out outchan with Sys_error _ -> () end; + close_in errchan; + snd(waitpid_non_intr pid) +(* end ocaml<4.08/4.12 compat *) + +let command cmd args = + let args = Array.of_list (cmd::args) in + let pin, pout, pid = open_process_args_pid cmd args in + let ret = close_process_pid (pin,pout,pid) in + match ret with + | Unix.WEXITED code -> code + | _ -> 255 \ No newline at end of file diff --git a/src/compiler/retyper.ml b/src/compiler/retyper.ml new file mode 100644 index 00000000..c4988ebb --- /dev/null +++ b/src/compiler/retyper.ml @@ -0,0 +1,279 @@ +open Globals +open Ast +open Typecore +open Type +open TypeloadModule +open TypeloadFields + +exception Fail of string + +type retyping_context = { + typer : typer; + print_stack : string list; +} + +let fail rctx s = + let stack = String.concat " " (List.rev rctx.print_stack) in + raise (Fail (Printf.sprintf "%s: %s" stack s)) + +let disable_typeloading rctx ctx f = + let old = ctx.g.load_only_cached_modules in + ctx.g.load_only_cached_modules <- true; + try + Std.finally (fun () -> ctx.g.load_only_cached_modules <- old) f () + with (Error.Error (Module_not_found path,_,_)) -> + fail rctx (Printf.sprintf "Could not load [Module %s]" (s_type_path path)) + +let pair_type th t = match th with + | None -> + TExprToExpr.convert_type t,null_pos + | Some t -> + t + +let pair_class_field rctx ctx cctx fctx cf cff p = + match cff.cff_kind with + | FFun fd -> + let targs,tret = match follow cf.cf_type with + | TFun(args,ret) -> + args,ret + | _ -> + fail rctx "Type change" + in + let args = try + List.map2 (fun (name,opt,meta,th,eo) (_,_,t) -> + (name,opt,meta,Some (pair_type th t),eo) + ) fd.f_args targs + with Invalid_argument _ -> + fail rctx "Type change" + in + let ret = pair_type fd.f_type tret in + let fd = { + fd with + f_args = args; + f_type = Some ret + } in + let load_args_ret () = + setup_args_ret ctx cctx fctx (fst cff.cff_name) fd p + in + let args,ret = disable_typeloading rctx ctx load_args_ret in + let t = TFun(args#for_type,ret) in + (fun () -> + (* This is the only part that should actually modify anything. *) + cf.cf_type <- t; + TypeBinding.bind_method ctx cctx fctx cf t args ret fd.f_expr (match fd.f_expr with Some e -> snd e | None -> cff.cff_pos); + if ctx.com.display.dms_full_typing then + remove_class_field_flag cf CfPostProcessed; + ) + | FVar(th,eo) | FProp(_,_,th,eo) -> + let th = Some (pair_type th cf.cf_type) in + let t = disable_typeloading rctx ctx (fun () -> load_variable_type_hint ctx fctx eo (pos cff.cff_name) th) in + (fun () -> + cf.cf_type <- t; + TypeBinding.bind_var ctx cctx fctx cf eo; + if ctx.com.display.dms_full_typing then + remove_class_field_flag cf CfPostProcessed; + ) + +let pair_classes rctx context_init c d p = + let rctx = {rctx with + print_stack = (Printf.sprintf "[Class %s]" (s_type_path c.cl_path)) :: rctx.print_stack + } in + c.cl_restore(); + (* TODO: What do we do with build macros? *) + let cctx = create_class_context c context_init p in + let ctx = create_typer_context_for_class rctx.typer cctx p in + let _ = + let rctx = {rctx with + print_stack = (Printf.sprintf "[Relations]") :: rctx.print_stack + } in + let has_extends = ref false in + let implements = ref c.cl_implements in + List.iter (function + | HExtends(path,p) -> + has_extends := true; + begin match c.cl_super with + | None -> + fail rctx (Printf.sprintf "parent %s appeared" (Ast.Printer.s_complex_type_path "" (path,p))) + | Some(c,tl) -> + let th = pair_type (Some(CTPath path,p)) (TInst(c,tl)) in + ignore (disable_typeloading rctx ctx (fun () -> Typeload.load_complex_type ctx false th)) + end + | HImplements(path,p) -> + begin match !implements with + | (c,tl) :: rest -> + (* TODO: I think this should somehow check if it's actually the same interface. There could be cases + where the order changes or something like that... Maybe we can compare the loaded type. + However, this doesn't matter until we start retyping invalidated modules. + *) + implements := rest; + let th = pair_type (Some(CTPath path,p)) (TInst(c,tl)) in + ignore (disable_typeloading rctx ctx (fun () -> Typeload.load_complex_type ctx false th)); + | [] -> + fail rctx (Printf.sprintf "interface %s appeared" (Ast.Printer.s_complex_type_path "" (path,p))) + end + | _ -> + () + ) d.d_flags; + (* TODO: There are probably cases where the compiler generates a cl_super even though it's not in syntax *) + if not !has_extends then begin match c.cl_super with + | None -> () + | Some(c,_) -> fail rctx (Printf.sprintf "parent %s disappeared" (s_type_path c.cl_path)) + end; + begin match !implements with + | (c,_) :: _ -> fail rctx (Printf.sprintf "interface %s disappeared" (s_type_path c.cl_path)) + | [] -> () + end + in + let fl = List.map (fun cff -> + let name = fst cff.cff_name in + let rctx = {rctx with + print_stack = (Printf.sprintf "[Field %s]" name) :: rctx.print_stack + } in + let display_modifier = Typeload.check_field_access ctx cff in + let fctx = create_field_context cctx cff ctx.is_display_file display_modifier in + let cf = match fctx.field_kind with + | FKConstructor -> + begin match c.cl_constructor with + | None -> + fail rctx "Constructor not found" + | Some cf -> + cf + end + | FKNormal -> + begin try + PMap.find name (if fctx.is_static then c.cl_statics else c.cl_fields) + with Not_found -> + fail rctx "Field not found" + end + | FKInit -> + fail rctx "TODO" + in + pair_class_field rctx ctx cctx fctx cf cff p + ) d.d_data in + fl @ [fun () -> TypeloadFields.finalize_class ctx cctx] + +let pair_enums ctx rctx en d = + let ctx = { ctx with type_params = en.e_params } in + let rctx = {rctx with + print_stack = (Printf.sprintf "[Enum %s]" (s_type_path en.e_path)) :: rctx.print_stack + } in + List.iter (fun eff -> + let name = fst eff.ec_name in + let rctx = {rctx with + print_stack = (Printf.sprintf "[Field %s]" name) :: rctx.print_stack + } in + let ef = try + PMap.find name en.e_constrs + with Not_found -> + fail rctx "Field not found" + in + let th = pair_type eff.ec_type ef.ef_type in + ignore (disable_typeloading rctx ctx (fun () -> Typeload.load_complex_type ctx false th)) + ) d.d_data; + [] + +let pair_typedefs ctx rctx td d = + let rctx = {rctx with + print_stack = (Printf.sprintf "[Typedef %s]" (s_type_path td.t_path)) :: rctx.print_stack + } in + let ctx = { ctx with type_params = td.t_params } in + ignore (disable_typeloading rctx ctx (fun () -> Typeload.load_complex_type ctx false d.d_data)); + [] + +let pair_abstracts ctx rctx context_init a d p = + let rctx = {rctx with + print_stack = (Printf.sprintf "[Abstract %s]" (s_type_path a.a_path)) :: rctx.print_stack + } in + match a.a_impl with + | Some c -> + c.cl_restore(); + let cctx = create_class_context c context_init p in + let ctx = create_typer_context_for_class rctx.typer cctx p in + let fl = List.map (fun cff -> + let cff = TypeloadFields.transform_abstract_field2 ctx a cff in + let name = fst cff.cff_name in + let rctx = {rctx with + print_stack = (Printf.sprintf "[Field %s]" name) :: rctx.print_stack + } in + let display_modifier = Typeload.check_field_access ctx cff in + let fctx = create_field_context cctx cff ctx.is_display_file display_modifier in + let cf = try + PMap.find name c.cl_statics + with Not_found -> + fail rctx "Field not found" + in + pair_class_field rctx ctx cctx fctx cf cff p + ) d.d_data in + fl @ [fun () -> TypeloadFields.finalize_class ctx cctx] + | None -> + (* ?*) + [] + +let attempt_retyping ctx m p = + let com = ctx.com in + let file,_,_,decls = TypeloadParse.parse_module' com m.m_path p in + let ctx = create_typer_context_for_module ctx m in + let rctx = { + typer = ctx; + print_stack = [Printf.sprintf "[Module %s]" (s_type_path m.m_path)]; + } in + (* log rctx 0 (Printf.sprintf "Retyping module %s" (s_type_path m.m_path)); *) + let context_init = new TypeloadFields.context_init in + let find_type name = try + List.find (fun t -> snd (t_infos t).mt_path = name) ctx.m.curmod.m_types + with Not_found -> + fail rctx (Printf.sprintf "Type %s not found" name) + in + let rec loop acc decls = match decls with + | [] -> + List.rev acc + | (d,p) :: decls -> + begin match d with + | EImport (path,mode) -> + ImportHandling.init_import ctx context_init path mode p; + ImportHandling.commit_import ctx path mode p; + loop acc decls + | EUsing path -> + ImportHandling.init_using ctx context_init path p; + loop acc decls + | EClass c -> + let mt = find_type (fst c.d_name) in + loop ((d,mt) :: acc) decls + | EEnum en -> + let mt = find_type (fst en.d_name) in + loop ((d,mt) :: acc) decls + | ETypedef td -> + let mt = find_type (fst td.d_name) in + loop ((d,mt) :: acc) decls + | EAbstract a -> + let mt = find_type (fst a.d_name) in + loop ((d,mt) :: acc) decls + | _ -> + loop acc decls + end; + in + try + m.m_extra.m_cache_state <- MSUnknown; + let pairs = loop [] decls in + let fl = List.map (fun (d,mt) -> match d,mt with + | EClass d,TClassDecl c -> + pair_classes rctx context_init c d p + | EEnum d,TEnumDecl en -> + pair_enums ctx rctx en d + | ETypedef d,TTypeDecl td -> + pair_typedefs ctx rctx td d + | EAbstract d,TAbstractDecl a -> + pair_abstracts ctx rctx context_init a d p + | _ -> + fail rctx "?" + ) pairs in + (* If we get here we know that the everything is ok. *) + delay ctx PConnectField (fun () -> context_init#run); + List.iter (fun fl -> + List.iter (fun f -> f()) fl + ) fl; + m.m_extra.m_cache_state <- MSGood; + m.m_extra.m_time <- Common.file_time file; + None + with Fail s -> + Some s \ No newline at end of file diff --git a/src/compiler/server.ml b/src/compiler/server.ml index 326d9e23..6427c5d0 100644 --- a/src/compiler/server.ml +++ b/src/compiler/server.ml @@ -1,127 +1,47 @@ +open Extlib_leftovers open Printf open Globals open Ast open Common -open CompilationServer -open DisplayTypes.DisplayMode +open CompilationCache open Timer open Type -open DisplayOutput +open DisplayProcessingGlobals open Json +open Compiler +open CompilationContext -exception Dirty of path +exception Dirty of module_skip_reason exception ServerError of string -let prompt = ref false -let start_time = ref (Timer.get_time()) - -let is_debug_run = try Sys.getenv "HAXEDEBUG" = "1" with _ -> false - -type context = { - com : Common.context; - mutable flush : unit -> unit; - mutable setup : unit -> unit; - mutable messages : compiler_message list; - mutable has_next : bool; - mutable has_error : bool; -} +let has_error ctx = + ctx.has_error || ctx.com.Common.has_error let check_display_flush ctx f_otherwise = match ctx.com.json_out with | None -> - begin match ctx.com.display.dms_kind with - | DMDiagnostics _-> - List.iter (fun msg -> - let msg,p,kind = match msg with - | CMInfo(msg,p) -> msg,p,DisplayTypes.DiagnosticsSeverity.Information - | CMWarning(msg,p) -> msg,p,DisplayTypes.DiagnosticsSeverity.Warning - | CMError(msg,p) -> msg,p,DisplayTypes.DiagnosticsSeverity.Error - in - add_diagnostics_message ctx.com msg p DisplayTypes.DiagnosticsKind.DKCompilerError kind + if is_diagnostics ctx.com then begin + List.iter (fun cm -> + add_diagnostics_message ~depth:cm.cm_depth ctx.com (located cm.cm_message cm.cm_pos) cm.cm_kind cm.cm_severity ) (List.rev ctx.messages); raise (Completion (Diagnostics.print ctx.com)) - | _ -> + end else f_otherwise () - end | Some api -> - if ctx.has_error then begin - let errors = List.map (fun msg -> - let msg,p,i = match msg with - | CMInfo(msg,p) -> msg,p,3 - | CMWarning(msg,p) -> msg,p,2 - | CMError(msg,p) -> msg,p,1 - in + if has_error ctx then begin + let errors = List.map (fun cm -> JObject [ - "severity",JInt i; - "location",Genjson.generate_pos_as_location p; - "message",JString msg; + "severity",JInt (MessageSeverity.to_int cm.cm_severity); + "location",Genjson.generate_pos_as_location cm.cm_pos; + "message",JString cm.cm_message; ] ) (List.rev ctx.messages) in api.send_error errors end -let default_flush ctx = - check_display_flush ctx (fun () -> - List.iter - (fun msg -> match msg with - | CMInfo _ -> print_endline (compiler_message_string msg) - | CMWarning _ | CMError _ -> prerr_endline (compiler_message_string msg) - ) - (List.rev ctx.messages); - if ctx.has_error && !prompt then begin - print_endline "Press enter to exit..."; - ignore(read_line()); - end; - if ctx.has_error then exit 1 - ) - -let create_context params = - let ctx = { - com = Common.create version (s_version true) params; - flush = (fun()->()); - setup = (fun()->()); - messages = []; - has_next = false; - has_error = false; - } in - ctx.flush <- (fun() -> default_flush ctx); - ctx - -let parse_hxml_data data = - let lines = Str.split (Str.regexp "[\r\n]+") data in - List.concat (List.map (fun l -> - let l = unquote (ExtString.String.strip l) in - if l = "" || l.[0] = '#' then - [] - else if l.[0] = '-' then - try - let a, b = ExtString.String.split l " " in - [unquote a; unquote (ExtString.String.strip b)] - with - _ -> [l] - else - [l] - ) lines) - -let parse_hxml file = - let ch = IO.input_channel (try open_in_bin file with _ -> raise Not_found) in - let data = IO.read_all ch in - IO.close_in ch; - parse_hxml_data data - -let ssend sock str = - let rec loop pos len = - if len = 0 then - () - else - let s = Unix.send sock str pos len [] in - loop (pos + s) (len - s) - in - loop 0 (Bytes.length str) - let current_stdin = ref None let parse_file cs com file p = - let cc = CommonCache.get_cache cs com in + let cc = CommonCache.get_cache com in let ffile = Path.get_full_path file and fkey = com.file_keys#get file in let is_display_file = DisplayPosition.display_position#is_in_file (com.file_keys#get ffile) in @@ -151,7 +71,7 @@ let parse_file cs com file p = end else begin try (* We assume that when not in display mode it's okay to cache stuff that has #if display checks. The reasoning is that non-display mode has more information than display mode. *) - if not com.display.dms_display then raise Not_found; + if com.display.dms_full_typing then raise Not_found; let ident = Hashtbl.find Parser.special_identifier_files fkey in Printf.sprintf "not cached, using \"%s\" define" ident,true with Not_found -> @@ -164,54 +84,428 @@ let parse_file cs com file p = ) () in data -module ServerCompilationContext = struct - type t = { - (* If true, prints some debug information *) - verbose : bool; - (* The list of changed directories per-signature *) - changed_directories : (Digest.t,cached_directory list) Hashtbl.t; - (* A reference to the compilation server instance *) - cs : CompilationServer.t; - (* A list of class paths per-signature *) - class_paths : (Digest.t,string list) Hashtbl.t; - (* Increased for each typed module *) - mutable mark_loop : int; - (* Increased for each compilation *) - mutable compilation_step : int; - (* The [mark_loop] value at which we started the current compilation *) - mutable compilation_mark : int; - (* A list of delays which are run after compilation *) - mutable delays : (unit -> unit) list; - (* True if it's an actual compilation, false if it's a display operation *) - mutable was_compilation : bool; +open ServerCompilationContext + +module Communication = struct + type error_context = { + mutable last_positions : pos IntMap.t; + mutable max_lines : int IntMap.t; + mutable gutter : int IntMap.t; + mutable previous : (pos * MessageSeverity.t * int) option; } - let create verbose cs = { - verbose = verbose; - cs = cs; - class_paths = Hashtbl.create 0; - changed_directories = Hashtbl.create 0; - compilation_step = 0; - compilation_mark = 0; - mark_loop = 0; - delays = []; - was_compilation = false; + let create_error_context () = { + last_positions = IntMap.empty; + max_lines = IntMap.empty; + gutter = IntMap.empty; + previous = None; } - let add_delay sctx f = - sctx.delays <- f :: sctx.delays + let error_printer file line = Printf.sprintf "%s:%d:" file line - let run_delays sctx = - let fl = sctx.delays in - sctx.delays <- []; - List.iter (fun f -> f()) fl + let resolve_source file l1 p1 l2 p2 = + let ch = open_in_bin file in + let curline = ref 1 in + let lines = ref [] in + let rec loop p line = + let inc i line = + if (!curline >= l1) && (!curline <= l2) then lines := (!curline, line) :: !lines; + curline := !curline + 1; + (i, "") + in - let reset sctx = - Hashtbl.clear sctx.changed_directories; - sctx.was_compilation <- false -end + let input_char_or_done ch line = + try input_char ch with End_of_file -> begin + ignore(inc 0 line); + raise End_of_file + end + in -open ServerCompilationContext + try + let read_char line = match input_char_or_done ch line with + | '\n' -> inc 1 line + | '\r' -> + ignore(input_char_or_done ch line); + inc 2 line + | c -> begin + let line = ref (line ^ (String.make 1 c)) in + let rec skip n = + if n > 0 then begin + let c = input_char_or_done ch !line in + line := !line ^ (String.make 1 c); + skip (n - 1) + end + in + + let code = int_of_char c in + if code < 0xC0 then () + else if code < 0xE0 then skip 1 + else if code < 0xF0 then skip 2 + else skip 3; + + (1, !line) + end + in + + let (delta, line) = read_char line in + loop (p + delta) line + with End_of_file -> + close_in ch; + in + + loop 0 ""; + List.rev !lines + + let resolve_file ctx f = + let ext = Common.extension f in + let second_ext = Common.extension (Common.remove_extension f) in + let platform_ext = "." ^ (platform_name_macro ctx) in + if platform_ext = second_ext then + (Common.remove_extension (Common.remove_extension f)) ^ ext + else + f + + let compiler_pretty_message_string ctx ectx cm = + match cm.cm_message with + (* Filter some messages that don't add much when using this message renderer *) + | "End of overload failure reasons" -> None + | _ -> begin + ectx.last_positions <- (IntMap.add cm.cm_depth cm.cm_pos ectx.last_positions); + let is_null_pos = cm.cm_pos = null_pos || cm.cm_pos.pmin = -1 in + let is_unknown_file f = f = "" || f = "?" in + + (* Extract informations from position *) + let l1, p1, l2, p2, epos, lines = + if is_null_pos then begin + let epos = if is_unknown_file cm.cm_pos.pfile then "(unknown position)" else cm.cm_pos.pfile in + (-1, -1, -1, -1, epos, []) + end else begin + let f = resolve_file ctx.com cm.cm_pos.pfile in + let f = + try Common.find_file ctx.com f + with Not_found -> failwith ("File not found '" ^ cm.cm_pos.pfile ^ "'") + in + + let l1, p1, l2, p2 = Lexer.get_pos_coords cm.cm_pos in + let lines = resolve_source f l1 p1 l2 p2 in + let epos = Lexer.get_error_pos error_printer cm.cm_pos in + (l1, p1, l2, p2, epos, lines) + end in + + (* If 4 lines or less, display all; if more, crop the middle *) + let lines = match lines with + | _ :: (_ :: (_ :: (_ :: []))) -> lines + | hd :: (_ :: (_ :: (_ :: l))) -> + let _,line = hd in + let indent = ref 0 in + let found = ref false in + + while (not !found) && (!indent < (String.length line - 1)) do + found := not (Lexer.is_whitespace (String.unsafe_get line !indent)); + indent := !indent + 1 + done; + + [hd; (0, (String.make (!indent+1) ' ') ^ "[...]"); List.hd (List.rev l)] + | _ -> lines + in + + let parent_pos = + if cm.cm_depth = 0 then null_pos + else (try IntMap.find (cm.cm_depth-1) ectx.last_positions with Not_found -> null_pos) + in + + let prev_pos,prev_sev,prev_nl = match ectx.previous with + | None -> (None, None, 0) + | Some (p, sev, depth) -> (Some p, Some sev, depth) + in + + let sev_changed = prev_sev = None || Some cm.cm_severity <> prev_sev in + let pos_changed = (prev_pos = None || cm.cm_pos <> Option.get prev_pos || (cm.cm_depth <> prev_nl && cm.cm_depth <> prev_nl + 1)) && (parent_pos = null_pos || cm.cm_pos <> parent_pos) in + let file_changed = prev_pos = None || (pos_changed && match (cm.cm_pos.pfile, (Option.get prev_pos).pfile) with + | (f1, f2) when (is_unknown_file f1) && (is_unknown_file f2) -> false + | (f1, f2) -> f1 <> f2 + ) in + + let display_heading = cm.cm_depth = 0 || sev_changed || file_changed in + let display_source = cm.cm_depth = 0 || sev_changed || pos_changed in + let display_pos_marker = (not is_null_pos) && (cm.cm_depth = 0 || sev_changed || pos_changed) in + + let gutter_len = (try String.length (Printf.sprintf "%d" (IntMap.find cm.cm_depth ectx.max_lines)) with Not_found -> 0) + 2 in + + let no_color = Define.defined ctx.com.defines Define.NoColor in + let c_reset = if no_color then "" else "\x1b[0m" in + let c_bold = if no_color then "" else "\x1b[1m" in + let c_dim = if no_color then "" else "\x1b[2m" in + + let (c_sev, c_sev_bg) = if no_color then ("", "") else match cm.cm_severity with + | MessageSeverity.Warning -> ("\x1b[33m", "\x1b[30;43m") + | Information | Hint -> ("\x1b[34m", "\x1b[30;44m") + | Error -> ("\x1b[31m", "\x1b[30;41m") + in + + let sev_label = if cm.cm_depth > 0 then " -> " else Printf.sprintf + (if no_color then "[%s]" else " %s ") + (match cm.cm_severity with + | MessageSeverity.Warning -> "WARNING" + | Information -> "INFO" + | Hint -> "HINT" + | Error -> "ERROR" + ) in + + let out = ref "" in + + if display_heading then + out := Printf.sprintf "%s%s\n\n" + (* Severity heading *) + (c_sev_bg ^ sev_label ^ c_reset ^ " ") + (* File + line pointer *) + epos; + + (* Error source *) + if display_source then out := List.fold_left (fun out (l, line) -> + let nb_len = String.length (string_of_int l) in + + (* Replace tabs with 1 space to avoid column misalignments *) + let line = String.concat " " (ExtString.String.nsplit line "\t") in + let len = String.length line in + + out ^ Printf.sprintf "%s%s | %s\n" + (* left-padded line number *) + (String.make (gutter_len-nb_len-1) ' ') + (if l = 0 then "-" else Printf.sprintf "%d" l) + (* Source code at that line *) + ( + if l = 0 then + c_dim ^ line ^ c_reset + else if l1 = l2 then + (if p1 > 1 then c_dim ^ (String.sub line 0 (p1-1)) else "") + ^ c_reset ^ c_bold ^ (String.sub line (p1-1) (p2-p1)) + ^ c_reset ^ c_dim ^ (String.sub line (p2-1) (len - p2 + 1)) + ^ c_reset + else begin + (if (l = l1) then + (if p1 > 1 then c_dim ^ (String.sub line 0 (p1-1)) else "") + ^ c_reset ^ c_bold ^ (String.sub line (p1-1) (len-p1+1)) + ^ c_reset + else if (l = l2) then + (if p2 > 1 then c_bold ^ (String.sub line 0 (p2-1)) else "") + ^ c_reset ^ c_dim ^ (String.sub line (p2-1) (len-p2+1)) + ^ c_reset + else c_bold ^ line ^ c_reset) + end + ) + ) !out lines; + + (* Error position marker *) + if display_pos_marker then + out := Printf.sprintf "%s%s|%s\n" + !out + (String.make gutter_len ' ') + (if l1 = l2 then String.make p1 ' ' ^ c_sev ^ String.make (if p1 = p2 then 1 else p2-p1) '^' ^ c_reset else ""); + + (* Error message *) + out := List.fold_left (fun out str -> Printf.sprintf "%s%s| %s\n" + out + (String.make gutter_len ' ') + (* Remove "... " prefix *) + (if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str) + ) !out (ExtString.String.nsplit cm.cm_message "\n"); + + ectx.previous <- Some ((if is_null_pos then null_pos else cm.cm_pos), cm.cm_severity, cm.cm_depth); + ectx.gutter <- (IntMap.add cm.cm_depth gutter_len ectx.gutter); + + (* Indent sub errors *) + let rec indent ?(acc=0) depth = + if depth = 0 then acc + else indent ~acc:(acc + try IntMap.find (depth-1) ectx.gutter with Not_found -> 3) (depth-1) + in + + Some ( + if cm.cm_depth > 0 then String.concat "\n" (List.map (fun str -> match str with + | "" -> "" + | _ -> (String.make (indent cm.cm_depth) ' ') ^ str + ) (ExtString.String.nsplit !out "\n")) + else !out + ) + end + + let compiler_message_string ctx ectx cm = + let str = match cm.cm_severity with + | MessageSeverity.Warning -> "Warning : " ^ cm.cm_message + | Information | Error | Hint -> cm.cm_message + in + + if cm.cm_pos = null_pos then + Some str + else begin + let epos = Lexer.get_error_pos error_printer cm.cm_pos in + let str = + let lines = + match (ExtString.String.nsplit str "\n") with + | first :: rest -> first :: List.map Error.compl_msg rest + | l -> l + in + String.concat ("\n" ^ epos ^ " : ") lines + in + Some (Printf.sprintf "%s : %s" epos str) + end + + let compiler_indented_message_string ctx ectx cm = + match cm.cm_message with + (* Filter some messages that don't add much when using this message renderer *) + | "End of overload failure reasons" -> None + | _ -> + let str = match cm.cm_severity with + | MessageSeverity.Warning -> "Warning : " ^ cm.cm_message + | Information -> "Info : " ^ cm.cm_message + | Error | Hint -> cm.cm_message + in + + if cm.cm_pos = null_pos then + Some str + else begin + let epos = Lexer.get_error_pos error_printer cm.cm_pos in + let lines = + match (ExtString.String.nsplit str "\n") with + | first :: rest -> (cm.cm_depth, first) :: List.map (fun msg -> (cm.cm_depth+1, msg)) rest + | l -> [(cm.cm_depth, List.hd l)] + in + let rm_prefix str = if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str in + Some (String.concat "\n" (List.map (fun (depth, msg) -> (String.make (depth*2) ' ') ^ epos ^ " : " ^ (rm_prefix msg)) lines)) + end + + let get_max_line max_lines messages = + List.fold_left (fun max_lines cm -> + let _,_,l2,_ = Lexer.get_pos_coords cm.cm_pos in + let old = try IntMap.find cm.cm_depth max_lines with Not_found -> 0 in + + if l2 > old then IntMap.add cm.cm_depth l2 max_lines + else max_lines + ) max_lines messages + + let display_messages ctx on_message = begin + let ectx = create_error_context () in + ectx.max_lines <- get_max_line ectx.max_lines ctx.messages; + + let get_formatter def default = + let format_mode = Define.defined_value_safe ~default ctx.com.defines def in + match format_mode with + | "pretty" -> compiler_pretty_message_string ctx ectx + | "indent" -> compiler_indented_message_string ctx ectx + | "classic" -> compiler_message_string ctx ectx + | m -> begin + let def = Define.get_define_key def in + error ctx (Printf.sprintf "Invalid message reporting mode: \"%s\", expected classic | pretty | indent (for -D %s)." m def) null_pos; + compiler_message_string ctx ectx + end + in + + let message_formatter = get_formatter Define.MessageReporting "classic" in + let log_formatter = get_formatter Define.MessagesLogFormat "indent" in + + let log_messages = ref (Define.defined ctx.com.defines Define.MessagesLogFile) in + let log_message = ref None in + let close_logs = ref None in + + if !log_messages then begin + try begin + let buf = Rbuffer.create 16000 in + + let file = Define.defined_value ctx.com.defines Define.MessagesLogFile in + let chan = + Path.mkdir_from_path file; + open_out_bin file + in + + log_message := (Some (fun msg -> + match (log_formatter msg) with + | None -> () + | Some str -> Rbuffer.add_string buf (str ^ "\n"))); + + close_logs := (Some (fun () -> + Rbuffer.output_buffer chan buf; + Rbuffer.clear buf; + close_out chan + )); + end with + | Failure e | Sys_error e -> begin + let def = Define.get_define_key Define.MessagesLogFile in + error ctx (Printf.sprintf "Error opening log file: %s. Logging to file disabled (-D %s)" e def) null_pos; + log_messages := false; + end + end; + + List.iter (fun cm -> + if !log_messages then (Option.get !log_message) cm; + + match (message_formatter cm) with + | None -> () + | Some str -> on_message cm.cm_severity str + ) (List.rev ctx.messages); + + if !log_messages then (Option.get !close_logs) (); + end + + let create_stdio () = + let rec self = { + write_out = (fun s -> + print_string s; + flush stdout; + ); + write_err = (fun s -> + prerr_string s; + ); + flush = (fun ctx -> + display_messages ctx (fun sev output -> + match sev with + | MessageSeverity.Information -> print_endline output + | Warning | Error | Hint -> prerr_endline output + ); + + if has_error ctx && !Helper.prompt then begin + print_endline "Press enter to exit..."; + ignore(read_line()); + end; + flush stdout; + ); + exit = (fun code -> + if code = 0 then begin + Timer.close_times(); + if !Timer.measure_times then Timer.report_times (fun s -> self.write_err (s ^ "\n")); + end; + exit code; + ); + is_server = false; + } in + self + + let create_pipe sctx write = { + write_out = (fun s -> + write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit s "\n") ^ "\n") + ); + write_err = (fun s -> + write s + ); + flush = (fun ctx -> + check_display_flush ctx (fun () -> + display_messages ctx (fun _ output -> + write (output ^ "\n"); + ServerMessage.message output; + ); + + sctx.was_compilation <- ctx.com.display.dms_full_typing; + if has_error ctx then begin + measure_times := false; + write "\x02\n" + end + ) + ); + exit = (fun i -> + () + ); + is_server = true; + } +end let stat dir = (Unix.stat (Path.remove_trailing_slash dir)).Unix.st_mtime @@ -239,10 +533,10 @@ let get_changed_directories sctx (ctx : Typecore.typer) = if not (cs#has_directory sign dir) then begin let time = stat dir in ServerMessage.added_directory com "" dir; - cs#add_directory sign (CompilationServer.create_directory dir time) + cs#add_directory sign (CompilationCache.create_directory dir time) end; ) sub_dirs; - (CompilationServer.create_directory dir.c_path time') :: acc + (CompilationCache.create_directory dir.c_path time') :: acc end else acc with Unix.Unix_error _ -> @@ -262,7 +556,7 @@ let get_changed_directories sctx (ctx : Typecore.typer) = let add_dir path = try let time = stat path in - dirs := CompilationServer.create_directory path time :: !dirs + dirs := CompilationCache.create_directory path time :: !dirs with Unix.Unix_error _ -> () in @@ -285,7 +579,7 @@ let get_changed_directories sctx (ctx : Typecore.typer) = [Some m'] where [m'] is the module responsible for [m] not being reusable. *) let check_module sctx ctx m p = let com = ctx.Typecore.com in - let cc = CommonCache.get_cache sctx.cs com in + let cc = CommonCache.get_cache com in let content_changed m file = let fkey = ctx.com.file_keys#get file in try @@ -304,13 +598,13 @@ let check_module sctx ctx m p = let time = file_time file in if time > m.m_extra.m_time then begin ServerMessage.module_path_changed com "" (m,time,file); - raise Not_found + raise (Dirty (Shadowed file)) end end ) paths in - let mark = sctx.mark_loop in - let start_mark = sctx.compilation_mark in + let start_mark = sctx.compilation_step in + let unknown_state_modules = ref [] in let rec check m = let check_module_path () = let directories = get_changed_directories sctx ctx in @@ -318,27 +612,26 @@ let check_module sctx ctx m p = | MFake | MImport -> () (* don't get classpath *) | MExtern -> (* if we have a file then this will override our extern type *) - let has_file = (try check_module_shadowing directories m; false with Not_found -> true) in - if has_file then begin - if sctx.verbose then print_endline ("A file is masking the library file " ^ s_type_path m.m_path); (* TODO *) - raise Not_found; - end; + check_module_shadowing directories m; let rec loop = function | [] -> if sctx.verbose then print_endline ("No library file was found for " ^ s_type_path m.m_path); (* TODO *) - raise Not_found (* no extern registration *) + raise (Dirty LibraryChanged) | (file,load) :: l -> match load m.m_path p with - | None -> loop l + | None -> + loop l | Some _ -> if com.file_keys#get file <> (Path.UniqueKey.lazy_key m.m_extra.m_file) then begin if sctx.verbose then print_endline ("Library file was changed for " ^ s_type_path m.m_path); (* TODO *) - raise Not_found; + raise (Dirty LibraryChanged) end in loop com.load_extern_type - | MCode -> check_module_shadowing directories m - | MMacro when ctx.Typecore.in_macro -> check_module_shadowing directories m + | MCode -> + check_module_shadowing directories m + | MMacro when com.is_macro_context -> + check_module_shadowing directories m | MMacro -> (* Creating another context while the previous one is incomplete means we have an infinite loop in the compiler. @@ -346,7 +639,7 @@ let check_module sctx ctx m p = Prevents spending another 5 hours for debugging. @see https://github.com/HaxeFoundation/haxe/issues/8174 *) - if not ctx.g.complete && ctx.in_macro then + if not ctx.g.complete && ctx.com.is_macro_context then raise (ServerError ("Infinite loop in Haxe server detected. " ^ "Probably caused by shadowing a module of the standard library. " ^ "Make sure shadowed module does not pull macro context.")); @@ -365,72 +658,110 @@ let check_module sctx ctx m p = end else begin ServerMessage.not_cached com "" m; if m.m_extra.m_kind = MFake then Hashtbl.remove Typecore.fake_modules (Path.UniqueKey.lazy_key m.m_extra.m_file); - raise Not_found; + raise (Dirty (FileChanged file)) end end in let check_dependencies () = PMap.iter (fun _ m2 -> match check m2 with | None -> () - | Some path -> raise (Dirty path) + | Some reason -> raise (Dirty (DependencyDirty(m2.m_path,reason))) ) m.m_extra.m_deps; in - begin match m.m_extra.m_dirty with - | Some path -> - Some path - | None -> - if m.m_extra.m_mark = mark then - None - else try - let old_mark = m.m_extra.m_mark in - m.m_extra.m_mark <- mark; - if old_mark <= start_mark then begin - if not (has_policy NoCheckShadowing) then check_module_path(); - if not (has_policy NoCheckFileTimeModification) || file_extension (Path.UniqueKey.lazy_path m.m_extra.m_file) <> "hx" then check_file(); - end; + let check () = + try + if not (has_policy NoCheckShadowing) then check_module_path(); + if not (has_policy NoCheckFileTimeModification) || Path.file_extension (Path.UniqueKey.lazy_path m.m_extra.m_file) <> "hx" then check_file(); if not (has_policy NoCheckDependencies) then check_dependencies(); None with - | Not_found -> - m.m_extra.m_dirty <- Some m.m_path; - Some m.m_path - | Dirty path -> - m.m_extra.m_dirty <- Some path; - Some path - end + | Dirty reason -> + Some reason + in + (* If the module mark matches our compilation mark, we are done *) + if m.m_extra.m_checked = start_mark then begin match m.m_extra.m_cache_state with + | MSGood | MSUnknown -> + None + | MSBad reason -> + Some reason + end else begin + (* Otherwise, set to current compilation mark for recursion *) + m.m_extra.m_checked <- start_mark; + let dirty = match m.m_extra.m_cache_state with + | MSBad reason -> + (* If we are already dirty, stick to it. *) + Some reason + | MSUnknown -> + (* This should not happen because any MSUnknown module is supposed to have the current m_checked. *) + die "" __LOC__ + | MSGood -> + (* Otherwise, run the checks *) + m.m_extra.m_cache_state <- MSUnknown; + check () + in + let dirty = match dirty with + | Some (DependencyDirty _) when has_policy Retype -> + let result = Retyper.attempt_retyping ctx m p in + begin match result with + | None -> + ServerMessage.retyper_ok com "" m; + None + | Some reason -> + ServerMessage.retyper_fail com "" m reason; + dirty + end + | _ -> + dirty + in + (* Update the module now. It will use this dirty status for the remainder of this compilation. *) + begin match dirty with + | Some reason -> + (* Update the state if we're dirty. *) + m.m_extra.m_cache_state <- MSBad reason; + | None -> + (* We cannot update if we're clean because at this point it might just be an assumption. + Instead We add the module to a list which is updated at the end of handling this subgraph. *) + unknown_state_modules := m :: !unknown_state_modules; + end; + dirty + end in - check m + let state = check m in + begin match state with + | None -> + (* If the entire subgraph is clean, we can set all modules to good state *) + List.iter (fun m -> m.m_extra.m_cache_state <- MSGood) !unknown_state_modules; + | Some _ -> + (* Otherwise, unknown state module may or may not be dirty. We didn't check everything eagerly, so we have + to make sure that the module is checked again if it appears in a different check. This is achieved by + setting m_checked to a lower value and assuming Good state again. *) + List.iter (fun m -> match m.m_extra.m_cache_state with + | MSUnknown -> + m.m_extra.m_checked <- start_mark - 1; + m.m_extra.m_cache_state <- MSGood; + | MSGood | MSBad _ -> + () + ) !unknown_state_modules + end; + state (* Adds module [m] and all its dependencies (recursively) from the cache to the current compilation context. *) let add_modules sctx ctx m p = let com = ctx.Typecore.com in let rec add_modules tabs m0 m = - if m.m_extra.m_added < sctx.compilation_step then begin + if m.m_extra.m_added < ctx.com.compilation_step then begin (match m0.m_extra.m_kind, m.m_extra.m_kind with | MCode, MMacro | MMacro, MCode -> (* this was just a dependency to check : do not add to the context *) PMap.iter (Hashtbl.replace com.resources) m.m_extra.m_binded_res; | _ -> + m.m_extra.m_added <- ctx.com.compilation_step; ServerMessage.reusing com tabs m; - m.m_extra.m_added <- sctx.compilation_step; List.iter (fun t -> - match t with - | TClassDecl c -> c.cl_restore() - | TEnumDecl e -> - let rec loop acc = function - | [] -> () - | (Meta.RealPath,[Ast.EConst (Ast.String(path,_)),_],_) :: l -> - e.e_path <- Ast.parse_path path; - e.e_meta <- (List.rev acc) @ l; - | x :: l -> loop (x::acc) l - in - loop [] e.e_meta - | TAbstractDecl a -> - a.a_meta <- List.filter (fun (m,_,_) -> m <> Meta.ValueUsed) a.a_meta - | _ -> () + (t_infos t).mt_restore() ) m.m_types; - TypeloadModule.add_module ctx m p; + TypeloadModule.ModuleLevel.add_module ctx m p; PMap.iter (Hashtbl.replace com.resources) m.m_extra.m_binded_res; PMap.iter (fun _ m2 -> add_modules (tabs ^ " ") m0 m2) m.m_extra.m_deps ) @@ -443,15 +774,14 @@ let add_modules sctx ctx m p = let type_module sctx (ctx:Typecore.typer) mpath p = let t = Timer.timer ["server";"module cache"] in let com = ctx.Typecore.com in - let cc = CommonCache.get_cache sctx.cs com in - sctx.mark_loop <- sctx.mark_loop + 1; + let cc = CommonCache.get_cache com in try let m = cc#find_module mpath in let tcheck = Timer.timer ["server";"module cache";"check"] in begin match check_module sctx ctx m p with | None -> () - | Some path -> - ServerMessage.skipping_dep com "" (m,path); + | Some reason -> + ServerMessage.skipping_dep com "" (m,(Printer.s_module_skip_reason reason)); tcheck(); raise Not_found; end; @@ -465,161 +795,199 @@ let type_module sctx (ctx:Typecore.typer) mpath p = t(); None -(* Sets up the per-compilation context. *) -let create sctx write params = +let before_anything sctx ctx = + ensure_macro_setup sctx + +let after_arg_parsing sctx ctx = + let com = ctx.com in let cs = sctx.cs in - let maybe_cache_context com = - if com.display.dms_full_typing then begin - CommonCache.cache_context sctx.cs com; - ServerMessage.cached_modules com "" (List.length com.modules); - end - in - let ctx = create_context params in - ctx.flush <- (fun() -> - sctx.compilation_step <- sctx.compilation_step + 1; - sctx.compilation_mark <- sctx.mark_loop; - check_display_flush ctx (fun () -> - List.iter - (fun msg -> - let s = compiler_message_string msg in - write (s ^ "\n"); - ServerMessage.message s; - ) - (List.rev ctx.messages); - sctx.was_compilation <- ctx.com.display.dms_full_typing; - if ctx.has_error then begin - measure_times := false; - write "\x02\n" - end else maybe_cache_context ctx.com; - ) - ); - ctx.setup <- (fun() -> - let sign = Define.get_signature ctx.com.defines in - ServerMessage.defines ctx.com ""; - ServerMessage.signature ctx.com "" sign; - ServerMessage.display_position ctx.com "" (DisplayPosition.display_position#get); - try - if (Hashtbl.find sctx.class_paths sign) <> ctx.com.class_path then begin - ServerMessage.class_paths_changed ctx.com ""; - Hashtbl.replace sctx.class_paths sign ctx.com.class_path; - cs#clear_directories sign; - (cs#get_context sign)#set_initialized false; - end; - with Not_found -> - Hashtbl.add sctx.class_paths sign ctx.com.class_path; - () - ); - ctx.com.print <- (fun str -> write ("\x01" ^ String.concat "\x01" (ExtString.String.nsplit str "\n") ^ "\n")); - ctx - -(* Resets the state for a new compilation *) -let init_new_compilation sctx = - ServerCompilationContext.reset sctx; - Parser.reset_state(); - return_partial_type := false; - measure_times := false; - Hashtbl.clear DeprecationCheck.warned_positions; - close_times(); - stats.s_files_parsed := 0; - stats.s_classes_built := 0; - stats.s_methods_typed := 0; - stats.s_macros_called := 0; - Hashtbl.clear Timer.htimers; - sctx.compilation_step <- sctx.compilation_step + 1; - sctx.compilation_mark <- sctx.mark_loop; - start_time := get_time() - -let cleanup () = - begin match !MacroContext.macro_interp_cache with - | Some interp -> EvalContext.GlobalState.cleanup interp - | None -> () - end + let sign = Define.get_signature com.defines in + ServerMessage.defines com ""; + ServerMessage.signature com "" sign; + ServerMessage.display_position com "" (DisplayPosition.display_position#get); + try + if (Hashtbl.find sctx.class_paths sign) <> com.class_path then begin + ServerMessage.class_paths_changed com ""; + Hashtbl.replace sctx.class_paths sign com.class_path; + cs#clear_directories sign; + (cs#get_context sign)#set_initialized false; + end; + with Not_found -> + Hashtbl.add sctx.class_paths sign com.class_path; + () -let gc_heap_stats () = - let stats = Gc.quick_stat() in - stats.major_words,stats.heap_words - -let fmt_percent f = - int_of_float (f *. 100.) - -module Tasks = struct - class gc_task (max_working_memory : float) (heap_size : float) = object(self) - inherit server_task ["gc"] 100 - - method private execute = - let t0 = get_time() in - let stats = Gc.stat() in - let live_words = float_of_int stats.live_words in - (* Maximum heap size needed for the last X compilations = sum of what's live + max working memory. *) - let needed_max = live_words +. max_working_memory in - (* Additional heap percentage needed = what's live / max of what was live. *) - let percent_needed = (1. -. live_words /. needed_max) in - (* Effective cache size percentage = what's live / heap size. *) - let percent_used = live_words /. heap_size in - (* Set allowed space_overhead to the maximum of what we needed during the last X compilations. *) - let new_space_overhead = int_of_float ((percent_needed +. 0.05) *. 100.) in - let old_gc = Gc.get() in - Gc.set { old_gc with Gc.space_overhead = new_space_overhead; }; - (* Compact if less than 80% of our heap words consist of the cache and there's less than 50% overhead. *) - let do_compact = percent_used < 0.8 && percent_needed < 0.5 in - begin if do_compact then - Gc.compact() - else - Gc.full_major(); - end; - Gc.set old_gc; - ServerMessage.gc_stats (get_time() -. t0) stats do_compact new_space_overhead - end +let after_compilation sctx ctx = + if not (has_error ctx) then + maybe_cache_context sctx ctx.com - class class_maintenance_task (cs : CompilationServer.t) (c : tclass) = object(self) - inherit server_task ["module maintenance"] 70 +let mk_length_prefixed_communication allow_nonblock chin chout = + let sin = Unix.descr_of_in_channel chin in + let chin = IO.input_channel chin in + let chout = IO.output_channel chout in + + let bout = Buffer.create 0 in - method private execute = - let rec field cf = - (* Unset cf_expr. This holds the optimized version for generators, which we don't need to persist. If - we compile again, the semi-optimized expression will be restored by calling cl_restore(). *) - cf.cf_expr <- None; - List.iter field cf.cf_overloads + let block () = Unix.clear_nonblock sin in + let unblock () = Unix.set_nonblock sin in + + let read_nonblock _ = + let len = IO.read_i32 chin in + Some (IO.really_nread_string chin len) + in + let read = if allow_nonblock then fun do_block -> + if do_block then begin + block(); + read_nonblock true; + end else begin + let c0 = + unblock(); + try + Some (IO.read_byte chin) + with + | Sys_blocked_io + (* TODO: We're supposed to catch Sys_blocked_io only, but that doesn't work on my PC... *) + | Sys_error _ -> + None in - (* What we're doing here at the moment is free, so we can just do it in one task. If this ever gets more expensive, - we should spawn a task per-field. *) - List.iter field c.cl_ordered_fields; - List.iter field c.cl_ordered_statics; - Option.may field c.cl_constructor; - end + begin match c0 with + | Some c0 -> + block(); (* We got something, make sure we block until we're done. *) + let c1 = IO.read_byte chin in + let c2 = IO.read_byte chin in + let c3 = IO.read_byte chin in + let len = c3 lsl 24 + c2 lsl 16 + c1 lsl 8 + c0 in + Some (IO.really_nread_string chin len) + | None -> + None + end + end + else read_nonblock in - class module_maintenance_task (cs : CompilationServer.t) (m : module_def) = object(self) - inherit server_task ["module maintenance"] 80 + let write = Buffer.add_string bout in - method private execute = - List.iter (fun mt -> match mt with - | TClassDecl c -> - cs#add_task (new class_maintenance_task cs c) - | _ -> - () - ) m.m_types - end + let close = fun() -> + flush stdout; + IO.write_i32 chout (Buffer.length bout); + IO.nwrite_string chout (Buffer.contents bout); + IO.flush chout + in - class server_exploration_task (cs : CompilationServer.t) = object(self) - inherit server_task ["server explore"] 90 + fun () -> + Buffer.clear bout; + allow_nonblock, read, write, close - method private execute = - cs#iter_modules (fun m -> cs#add_task (new module_maintenance_task cs m)) - end -end +let ssend sock str = + let rec loop pos len = + if len = 0 then + () + else + let s = Unix.send sock str pos len [] in + loop (pos + s) (len - s) + in + loop 0 (Bytes.length str) + +(* The accept-function to wait for a stdio connection. *) +let init_wait_stdio() = + set_binary_mode_in stdin true; + set_binary_mode_out stderr true; + mk_length_prefixed_communication false stdin stderr + +(* The connect function to connect to [host] at [port] and send arguments [args]. *) +let do_connect host port args = + let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in + (try Unix.connect sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't connect on " ^ host ^ ":" ^ string_of_int port)); + let rec display_stdin args = + match args with + | [] -> "" + | "-D" :: ("display_stdin" | "display-stdin") :: _ -> + let accept = init_wait_stdio() in + let _, read, _, _ = accept() in + Option.default "" (read true) + | _ :: args -> + display_stdin args + in + let args = ("--cwd " ^ Unix.getcwd()) :: args in + let s = (String.concat "" (List.map (fun a -> a ^ "\n") args)) ^ (display_stdin args) in + ssend sock (Bytes.of_string (s ^ "\000")); + let has_error = ref false in + let rec print line = + match (if line = "" then '\x00' else line.[0]) with + | '\x01' -> + print_string (String.concat "\n" (List.tl (ExtString.String.nsplit line "\x01"))); + flush stdout + | '\x02' -> + has_error := true; + | _ -> + prerr_endline line; + in + let buf = Buffer.create 0 in + let process() = + let lines = ExtString.String.nsplit (Buffer.contents buf) "\n" in + (* the last line ends with \n *) + let lines = (match List.rev lines with "" :: l -> List.rev l | _ -> lines) in + List.iter print lines; + in + let tmp = Bytes.create 1024 in + let rec loop() = + let b = Unix.recv sock tmp 0 1024 [] in + Buffer.add_subbytes buf tmp 0 b; + if b > 0 then begin + if Bytes.get tmp (b - 1) = '\n' then begin + process(); + Buffer.reset buf; + end; + loop(); + end + in + loop(); + process(); + if !has_error then exit 1 + +let enable_cache_mode sctx = + TypeloadModule.type_module_hook := type_module sctx; + MacroContext.macro_enable_cache := true; + ServerCompilationContext.ensure_macro_setup sctx; + TypeloadParse.parse_hook := parse_file sctx.cs + +let rec process sctx comm args = + let t0 = get_time() in + ServerMessage.arguments args; + reset sctx; + let api = { + on_context_create = (fun () -> + sctx.compilation_step <- sctx.compilation_step + 1; + sctx.compilation_step; + ); + cache = sctx.cs; + callbacks = { + before_anything = before_anything sctx; + after_arg_parsing = after_arg_parsing sctx; + after_compilation = after_compilation sctx; + }; + init_wait_socket = init_wait_socket; + init_wait_connect = init_wait_connect; + init_wait_stdio = init_wait_stdio; + wait_loop = wait_loop; + do_connect = do_connect; + } in + Compiler.HighLevel.entry api comm args; + run_delays sctx; + ServerMessage.stats stats (get_time() -. t0) (* The server main loop. Waits for the [accept] call to then process the sent compilation parameters through [process_params]. *) -let wait_loop process_params verbose accept = +and wait_loop verbose accept = if verbose then ServerMessage.enable_all (); Sys.catch_break false; (* Sys can never catch a break *) (* Create server context and set up hooks for parsing and typing *) - let cs = CompilationServer.create () in - let sctx = ServerCompilationContext.create verbose cs in - TypeloadModule.type_module_hook := type_module sctx; - MacroContext.macro_enable_cache := true; - TypeloadParse.parse_hook := parse_file cs; + let sctx = ServerCompilationContext.create verbose in + let cs = sctx.cs in + enable_cache_mode sctx; let ring = Ring.create 10 0. in + let gc_heap_stats () = + let stats = Gc.quick_stat() in + stats.major_words,stats.heap_words + in let heap_stats_start = ref (gc_heap_stats()) in let update_heap () = (* On every compilation: Track how many words were allocated for this compilation (working memory). *) @@ -638,41 +1006,21 @@ let wait_loop process_params verbose accept = (* Main loop: accept connections and process arguments *) while true do let support_nonblock, read, write, close = accept() in - let process s = - let t0 = get_time() in - let hxml = - try - let idx = String.index s '\001' in - current_stdin := Some (String.sub s (idx + 1) ((String.length s) - idx - 1)); - (String.sub s 0 idx) - with Not_found -> - s - in - let data = parse_hxml_data hxml in - ServerMessage.arguments data; - init_new_compilation sctx; - begin try - let create = create sctx write in - (* Pass arguments to normal handling in main.ml *) - process_params create data; - close_times(); - if !measure_times then report_times (fun s -> write (s ^ "\n")) - with - | Completion str -> - ServerMessage.completion str; - write str - | Arg.Bad msg -> - print_endline ("Error: " ^ msg); - end; - run_delays sctx; - ServerMessage.stats stats (get_time() -. t0) - in begin try (* Read arguments *) let rec loop block = match read block with - | Some data -> - process data + | Some s -> + let hxml = + try + let idx = String.index s '\001' in + current_stdin := Some (String.sub s (idx + 1) ((String.length s) - idx - 1)); + (String.sub s 0 idx) + with Not_found -> + s + in + let data = Helper.parse_hxml_data hxml in + process sctx (Communication.create_pipe sctx write) data | None -> if not cs#has_task then (* If there is no pending task, turn into blocking mode. *) @@ -690,7 +1038,7 @@ let wait_loop process_params verbose accept = let estr = Printexc.to_string e in ServerMessage.uncaught_error estr; (try write ("\x02\n" ^ estr); with _ -> ()); - if is_debug_run then print_endline (estr ^ "\n" ^ Printexc.get_backtrace()); + if Helper.is_debug_run then print_endline (estr ^ "\n" ^ Printexc.get_backtrace()); if e = Out_of_memory then begin close(); exit (-1); @@ -706,77 +1054,17 @@ let wait_loop process_params verbose accept = while cs#has_task do cs#get_task#run done else if sctx.was_compilation then cs#add_task (new Tasks.server_exploration_task cs) - done - -let mk_length_prefixed_communication allow_nonblock chin chout = - let sin = Unix.descr_of_in_channel chin in - let chin = IO.input_channel chin in - let chout = IO.output_channel chout in - - let bout = Buffer.create 0 in - - let block () = Unix.clear_nonblock sin in - let unblock () = Unix.set_nonblock sin in - - let read_nonblock _ = - let len = IO.read_i32 chin in - Some (IO.really_nread_string chin len) - in - let read = if allow_nonblock then fun do_block -> - if do_block then begin - block(); - read_nonblock true; - end else begin - let c0 = - unblock(); - try - Some (IO.read_byte chin) - with - | Sys_blocked_io - (* TODO: We're supposed to catch Sys_blocked_io only, but that doesn't work on my PC... *) - | Sys_error _ -> - None - in - begin match c0 with - | Some c0 -> - block(); (* We got something, make sure we block until we're done. *) - let c1 = IO.read_byte chin in - let c2 = IO.read_byte chin in - let c3 = IO.read_byte chin in - let len = c3 lsl 24 + c2 lsl 16 + c1 lsl 8 + c0 in - Some (IO.really_nread_string chin len) - | None -> - None - end - end - else read_nonblock in - - let write = Buffer.add_string bout in - - let close = fun() -> - IO.write_i32 chout (Buffer.length bout); - IO.nwrite_string chout (Buffer.contents bout); - IO.flush chout - in - - fun () -> - Buffer.clear bout; - allow_nonblock, read, write, close - -(* The accept-function to wait for a stdio connection. *) -let init_wait_stdio() = - set_binary_mode_in stdin true; - set_binary_mode_out stderr true; - mk_length_prefixed_communication false stdin stderr + done; + 0 (* Connect to given host/port and return accept function for communication *) -let init_wait_connect host port = +and init_wait_connect host port = let host = Unix.inet_addr_of_string host in let chin, chout = Unix.open_connection (Unix.ADDR_INET (host,port)) in mk_length_prefixed_communication true chin chout (* The accept-function to wait for a socket connection. *) -let init_wait_socket host port = +and init_wait_socket host port = let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in (try Unix.setsockopt sock Unix.SO_REUSEADDR true with _ -> ()); (try Unix.bind sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't wait on " ^ host ^ ":" ^ string_of_int port)); @@ -817,54 +1105,3 @@ let init_wait_socket host port = false, read, write, close ) in accept - -(* The connect function to connect to [host] at [port] and send arguments [args]. *) -let do_connect host port args = - let sock = Unix.socket Unix.PF_INET Unix.SOCK_STREAM 0 in - (try Unix.connect sock (Unix.ADDR_INET (Unix.inet_addr_of_string host,port)) with _ -> failwith ("Couldn't connect on " ^ host ^ ":" ^ string_of_int port)); - let rec display_stdin args = - match args with - | [] -> "" - | "-D" :: ("display_stdin" | "display-stdin") :: _ -> - let accept = init_wait_stdio() in - let _, read, _, _ = accept() in - Option.default "" (read true) - | _ :: args -> - display_stdin args - in - let args = ("--cwd " ^ Unix.getcwd()) :: args in - let s = (String.concat "" (List.map (fun a -> a ^ "\n") args)) ^ (display_stdin args) in - ssend sock (Bytes.of_string (s ^ "\000")); - let has_error = ref false in - let rec print line = - match (if line = "" then '\x00' else line.[0]) with - | '\x01' -> - print_string (String.concat "\n" (List.tl (ExtString.String.nsplit line "\x01"))); - flush stdout - | '\x02' -> - has_error := true; - | _ -> - prerr_endline line; - in - let buf = Buffer.create 0 in - let process() = - let lines = ExtString.String.nsplit (Buffer.contents buf) "\n" in - (* the last line ends with \n *) - let lines = (match List.rev lines with "" :: l -> List.rev l | _ -> lines) in - List.iter print lines; - in - let tmp = Bytes.create 1024 in - let rec loop() = - let b = Unix.recv sock tmp 0 1024 [] in - Buffer.add_subbytes buf tmp 0 b; - if b > 0 then begin - if Bytes.get tmp (b - 1) = '\n' then begin - process(); - Buffer.reset buf; - end; - loop(); - end - in - loop(); - process(); - if !has_error then exit 1 diff --git a/src/compiler/serverCompilationContext.ml b/src/compiler/serverCompilationContext.ml new file mode 100644 index 00000000..95a567aa --- /dev/null +++ b/src/compiler/serverCompilationContext.ml @@ -0,0 +1,74 @@ +open Globals +open Common +open Timer +open CompilationCache + +type t = { + (* If true, prints some debug information *) + verbose : bool; + (* The list of changed directories per-signature *) + changed_directories : (Digest.t,cached_directory list) Hashtbl.t; + (* A reference to the compilation server instance *) + cs : CompilationCache.t; + (* A list of class paths per-signature *) + class_paths : (Digest.t,string list) Hashtbl.t; + (* Increased for each compilation *) + mutable compilation_step : int; + (* A list of delays which are run after compilation *) + mutable delays : (unit -> unit) list; + (* True if it's an actual compilation, false if it's a display operation *) + mutable was_compilation : bool; + (* True if the macro context has been set up *) + mutable macro_context_setup : bool; +} + +let create verbose = { + verbose = verbose; + cs = new CompilationCache.cache; + class_paths = Hashtbl.create 0; + changed_directories = Hashtbl.create 0; + compilation_step = 0; + delays = []; + was_compilation = false; + macro_context_setup = false; +} + +let add_delay sctx f = + sctx.delays <- f :: sctx.delays + +let run_delays sctx = + let fl = sctx.delays in + sctx.delays <- []; + List.iter (fun f -> f()) fl + +(* Resets the state for a new compilation *) +let reset sctx = + Hashtbl.clear sctx.changed_directories; + sctx.was_compilation <- false; + Parser.reset_state(); + return_partial_type := false; + measure_times := false; + Hashtbl.clear DeprecationCheck.warned_positions; + close_times(); + stats.s_files_parsed := 0; + stats.s_classes_built := 0; + stats.s_methods_typed := 0; + stats.s_macros_called := 0; + Hashtbl.clear Timer.htimers; + Helper.start_time := get_time() + +let maybe_cache_context sctx com = + if com.display.dms_full_typing then begin + CommonCache.cache_context sctx.cs com; + ServerMessage.cached_modules com "" (List.length com.modules); + end + +let ensure_macro_setup sctx = + if not sctx.macro_context_setup then begin + sctx.macro_context_setup <- true; + MacroContext.setup(); + end + +let cleanup () = match !MacroContext.macro_interp_cache with + | Some interp -> EvalContext.GlobalState.cleanup interp + | None -> () \ No newline at end of file diff --git a/src/compiler/serverMessage.ml b/src/compiler/serverMessage.ml index 95100800..1ced4d41 100644 --- a/src/compiler/serverMessage.ml +++ b/src/compiler/serverMessage.ml @@ -1,6 +1,6 @@ open Globals open Common -open CompilationServer +open CompilationCache open Type open Json @@ -13,6 +13,7 @@ type server_message_options = { mutable print_parsed : bool; mutable print_removed_directory : bool; mutable print_reusing : bool; + mutable print_retyping : bool; mutable print_skipping_dep : bool; mutable print_unchanged_content : bool; mutable print_cached_modules : bool; @@ -38,6 +39,7 @@ let config = { print_parsed = false; print_removed_directory = false; print_reusing = false; + print_retyping = false; print_skipping_dep = false; print_unchanged_content = false; print_cached_modules = false; @@ -56,7 +58,7 @@ let config = { let sign_string com = let sign = Define.get_signature com.defines in - let cs = CompilationServer.force () in + let cs = com.cs in let sign_id = (cs#get_context sign)#get_index in Printf.sprintf "%2i,%3s: " sign_id (short_platform_name com.platform) @@ -85,8 +87,17 @@ let removed_directory com tabs dir = let reusing com tabs m = if config.print_reusing then print_endline (Printf.sprintf "%s%sreusing %s" (sign_string com) tabs (s_type_path m.m_path)) -let skipping_dep com tabs (m,path) = - if config.print_skipping_dep then print_endline (Printf.sprintf "%sskipping %s%s" (sign_string com) (s_type_path m.m_path) (if m.m_path = path then "" else Printf.sprintf "(%s)" (s_type_path path))) +let retyper_ok com tabs m = + if config.print_retyping then print_endline (Printf.sprintf "%s%sretyped %s" (sign_string com) tabs (s_type_path m.m_path)) + +let retyper_fail com tabs m reason = + if config.print_retyping then begin + print_endline (Printf.sprintf "%s%sfailed retyping %s" (sign_string com) tabs (s_type_path m.m_path)); + print_endline (Printf.sprintf "%s%s%s" (sign_string com) (tabs ^ " ") reason); + end + +let skipping_dep com tabs (m,reason) = + if config.print_skipping_dep then print_endline (Printf.sprintf "%sskipping %s (%s)" (sign_string com) (s_type_path m.m_path) reason) let unchanged_content com tabs file = if config.print_unchanged_content then print_endline (Printf.sprintf "%s%s changed time not but content, reusing" (sign_string com) file) @@ -105,8 +116,11 @@ let completion str = let defines com tabs = if config.print_defines then begin - let defines = PMap.foldi (fun k v acc -> (k ^ "=" ^ v) :: acc) com.defines.Define.values [] in - print_endline ("Defines " ^ (String.concat "," (List.sort compare defines))) + let buffer = Buffer.create 64 in + Buffer.add_string buffer "Defines "; + PMap.iter (Printf.bprintf buffer "%s=%s,") com.defines.values; + Buffer.truncate buffer (Buffer.length buffer - 1); + print_endline (Buffer.contents buffer) end let signature com tabs sign = @@ -151,6 +165,7 @@ let enable_all () = config.print_parsed <- true; config.print_removed_directory <- true; config.print_reusing <- true; + config.print_retyping <- true; config.print_skipping_dep <- true; config.print_unchanged_content <- true; config.print_cached_modules <- true; @@ -174,6 +189,7 @@ let set_by_name name value = match name with | "parsed" -> config.print_parsed <- value; | "removedDirectory" -> config.print_removed_directory <- value; | "reusing" -> config.print_reusing <- value; + | "retyping" -> config.print_retyping <- value; | "skippingDep" -> config.print_skipping_dep <- value; | "unchangedContent" -> config.print_unchanged_content <- value; | "cachedModules" -> config.print_cached_modules <- value; @@ -187,4 +203,4 @@ let set_by_name name value = match name with | "socketMessage" -> config.print_socket_message <- value; | "uncaughtError" -> config.print_uncaught_error <- value; | "newContext" -> config.print_new_context <- value; - | _ -> raise Not_found \ No newline at end of file + | _ -> raise Not_found diff --git a/src/compiler/tasks.ml b/src/compiler/tasks.ml new file mode 100644 index 00000000..42d6ebcf --- /dev/null +++ b/src/compiler/tasks.ml @@ -0,0 +1,67 @@ + +open Type +open CompilationCache + +class gc_task (max_working_memory : float) (heap_size : float) = object(self) + inherit server_task ["gc"] 100 + + method private execute = + let t0 = Timer.get_time() in + let stats = Gc.stat() in + let live_words = float_of_int stats.live_words in + (* Maximum heap size needed for the last X compilations = sum of what's live + max working memory. *) + let needed_max = live_words +. max_working_memory in + (* Additional heap percentage needed = what's live / max of what was live. *) + let percent_needed = (1. -. live_words /. needed_max) in + (* Effective cache size percentage = what's live / heap size. *) + let percent_used = live_words /. heap_size in + (* Set allowed space_overhead to the maximum of what we needed during the last X compilations. *) + let new_space_overhead = int_of_float ((percent_needed +. 0.05) *. 100.) in + let old_gc = Gc.get() in + Gc.set { old_gc with Gc.space_overhead = new_space_overhead; }; + (* Compact if less than 80% of our heap words consist of the cache and there's less than 50% overhead. *) + let do_compact = percent_used < 0.8 && percent_needed < 0.5 in + begin if do_compact then + Gc.compact() + else + Gc.full_major(); + end; + Gc.set old_gc; + ServerMessage.gc_stats (Timer.get_time() -. t0) stats do_compact new_space_overhead +end + +class class_maintenance_task (cs : CompilationCache.t) (c : tclass) = object(self) + inherit server_task ["module maintenance"] 70 + + method private execute = + let rec field cf = + (* Unset cf_expr. This holds the optimized version for generators, which we don't need to persist. If + we compile again, the semi-optimized expression will be restored by calling cl_restore(). *) + cf.cf_expr <- None; + List.iter field cf.cf_overloads + in + (* What we're doing here at the moment is free, so we can just do it in one task. If this ever gets more expensive, + we should spawn a task per-field. *) + List.iter field c.cl_ordered_fields; + List.iter field c.cl_ordered_statics; + Option.may field c.cl_constructor; +end + +class module_maintenance_task (cs : CompilationCache.t) (m : module_def) = object(self) + inherit server_task ["module maintenance"] 80 + + method private execute = + List.iter (fun mt -> match mt with + | TClassDecl c -> + cs#add_task (new class_maintenance_task cs c) + | _ -> + () + ) m.m_types +end + +class server_exploration_task (cs : CompilationCache.t) = object(self) + inherit server_task ["server explore"] 90 + + method private execute = + cs#iter_modules (fun m -> cs#add_task (new module_maintenance_task cs m)) +end \ No newline at end of file diff --git a/src/context/abstractCast.ml b/src/context/abstractCast.ml index 0469842d..820a6c7d 100644 --- a/src/context/abstractCast.ml +++ b/src/context/abstractCast.ml @@ -19,7 +19,7 @@ let rec make_static_call ctx c cf a pl args t p = | None -> type_expr ctx (EConst (Ident "null"),p) WithType.value in ctx.with_type_stack <- List.tl ctx.with_type_stack; - let e = try cast_or_unify_raise ctx t e p with Error(Unify _,_) -> raise Not_found in + let e = try cast_or_unify_raise ctx t e p with Error(Unify _,_,_) -> raise Not_found in f(); e | _ -> die "" __LOC__ @@ -35,10 +35,13 @@ and do_check_cast ctx uctx tleft eright p = if cf.cf_kind = Method MethMacro then begin match cast_stack.rec_stack with | previous_from :: _ when previous_from == cf -> - raise (Error (Unify [cannot_unify eright.etype tleft], eright.epos)); + (try + Type.unify_custom uctx eright.etype tleft; + with Unify_error l -> + raise (Error (Unify l, eright.epos,0))) | _ -> () end; - if cf == ctx.curfield || rec_stack_memq cf cast_stack then error "Recursive implicit cast" p; + if cf == ctx.curfield || rec_stack_memq cf cast_stack then typing_error "Recursive implicit cast" p; rec_stack_loop cast_stack cf f () in let make (a,tl,(tcf,cf)) = @@ -84,6 +87,10 @@ and do_check_cast ctx uctx tleft eright p = in loop2 a.a_to end + | TInst(c,tl), TFun _ when has_class_flag c CFunctionalInterface -> + let cf = ctx.g.functional_interface_lut#find c.cl_path in + unify_raise_custom uctx eright.etype (apply_params c.cl_params tl cf.cf_type) p; + eright | _ -> raise Not_found end @@ -99,69 +106,87 @@ and cast_or_unify_raise ctx ?(uctx=None) tleft eright p = try do_check_cast ctx uctx tleft eright p with Not_found -> - unify_raise_custom uctx ctx eright.etype tleft p; + unify_raise_custom uctx eright.etype tleft p; eright and cast_or_unify ctx tleft eright p = try cast_or_unify_raise ctx tleft eright p - with Error (Unify l,p) -> + with Error (Unify l,p,_) -> raise_or_display ctx l p; eright -let find_array_access_raise ctx a pl e1 e2o p = - let is_set = e2o <> None in - let ta = apply_params a.a_params pl a.a_this in +let prepare_array_access_field ctx a pl cf p = + let monos = List.map (fun _ -> spawn_monomorph ctx p) cf.cf_params in + let map t = apply_params a.a_params pl (apply_params cf.cf_params monos t) in + let check_constraints () = + List.iter2 (fun m tp -> match follow tp.ttp_type with + | TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] -> + List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> Type.unify m (map tc) ) constr + | _ -> () + ) monos cf.cf_params; + in + let get_ta() = + let ta = apply_params a.a_params pl a.a_this in + if has_class_field_flag cf CfImpl then ta + else TAbstract(a,pl) + in + map,check_constraints,get_ta + +let find_array_read_access_raise ctx a pl e1 p = let rec loop cfl = match cfl with | [] -> raise Not_found | cf :: cfl -> - let monos = List.map (fun _ -> spawn_monomorph ctx p) cf.cf_params in - let map t = apply_params a.a_params pl (apply_params cf.cf_params monos t) in - let check_constraints () = - List.iter2 (fun m (name,t) -> match follow t with - | TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] -> - List.iter (fun tc -> match follow m with TMono _ -> raise (Unify_error []) | _ -> Type.unify m (map tc) ) constr - | _ -> () - ) monos cf.cf_params; - in - let get_ta() = - if has_class_field_flag cf CfImpl then ta - else TAbstract(a,pl) - in + let map,check_constraints,get_ta = prepare_array_access_field ctx a pl cf p in match follow (map cf.cf_type) with - | TFun([(_,_,tab);(_,_,ta1);(_,_,ta2)],r) as tf when is_set -> + | TFun((_,_,tab) :: (_,_,ta1) :: args,r) as tf when is_empty_or_pos_infos args -> begin try Type.unify tab (get_ta()); let e1 = cast_or_unify_raise ctx ta1 e1 p in - let e2o = match e2o with None -> None | Some e2 -> Some (cast_or_unify_raise ctx ta2 e2 p) in check_constraints(); - cf,tf,r,e1,e2o - with Unify_error _ | Error (Unify _,_) -> + cf,tf,r,e1 + with Unify_error _ | Error (Unify _,_,_) -> loop cfl end - | TFun([(_,_,tab);(_,_,ta1)],r) as tf when not is_set -> + | _ -> loop cfl + in + loop a.a_array + +let find_array_write_access_raise ctx a pl e1 e2 p = + let rec loop cfl = + match cfl with + | [] -> raise Not_found + | cf :: cfl -> + let map,check_constraints,get_ta = prepare_array_access_field ctx a pl cf p in + match follow (map cf.cf_type) with + | TFun((_,_,tab) :: (_,_,ta1) :: (_,_,ta2) :: args,r) as tf when is_empty_or_pos_infos args -> begin try Type.unify tab (get_ta()); let e1 = cast_or_unify_raise ctx ta1 e1 p in + let e2 = cast_or_unify_raise ctx ta2 e2 p in check_constraints(); - cf,tf,r,e1,None - with Unify_error _ | Error (Unify _,_) -> + cf,tf,r,e1,e2 + with Unify_error _ | Error (Unify _,_,_) -> loop cfl end | _ -> loop cfl in loop a.a_array -let find_array_access ctx a tl e1 e2o p = - try find_array_access_raise ctx a tl e1 e2o p +let find_array_read_access ctx a tl e1 p = + try + find_array_read_access_raise ctx a tl e1 p + with Not_found -> + let s_type = s_type (print_context()) in + typing_error (Printf.sprintf "No @:arrayAccess function for %s accepts argument of %s" (s_type (TAbstract(a,tl))) (s_type e1.etype)) p + +let find_array_write_access ctx a tl e1 e2 p = + try + find_array_write_access_raise ctx a tl e1 e2 p with Not_found -> let s_type = s_type (print_context()) in - match e2o with - | None -> - error (Printf.sprintf "No @:arrayAccess function for %s accepts argument of %s" (s_type (TAbstract(a,tl))) (s_type e1.etype)) p - | Some e2 -> - error (Printf.sprintf "No @:arrayAccess function for %s accepts arguments of %s and %s" (s_type (TAbstract(a,tl))) (s_type e1.etype) (s_type e2.etype)) p + typing_error (Printf.sprintf "No @:arrayAccess function for %s accepts arguments of %s and %s" (s_type (TAbstract(a,tl))) (s_type e1.etype) (s_type e2.etype)) p let find_multitype_specialization com a pl p = let uctx = default_unification_context in @@ -177,7 +202,7 @@ let find_multitype_specialization com a pl p = stack := t :: !stack; match follow t with | TAbstract ({ a_path = [],"Class" },_) -> - error (Printf.sprintf "Cannot use %s as key type to Map because Class is not comparable on JavaScript" (s_type (print_context()) t1)) p; + typing_error (Printf.sprintf "Cannot use %s as key type to Map because Class is not comparable on JavaScript" (s_type (print_context()) t1)) p; | TEnum(en,tl) -> PMap.iter (fun _ ef -> ignore(loop ef.ef_type)) en.e_constrs; Type.map loop t @@ -194,16 +219,16 @@ let find_multitype_specialization com a pl p = if List.exists (fun t -> has_mono t) definitive_types then begin let at = apply_params a.a_params pl a.a_this in let st = s_type (print_context()) at in - error ("Type parameters of multi type abstracts must be known (for " ^ st ^ ")") p + typing_error ("Type parameters of multi type abstracts must be known (for " ^ st ^ ")") p end; t with Not_found -> let at = apply_params a.a_params pl a.a_this in let st = s_type (print_context()) at in if has_mono at then - error ("Type parameters of multi type abstracts must be known (for " ^ st ^ ")") p + typing_error ("Type parameters of multi type abstracts must be known (for " ^ st ^ ")") p else - error ("Abstract " ^ (s_type_path a.a_path) ^ " has no @:to function that accepts " ^ st) p; + typing_error ("Abstract " ^ (s_type_path a.a_path) ^ " has no @:to function that accepts " ^ st) p; in cf, follow m @@ -215,7 +240,7 @@ let handle_abstract_casts ctx e = let's construct the underlying type. *) match Abstract.get_underlying_type a pl with | TInst(c,tl) as t -> {e with eexpr = TNew(c,tl,el); etype = t} - | _ -> error ("Cannot construct " ^ (s_type (print_context()) (TAbstract(a,pl)))) e.epos + | _ -> typing_error ("Cannot construct " ^ (s_type (print_context()) (TAbstract(a,pl)))) e.epos end else begin (* a TNew of an abstract implementation is only generated if it is a multi type abstract *) let cf,m = find_multitype_specialization ctx.com a pl e.epos in @@ -227,7 +252,16 @@ let handle_abstract_casts ctx e = | TAbstract({a_impl = Some c} as a,tl) -> begin try let cf = PMap.find "toString" c.cl_statics in - make_static_call ctx c cf a tl [e1] ctx.t.tstring e.epos + let call() = make_static_call ctx c cf a tl [e1] ctx.t.tstring e.epos in + if not ctx.allow_transform then + { e1 with etype = ctx.t.tstring; epos = e.epos } + else if not (is_nullable e1.etype) then + call() + else begin + let p = e.epos in + let chk_null = mk (TBinop (Ast.OpEq, e1, mk (TConst TNull) e1.etype p)) ctx.com.basic.tbool p in + mk (TIf (chk_null, mk (TConst (TString "null")) ctx.com.basic.tstring p, Some (call()))) ctx.com.basic.tstring p + end with Not_found -> e end @@ -315,4 +349,4 @@ let handle_abstract_casts ctx e = in loop ctx e ;; -Typecore.cast_or_unify_raise_ref := cast_or_unify_raise \ No newline at end of file +Typecore.cast_or_unify_raise_ref := cast_or_unify_raise diff --git a/src/context/common.ml b/src/context/common.ml index e478b703..cd8ae028 100644 --- a/src/context/common.ml +++ b/src/context/common.ml @@ -18,11 +18,11 @@ *) open Extlib_leftovers open Ast -open CompilationServer open Type open Globals open Define open NativeLibraries +open Warning type package_rule = | Forbidden @@ -46,32 +46,6 @@ type stats = { s_macros_called : int ref; } -type compiler_message = - | CMInfo of string * pos - | CMWarning of string * pos - | CMError of string * pos - -let compiler_message_string msg = - let (str,p) = match msg with - | CMInfo(str,p) | CMError(str,p) -> (str,p) - | CMWarning(str,p) -> ("Warning : " ^ str, p) - in - if p = null_pos then - str - else begin - let error_printer file line = Printf.sprintf "%s:%d:" file line in - let epos = Lexer.get_error_pos error_printer p in - let str = - let lines = - match (ExtString.String.nsplit str "\n") with - | first :: rest -> first :: List.map Error.compl_msg rest - | l -> l - in - String.concat ("\n" ^ epos ^ " : ") lines - in - Printf.sprintf "%s : %s" epos str - end - (** The capture policy tells which handling we make of captured locals (the locals which are referenced in local functions) @@ -91,6 +65,11 @@ type exceptions_config = { ec_native_throws : path list; (* Base types which may be caught from Haxe code without wrapping. *) ec_native_catches : path list; + (* + Hint exceptions filter to avoid wrapping for targets, which can throw/catch any type + Ignored on targets with a specific native base type for exceptions. + *) + ec_avoid_wrapping : bool; (* Path of a native class or interface, which can be used for wildcard catches. *) ec_wildcard_catch : path; (* @@ -184,42 +163,53 @@ type platform_config = { pf_exceptions : exceptions_config; (** the scoping of local variables *) pf_scoping : var_scoping_config; + (** target supports atomic operations via haxe.Atomic **) + pf_supports_atomics : bool; } class compiler_callbacks = object(self) - val mutable before_typer_create = []; - val mutable after_init_macros = []; + val before_typer_create = ref []; + val after_init_macros = ref []; val mutable after_typing = []; - val mutable before_save = []; - val mutable after_save = []; - val mutable after_filters = []; - val mutable after_generation = []; + val before_save = ref []; + val after_save = ref []; + val after_filters = ref []; + val after_generation = ref []; val mutable null_safety_report = []; method add_before_typer_create (f : unit -> unit) : unit = - before_typer_create <- f :: before_typer_create + before_typer_create := f :: !before_typer_create method add_after_init_macros (f : unit -> unit) : unit = - after_init_macros <- f :: after_init_macros + after_init_macros := f :: !after_init_macros method add_after_typing (f : module_type list -> unit) : unit = after_typing <- f :: after_typing method add_before_save (f : unit -> unit) : unit = - before_save <- f :: before_save + before_save := f :: !before_save method add_after_save (f : unit -> unit) : unit = - after_save <- f :: after_save + after_save := f :: !after_save method add_after_filters (f : unit -> unit) : unit = - after_filters <- f :: after_filters + after_filters := f :: !after_filters method add_after_generation (f : unit -> unit) : unit = - after_generation <- f :: after_generation + after_generation := f :: !after_generation method add_null_safety_report (f : (string*pos) list -> unit) : unit = null_safety_report <- f :: null_safety_report + method run r = + match !r with + | [] -> + () + | l -> + r := []; + List.iter (fun f -> f()) (List.rev l); + self#run r + method get_before_typer_create = before_typer_create method get_after_init_macros = after_init_macros method get_after_typing = after_typing @@ -243,7 +233,7 @@ class file_keys = object(self) end type shared_display_information = { - mutable diagnostics_messages : (string * pos * DisplayTypes.DiagnosticsKind.t * DisplayTypes.DiagnosticsSeverity.t) list; + mutable diagnostics_messages : (string * pos * MessageKind.t * MessageSeverity.t * int (* depth *)) list; } type display_information = { @@ -281,14 +271,83 @@ type compiler_stage = | CGenerationStart (* Generation is about to begin. *) | CGenerationDone (* Generation just finished. *) +type report_mode = + | RMNone + | RMDiagnostics of Path.UniqueKey.t list + | RMStatistics + +class virtual ['key,'value] lookup = object(self) + method virtual add : 'key -> 'value -> unit + method virtual remove : 'key -> unit + method virtual find : 'key -> 'value + method virtual iter : ('key -> 'value -> unit) -> unit + method virtual fold : 'acc . ('key -> 'value -> 'acc -> 'acc) -> 'acc -> 'acc + method virtual mem : 'key -> bool + method virtual clear : unit +end + +class ['key,'value] pmap_lookup = object(self) + inherit ['key,'value] lookup + val mutable lut : ('key,'value) PMap.t = PMap.empty + + method add (key : 'key) (value : 'value) = + lut <- PMap.add key value lut + + method remove (key : 'key) = + lut <- PMap.remove key lut + + method find (key : 'key) : 'value = + PMap.find key lut + + method iter (f : 'key -> 'value -> unit) = + PMap.iter f lut + + method fold : 'acc . ('key -> 'value -> 'acc -> 'acc) -> 'acc -> 'acc = fun f acc -> + PMap.foldi f lut acc + + method mem (key : 'key) = + PMap.mem key lut + + method clear = + lut <- PMap.empty +end + +class ['key,'value] hashtbl_lookup = object(self) + inherit ['key,'value] lookup + val lut : ('key,'value) Hashtbl.t = Hashtbl.create 0 + + method add (key : 'key) (value : 'value) = + Hashtbl.replace lut key value + + method remove (key : 'key) = + Hashtbl.remove lut key + + method find (key : 'key) : 'value = + Hashtbl.find lut key + + method iter (f : 'key -> 'value -> unit) = + Hashtbl.iter f lut + + method fold : 'acc . ('key -> 'value -> 'acc -> 'acc) -> 'acc -> 'acc = fun f acc -> + Hashtbl.fold f lut acc + + method mem (key : 'key) = + Hashtbl.mem lut key + + method clear = + Hashtbl.clear lut +end + type context = { + compilation_step : int; mutable stage : compiler_stage; - mutable cache : context_cache option; + cs : CompilationCache.t; + mutable cache : CompilationCache.context_cache option; + is_macro_context : bool; + mutable json_out : json_api option; (* config *) version : int; args : string list; - shared : shared_context; - display_information : display_information; mutable sys_args : string list; mutable display : DisplayTypes.DisplayMode.settings; mutable debug : bool; @@ -300,48 +359,69 @@ type context = { mutable class_path : string list; mutable main_class : path option; mutable package_rules : (string,package_rule) PMap.t; - mutable error : string -> pos -> unit; - mutable info : string -> pos -> unit; - mutable warning : string -> pos -> unit; + mutable report_mode : report_mode; + (* communication *) + mutable print : string -> unit; + mutable error : ?depth:int -> string -> pos -> unit; + mutable located_error : ?depth:int -> located -> unit; + mutable info : ?depth:int -> string -> pos -> unit; + mutable warning : ?depth:int -> warning -> Warning.warning_option list list -> string -> pos -> unit; + mutable warning_options : Warning.warning_option list list; mutable get_messages : unit -> compiler_message list; mutable filter_messages : (compiler_message -> bool) -> unit; + mutable run_command : string -> int; + mutable run_command_args : string -> string list -> int; + (* typing setup *) mutable load_extern_type : (string * (path -> pos -> Ast.package option)) list; (* allow finding types which are not in sources *) callbacks : compiler_callbacks; defines : Define.define; - mutable print : string -> unit; + mutable user_defines : (string, Define.user_define) Hashtbl.t; + mutable user_metas : (string, Meta.user_meta) Hashtbl.t; mutable get_macros : unit -> context option; - mutable run_command : string -> int; - file_lookup_cache : (string,string option) Hashtbl.t; + (* typing state *) + shared : shared_context; + display_information : display_information; + file_lookup_cache : (string,string option) lookup; file_keys : file_keys; - readdir_cache : (string * string,(string array) option) Hashtbl.t; - parser_cache : (string,(type_def * pos) list) Hashtbl.t; - module_to_file : (path,string) Hashtbl.t; - cached_macros : (path * string,(((string * bool * t) list * t * tclass * Type.tclass_field) * module_def)) Hashtbl.t; - mutable stored_typed_exprs : (int, texpr) PMap.t; + readdir_cache : (string * string,(string array) option) lookup; + parser_cache : (string,(type_def * pos) list) lookup; + module_to_file : (path,string) lookup; + cached_macros : (path * string,(((string * bool * t) list * t * tclass * Type.tclass_field) * module_def)) lookup; + stored_typed_exprs : (int, texpr) lookup; + overload_cache : ((path * string),(Type.t * tclass_field) list) lookup; + module_lut : (path,module_def) lookup; + module_nonexistent_lut : (path,bool) lookup; + type_to_module : (path,path) lookup; + mutable has_error : bool; pass_debug_messages : string DynArray.t; - overload_cache : ((path * string),(Type.t * tclass_field) list) Hashtbl.t; (* output *) mutable file : string; - mutable flash_version : float; mutable features : (string,bool) Hashtbl.t; mutable modules : Type.module_def list; mutable main : Type.texpr option; mutable types : Type.module_type list; mutable resources : (string,string) Hashtbl.t; - mutable neko_libs : string list; + (* target-specific *) + mutable flash_version : float; + mutable neko_lib_paths : string list; mutable include_files : (string * string) list; mutable native_libs : native_libraries; mutable net_std : string list; net_path_map : (path,string list * string list * string) Hashtbl.t; mutable c_args : string list; mutable js_gen : (unit -> unit) option; - mutable json_out : json_api option; - (* typing *) + (* misc *) mutable basic : basic_types; memory_marker : float array; } -exception Abort of string * pos +exception Abort of located + +let ignore_error com = + let b = com.display.dms_error_policy = EPIgnore in + if b then + if b then com.has_error <- true; + b (* Defines *) @@ -370,8 +450,56 @@ let raw_define com v = let define_value com k v = Define.define_value com.defines k v -let raw_defined_value com k = - Define.raw_defined_value com.defines k +let convert_define k = + String.concat "_" (ExtString.String.nsplit k "-") + +let external_defined ctx k = + Define.raw_defined ctx.defines (convert_define k) + +let external_defined_value ctx k = + Define.raw_defined_value ctx.defines (convert_define k) + +let reserved_flags = [ + "true";"false";"null";"cross";"js";"lua";"neko";"flash";"php";"cpp";"cs";"java";"python"; + "swc";"macro";"sys";"static";"utf16";"haxe";"haxe_ver" + ] + +let reserved_flag_namespaces = ["target"] + +let convert_and_validate k = + let converted_flag = convert_define k in + let raise_reserved description = + raise (Arg.Bad (description ^ " and cannot be defined from the command line")) + in + if List.mem converted_flag reserved_flags then + raise_reserved (Printf.sprintf "`%s` is a reserved compiler flag" k); + List.iter (fun ns -> + if ExtString.String.starts_with converted_flag (ns ^ ".") then + raise_reserved (Printf.sprintf "`%s` uses the reserved compiler flag namespace `%s.*`" k ns) + ) reserved_flag_namespaces; + converted_flag + +let external_define_value ctx k v = + raw_define_value ctx.defines (convert_and_validate k) v + +(* TODO: Temporary function until #8690, remove after *) +let external_define_value_no_check ctx k v = + Define.raw_define_value ctx.defines (convert_define k) v + +let external_define ctx k = + Define.raw_define ctx.defines (convert_and_validate k) + +(* TODO: Temporary function until #8690, remove after *) +let external_define_no_check ctx k = + Define.raw_define ctx.defines (convert_define k) + +let defines_for_external ctx = + PMap.foldi (fun k v acc -> + let added_underscore = PMap.add k v acc in + match ExtString.String.nsplit k "_" with + | [_] -> added_underscore + | split -> PMap.add (String.concat "-" split) v added_underscore; + ) ctx.defines.values PMap.empty let get_es_version com = try int_of_string (defined_value com Define.JsEs) with _ -> 0 @@ -419,16 +547,18 @@ let default_config = ec_native_catches = []; ec_wildcard_catch = (["StdTypes"],"Dynamic"); ec_base_throw = (["StdTypes"],"Dynamic"); + ec_avoid_wrapping = true; ec_special_throw = fun _ -> false; }; pf_scoping = { vs_scope = BlockScope; vs_flags = []; - } + }; + pf_supports_atomics = false; } let get_config com = - let defined f = PMap.mem (fst (Define.infos f)) com.defines.values in + let defined f = PMap.mem (Define.get_define_key f) com.defines.values in match com.platform with | Cross -> default_config @@ -447,13 +577,15 @@ let get_config com = ["js";"lib"],"Error"; ["haxe"],"Exception"; ]; + ec_avoid_wrapping = false; }; pf_scoping = { vs_scope = if es6 then BlockScope else FunctionScope; vs_flags = (if defined Define.JsUnflatten then ReserveAllTopLevelSymbols else ReserveAllTypesFlat) :: if es6 then [NoShadowing; SwitchCasesNoBlocks;] else [VarHoisting; NoCatchVarShadowing]; - } + }; + pf_supports_atomics = true; } | Lua -> { @@ -462,6 +594,9 @@ let get_config com = pf_capture_policy = CPLoopVars; pf_uses_utf16 = false; pf_supports_rest_args = true; + pf_exceptions = { default_config.pf_exceptions with + ec_avoid_wrapping = false; + } } | Neko -> { @@ -492,6 +627,7 @@ let get_config com = ["flash";"errors"],"Error"; ["haxe"],"Exception"; ]; + ec_avoid_wrapping = false; }; pf_scoping = { vs_scope = FunctionScope; @@ -530,8 +666,10 @@ let get_config com = pf_supports_threads = true; pf_supports_unicode = (defined Define.Cppia) || not (defined Define.DisableUnicodeStrings); pf_scoping = { default_config.pf_scoping with - vs_flags = [NoShadowing] - } + vs_flags = [NoShadowing]; + vs_scope = FunctionScope; + }; + pf_supports_atomics = true; } | Cs -> { @@ -542,7 +680,7 @@ let get_config com = pf_supports_threads = true; pf_supports_rest_args = true; pf_this_before_super = false; - pf_exceptions = { + pf_exceptions = { default_config.pf_exceptions with ec_native_throws = [ ["cs";"system"],"Exception"; ["haxe"],"Exception"; @@ -559,6 +697,7 @@ let get_config com = vs_scope = FunctionScope; vs_flags = [NoShadowing] }; + pf_supports_atomics = true; } | Java -> { @@ -588,7 +727,8 @@ let get_config com = { vs_scope = FunctionScope; vs_flags = [NoShadowing; ReserveAllTopLevelSymbols; ReserveNames(["_"])]; - } + }; + pf_supports_atomics = true; } | Python -> { @@ -619,14 +759,7 @@ let get_config com = pf_capture_policy = CPWrapRef; pf_pad_nulls = true; pf_supports_threads = true; - pf_exceptions = { default_config.pf_exceptions with - ec_native_throws = [ - ["haxe"],"Exception"; - ]; - ec_native_catches = [ - ["haxe"],"Exception"; - ]; - } + pf_supports_atomics = true; } | Eval -> { @@ -640,14 +773,11 @@ let get_config com = let memory_marker = [|Unix.time()|] -let create version s_version args = +let create compilation_step cs version args = let m = Type.mk_mono() in - let defines = - PMap.add "true" "1" ( - PMap.add "source_header" ("Generated by Haxe " ^ s_version) PMap.empty - ) - in - { + let rec com = { + compilation_step = compilation_step; + cs = cs; cache = None; stage = CCreated; version = version; @@ -672,6 +802,7 @@ let create version s_version args = config = default_config; print = (fun s -> print_string s; flush stdout); run_command = Sys.command; + run_command_args = (fun s args -> com.run_command (Printf.sprintf "%s %s" s (String.concat " " args))); std_path = []; class_path = []; main_class = None; @@ -680,6 +811,9 @@ let create version s_version args = types = []; callbacks = new compiler_callbacks; modules = []; + module_lut = new hashtbl_lookup; + module_nonexistent_lut = new hashtbl_lookup; + type_to_module = new hashtbl_lookup; main = None; flash_version = 10.; resources = Hashtbl.create 0; @@ -687,56 +821,69 @@ let create version s_version args = native_libs = create_native_libs(); net_path_map = Hashtbl.create 0; c_args = []; - neko_libs = []; + neko_lib_paths = []; include_files = []; js_gen = None; load_extern_type = []; defines = { defines_signature = None; - values = defines; + values = PMap.empty; }; + user_defines = Hashtbl.create 0; + user_metas = Hashtbl.create 0; get_macros = (fun() -> None); - info = (fun _ _ -> die "" __LOC__); - warning = (fun _ _ -> die "" __LOC__); - error = (fun _ _ -> die "" __LOC__); + info = (fun ?depth _ _ -> die "" __LOC__); + warning = (fun ?depth _ _ _ -> die "" __LOC__); + warning_options = []; + error = (fun ?depth _ _ -> die "" __LOC__); + located_error = (fun ?depth _ -> die "" __LOC__); get_messages = (fun() -> []); filter_messages = (fun _ -> ()); pass_debug_messages = DynArray.create(); - overload_cache = Hashtbl.create 0; basic = { tvoid = m; tint = m; tfloat = m; tbool = m; - tnull = (fun _ -> die "" __LOC__); + tnull = (fun _ -> die "Could use locate abstract Null (was it redefined?)" __LOC__); tstring = m; - tarray = (fun _ -> die "" __LOC__); + tarray = (fun _ -> die "Could not locate class Array (was it redefined?)" __LOC__); }; - file_lookup_cache = Hashtbl.create 0; + file_lookup_cache = new hashtbl_lookup; file_keys = new file_keys; - readdir_cache = Hashtbl.create 0; - module_to_file = Hashtbl.create 0; - stored_typed_exprs = PMap.empty; - cached_macros = Hashtbl.create 0; + readdir_cache = new hashtbl_lookup; + module_to_file = new hashtbl_lookup; + stored_typed_exprs = new hashtbl_lookup; + cached_macros = new hashtbl_lookup; memory_marker = memory_marker; - parser_cache = Hashtbl.create 0; + parser_cache = new hashtbl_lookup; + overload_cache = new hashtbl_lookup; json_out = None; - } + has_error = false; + report_mode = RMNone; + is_macro_context = false; + } in + com + +let is_diagnostics com = match com.report_mode with + | RMDiagnostics _ -> true + | _ -> false + +let disable_report_mode com = + let old = com.report_mode in + com.report_mode <- RMNone; + (fun () -> com.report_mode <- old) let log com str = if com.verbose then com.print (str ^ "\n") -let clone com = +let clone com is_macro_context = let t = com.basic in { com with cache = None; basic = { t with tvoid = t.tvoid }; main_class = None; features = Hashtbl.create 0; - file_lookup_cache = Hashtbl.create 0; - readdir_cache = Hashtbl.create 0; - parser_cache = Hashtbl.create 0; - module_to_file = Hashtbl.create 0; callbacks = new compiler_callbacks; display_information = { unresolved_identifiers = []; @@ -748,16 +895,18 @@ let clone com = defines_signature = com.defines.defines_signature; }; native_libs = create_native_libs(); - overload_cache = Hashtbl.create 0; + is_macro_context = is_macro_context; + file_lookup_cache = new hashtbl_lookup; + readdir_cache = new hashtbl_lookup; + parser_cache = new hashtbl_lookup; + module_to_file = new hashtbl_lookup; + overload_cache = new hashtbl_lookup; + module_lut = new hashtbl_lookup; + type_to_module = new hashtbl_lookup; } let file_time file = Extc.filetime file -let file_extension file = - match List.rev (ExtString.String.nsplit file ".") with - | e :: _ -> String.lowercase e - | [] -> "" - let flash_versions = List.map (fun v -> let maj = int_of_float v in let min = int_of_float (mod_float (v *. 10.) 10.) in @@ -792,26 +941,38 @@ let init_platform com pf = com.package_rules <- List.fold_left forbid com.package_rules ("jvm" :: (List.map platform_name platforms)); com.config <- get_config com; if com.config.pf_static then begin - raw_define_value com.defines "target.static" "true"; + raw_define com "target.static"; define com Define.Static; end; if com.config.pf_sys then begin - raw_define_value com.defines "target.sys" "true"; + raw_define com "target.sys"; define com Define.Sys end else com.package_rules <- PMap.add "sys" Forbidden com.package_rules; if com.config.pf_uses_utf16 then begin - raw_define_value com.defines "target.utf16" "true"; + raw_define com "target.utf16"; define com Define.Utf16; end; if com.config.pf_supports_threads then begin - raw_define_value com.defines "target.threaded" "true"; + raw_define com "target.threaded"; end; if com.config.pf_supports_unicode then begin - raw_define_value com.defines "target.unicode" "true"; + raw_define com "target.unicode"; end; raw_define_value com.defines "target.name" name; - raw_define com name + raw_define com name; + if com.config.pf_supports_atomics then begin + raw_define com "target.atomics" + end + +let set_platform com pf file = + if com.platform <> Cross then failwith "Multiple targets"; + init_platform com pf; + com.file <- file; + if (pf = Flash) && Path.file_extension file = "swc" then define com Define.Swc; + (* Set the source header, unless the user has set one already or the platform sets a custom one *) + if not (defined com Define.SourceHeader) && (pf <> Hl) then + define_value com Define.SourceHeader ("Generated by Haxe " ^ s_version_full) let add_feature com f = Hashtbl.replace com.features f true @@ -863,31 +1024,32 @@ let allow_package ctx s = with Not_found -> () -let abort msg p = raise (Abort (msg,p)) +let abort_located ?depth msg = raise (Abort msg) +let abort ?depth msg p = abort_located ~depth (located msg p) let platform ctx p = ctx.platform = p let platform_name_macro com = if defined com Define.Macro then "macro" else platform_name com.platform +let remove_extension file = + try String.sub file 0 (String.rindex file '.') + with Not_found -> file + +let extension file = + try + let dot_pos = String.rindex file '.' in + String.sub file dot_pos (String.length file - dot_pos) + with Not_found -> file + let cache_directory ctx class_path dir f_dir = let platform_ext = "." ^ (platform_name_macro ctx) and is_loading_core_api = defined ctx Define.CoreApi in - let remove_extension file = - try String.sub file 0 (String.rindex file '.') - with Not_found -> file - in - let extension file = - try - let dot_pos = String.rindex file '.' in - String.sub file dot_pos (String.length file - dot_pos) - with Not_found -> file - in let dir_listing = try Some (Sys.readdir dir); with Sys_error _ -> None in - Hashtbl.add ctx.readdir_cache (class_path,dir) dir_listing; + ctx.readdir_cache#add (class_path,dir) dir_listing; (* This function is invoked for each file in the `dir`. Each file is checked if it's specific for current platform @@ -921,23 +1083,23 @@ let cache_directory ctx class_path dir f_dir = - or this is a platform-specific file for `representation` - this `representation` was never found before *) - if is_loading_core_api || is_platform_specific || not (Hashtbl.mem ctx.file_lookup_cache representation) then begin + if is_loading_core_api || is_platform_specific || not (ctx.file_lookup_cache#mem representation) then begin let full_path = if dir = "." then file_own_name else dir ^ "/" ^ file_own_name in - Hashtbl.replace ctx.file_lookup_cache representation (Some full_path); + ctx.file_lookup_cache#add representation (Some full_path); end in Option.may (Array.iter prepare_file) dir_listing let find_file ctx f = try - match Hashtbl.find ctx.file_lookup_cache f with + match ctx.file_lookup_cache#find f with | None -> raise Exit | Some f -> f with | Exit -> raise Not_found | Not_found when Path.is_absolute_path f -> - Hashtbl.add ctx.file_lookup_cache f (Some f); + ctx.file_lookup_cache#add f (Some f); f | Not_found -> let f_dir = Filename.dirname f in @@ -949,13 +1111,13 @@ let find_file ctx f = let dir = Filename.dirname file in (* If we have seen the directory before, we can assume that the file isn't in there because the else case below would have added it to `file_lookup_cache`, which we check before we get here. *) - if Hashtbl.mem ctx.readdir_cache (p,dir) then + if ctx.readdir_cache#mem (p,dir) then loop (had_empty || p = "") l else begin cache_directory ctx p dir f_dir; (* Caching might have located the file we're looking for, so check the lookup cache again. *) try - begin match Hashtbl.find ctx.file_lookup_cache f with + begin match ctx.file_lookup_cache#find f with | Some f -> f | None -> raise Not_found end @@ -964,7 +1126,7 @@ let find_file ctx f = end in let r = try Some (loop false ctx.class_path) with Not_found -> None in - Hashtbl.add ctx.file_lookup_cache f r; + ctx.file_lookup_cache#add f r; match r with | None -> raise Not_found | Some f -> f @@ -1067,35 +1229,48 @@ let utf16_to_utf8 str = loop 0; Buffer.contents b -let add_diagnostics_message com s p kind sev = +let add_diagnostics_message ?(depth = 0) com msg kind sev = + if sev = MessageSeverity.Error then com.has_error <- true; let di = com.shared.shared_display_information in - di.diagnostics_messages <- (s,p,kind,sev) :: di.diagnostics_messages + match (extract_located msg) with + | [] -> () + | (s,p) :: [] -> + di.diagnostics_messages <- (s,p,kind,sev,depth) :: di.diagnostics_messages + | (s,p) :: stack -> + let stack_diag = (List.map (fun (s,p) -> (s,p,kind,sev,depth+1)) (List.rev stack)) in + di.diagnostics_messages <- stack_diag @ ((s,p,kind,sev,depth) :: di.diagnostics_messages) + +let located_display_error com ?(depth = 0) msg = + if is_diagnostics com then + add_diagnostics_message ~depth com msg MessageKind.DKCompilerMessage MessageSeverity.Error + else + com.located_error msg ~depth + +let display_error com ?(depth = 0) msg p = + located_display_error com ~depth (Globals.located msg p) open Printer -let dump_context com = s_record_fields "" [ - "version",string_of_int com.version; - "args",s_list ", " (fun s -> s) com.args; - "debug",string_of_bool com.debug; - "platform",platform_name com.platform; - "std_path",s_list ", " (fun s -> s) com.std_path; - "class_path",s_list ", " (fun s -> s) com.class_path; - "defines",s_pmap (fun s -> s) (fun s -> s) com.defines.values; - "defines_signature",s_opt (fun s -> Digest.to_hex s) com.defines.defines_signature; -] - let dump_path com = Define.defined_value_safe ~default:"dump" com.defines Define.DumpPath let adapt_defines_to_macro_context defines = - let values = ref defines.values in - List.iter (fun p -> values := PMap.remove (Globals.platform_name p) !values) Globals.platforms; - let to_remove = List.map (fun d -> fst (Define.infos d)) [Define.NoTraces] in - let to_remove = to_remove @ List.map (fun (_,d) -> "flash" ^ d) flash_versions in - values := PMap.foldi (fun k v acc -> if List.mem k to_remove then acc else PMap.add k v acc) !values PMap.empty; - values := PMap.add "macro" "1" !values; - values := PMap.add (platform_name !Globals.macro_platform) "1" !values; - {values = !values; defines_signature = None } + let to_remove = List.map Globals.platform_name Globals.platforms in + let to_remove = List.fold_left (fun acc d -> Define.get_define_key d :: acc) to_remove [Define.NoTraces] in + let to_remove = List.fold_left (fun acc (_, d) -> ("flash" ^ d) :: acc) to_remove flash_versions in + let macro_defines = { + values = PMap.foldi (fun k v acc -> + if List.mem k to_remove then acc else PMap.add k v acc) defines.values PMap.empty; + defines_signature = None + } in + Define.define macro_defines Define.Macro; + Define.raw_define macro_defines (platform_name !Globals.macro_platform); + macro_defines + +let adapt_defines_to_display_context defines = + let defines = adapt_defines_to_macro_context defines in + Define.define defines Define.Display; + defines let is_legacy_completion com = match com.json_out with | None -> true diff --git a/src/context/commonCache.ml b/src/context/commonCache.ml index 4ceb5583..08be2999 100644 --- a/src/context/commonCache.ml +++ b/src/context/commonCache.ml @@ -1,66 +1,76 @@ open Globals open Common -open CompilationServer open Type +class lib_build_task cs file ftime lib = object(self) + inherit CompilationCache.server_task ["build_lib";lib#get_name] 60 + + method private execute = + (* Created lookup and eagerly read each known type. *) + lib#load; + let h = Hashtbl.create 0 in + List.iter (fun path -> + if not (Hashtbl.mem h path) then begin + let p = { pfile = file ^ " @ " ^ Globals.s_type_path path; pmin = 0; pmax = 0; } in + try begin match lib#build path p with + | Some r -> Hashtbl.add h path r + | None -> () + end with _ -> + () + end + ) lib#list_modules; + lib#close; + (* Save and set up lookup. *) + cs#add_native_lib file h ftime; +end + let handle_native_lib com lib = com.native_libs.all_libs <- lib#get_file_path :: com.native_libs.all_libs; com.load_extern_type <- com.load_extern_type @ [lib#get_file_path,lib#build]; - match get() with - | Some cs when not (Define.raw_defined com.defines "haxe.noNativeLibsCache") -> + if not (Define.raw_defined com.defines "haxe.noNativeLibsCache") then begin + let cs = com.cs in let init () = let file = lib#get_file_path in - let key = file in let ftime = file_time file in - begin match cs#get_native_lib key with + begin match cs#get_native_lib file with | Some lib when ftime <= lib.c_nl_mtime -> (* Cached lib is good, set up lookup into cached files. *) - lib.c_nl_files; + Some lib.c_nl_files; | _ -> - (* Cached lib is outdated or doesn't exist yet, read library. *) - lib#load; - (* Created lookup and eagerly read each known type. *) - let h = Hashtbl.create 0 in - List.iter (fun path -> - if not (Hashtbl.mem h path) then begin - let p = { pfile = file ^ " @ " ^ Globals.s_type_path path; pmin = 0; pmax = 0; } in - try begin match lib#build path p with - | Some r -> Hashtbl.add h path r - | None -> () - end with _ -> - () - end - ) lib#list_modules; - (* Save and set up lookup. *) - cs#add_native_lib key h ftime; - h; + (* Cached lib is outdated or doesn't exist yet, register build task. *) + cs#add_task (new lib_build_task cs file ftime lib); + None end; in (fun () -> let lut = init() in - let build path p = - try Some (Hashtbl.find lut path) - with Not_found -> None - in - com.load_extern_type <- List.map (fun (name,f) -> - name,if name = lib#get_file_path then build else f - ) com.load_extern_type + match lut with + | Some lut -> + let build path p = + try Some (Hashtbl.find lut path) + with Not_found -> None + in + com.load_extern_type <- List.map (fun (name,f) -> + name,if name = lib#get_file_path then build else f + ) com.load_extern_type + | None -> + lib#load ) - | _ -> + end else (* Offline mode, just read library as usual. *) (fun () -> lib#load) (* context *) -let get_cache cs com = match com.Common.cache with +let get_cache com = match com.Common.cache with | None -> let sign = Define.get_signature com.defines in - cs#get_context sign + com.cs#get_context sign | Some cache -> cache let rec cache_context cs com = - let cc = get_cache cs com in + let cc = get_cache com in let sign = Define.get_signature com.defines in let cache_module m = (* If we have a signature mismatch, look-up cache for module. Physical equality check is fine as a heueristic. *) @@ -76,9 +86,7 @@ let maybe_add_context_sign cs com desc = let sign = Define.get_signature com.defines in ignore(cs#add_info sign desc com.platform com.class_path com.defines) -let lock_signature com name = match CompilationServer.get() with - | Some cs -> - maybe_add_context_sign cs com name; - com.cache <- Some (get_cache cs com) - | None -> - () \ No newline at end of file +let lock_signature com name = + let cs = com.cs in + maybe_add_context_sign cs com name; + com.cache <- Some (get_cache com) \ No newline at end of file diff --git a/src/context/display/deprecationCheck.ml b/src/context/display/deprecationCheck.ml index 2d238283..a0983032 100644 --- a/src/context/display/deprecationCheck.ml +++ b/src/context/display/deprecationCheck.ml @@ -4,6 +4,7 @@ open Common open Ast let curclass = ref null_class +let curfield = ref null_field let warned_positions = Hashtbl.create 0 @@ -11,9 +12,10 @@ let warn_deprecation com s p_usage = let pkey p = (p.pfile,p.pmin) in if not (Hashtbl.mem warned_positions (pkey p_usage)) then begin Hashtbl.add warned_positions (pkey p_usage) (s,p_usage); - match com.display.dms_kind with - | DMDiagnostics _ -> () - | _ -> com.warning s p_usage; + if not (is_diagnostics com) then begin + let options = Warning.from_meta (!curclass.cl_meta @ !curfield.cf_meta) in + com.warning WDeprecated options s p_usage; + end end let print_deprecation_message com meta s p_usage = @@ -81,7 +83,14 @@ let run_on_expr com e = in expr e -let run_on_field com cf = match cf.cf_expr with None -> () | Some e -> run_on_expr com e +let run_on_field com cf = + match cf.cf_expr with + | None -> + () + | Some e -> + curfield := cf; + run_on_expr com e; + curfield := null_field let run com = List.iter (fun t -> match t with diff --git a/src/context/display/diagnostics.ml b/src/context/display/diagnostics.ml index 07d4a96a..5449c419 100644 --- a/src/context/display/diagnostics.ml +++ b/src/context/display/diagnostics.ml @@ -11,7 +11,10 @@ open DisplayException let add_removable_code ctx s p prange = ctx.removable_code <- (s,p,prange) :: ctx.removable_code -let is_diagnostics_run com p = DiagnosticsPrinter.is_diagnostics_file (com.file_keys#get p.pfile) +let error_in_diagnostics_run com p = + let b = DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get p.pfile) in + if b then com.has_error <- true; + b let find_unused_variables com e = let vars = Hashtbl.create 0 in @@ -40,10 +43,10 @@ let find_unused_variables com e = let check_other_things com e = let had_effect = ref false in let no_effect p = - add_diagnostics_message com "This code has no effect" p DKCompilerError DiagnosticsSeverity.Warning; + add_diagnostics_message com (located "This code has no effect" p) DKCompilerMessage Warning; in let pointless_compound s p = - add_diagnostics_message com (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p DKCompilerError DiagnosticsSeverity.Warning; + add_diagnostics_message com (located (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p) DKCompilerMessage Warning; in let rec compound s el p = let old = !had_effect in @@ -97,17 +100,10 @@ let prepare_field dctx com cf = match cf.cf_expr with check_other_things com e; DeprecationCheck.run_on_expr ~force:true com e -let prepare com = - let dctx = { - removable_code = []; - import_positions = PMap.empty; - dead_blocks = Hashtbl.create 0; - diagnostics_messages = []; - unresolved_identifiers = []; - missing_fields = PMap.empty; - } in +let collect_diagnostics dctx com = + let open CompilationCache in List.iter (function - | TClassDecl c when DiagnosticsPrinter.is_diagnostics_file (com.file_keys#get c.cl_pos.pfile) -> + | TClassDecl c when DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get c.cl_pos.pfile) -> List.iter (prepare_field dctx com) c.cl_ordered_fields; List.iter (prepare_field dctx com) c.cl_ordered_statics; (match c.cl_constructor with None -> () | Some cf -> prepare_field dctx com cf); @@ -116,14 +112,13 @@ let prepare com = ) com.types; let handle_dead_blocks com = match com.cache with | Some cc -> - let macro_defines = adapt_defines_to_macro_context com.defines in - let display_defines = {macro_defines with values = PMap.add "display" "1" macro_defines.values} in + let display_defines = adapt_defines_to_display_context com.defines in let is_true defines e = ParserEntry.is_true (ParserEntry.eval defines e) in Hashtbl.iter (fun file_key cfile -> - if DisplayPosition.display_position#is_in_file (com.file_keys#get cfile.CompilationServer.c_file_path) then begin - let dead_blocks = cfile.CompilationServer.c_pdi.pd_dead_blocks in + if DisplayPosition.display_position#is_in_file (com.file_keys#get cfile.c_file_path) then begin + let dead_blocks = cfile.c_pdi.pd_dead_blocks in let dead_blocks = List.filter (fun (_,e) -> not (is_true display_defines e)) dead_blocks in try let dead_blocks2 = Hashtbl.find dctx.dead_blocks file_key in @@ -137,7 +132,19 @@ let prepare com = | None -> () in - handle_dead_blocks com; + handle_dead_blocks com + +let prepare com = + let dctx = { + removable_code = []; + import_positions = PMap.empty; + dead_blocks = Hashtbl.create 0; + diagnostics_messages = []; + unresolved_identifiers = []; + missing_fields = PMap.empty; + } in + if not (List.exists (fun (_,_,_,sev,_) -> sev = MessageSeverity.Error) com.shared.shared_display_information.diagnostics_messages) then + collect_diagnostics dctx com; let process_modules com = List.iter (fun m -> PMap.iter (fun p b -> @@ -171,12 +178,14 @@ let prepare com = dctx let secure_generated_code ctx e = - if is_diagnostics_run ctx.com e.epos then mk (TMeta((Meta.Extern,[],e.epos),e)) e.etype e.epos else e + (* This causes problems and sucks in general... need a different solution. But I forgot which problem this solved anyway. *) + (* mk (TMeta((Meta.Extern,[],e.epos),e)) e.etype e.epos *) + e let print com = let dctx = prepare com in - Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics dctx) + Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics com dctx) let run com = let dctx = prepare com in - DisplayException.raise_diagnostics dctx \ No newline at end of file + dctx \ No newline at end of file diff --git a/src/context/display/diagnosticsPrinter.ml b/src/context/display/diagnosticsPrinter.ml index 25c99bbb..1e953ac7 100644 --- a/src/context/display/diagnosticsPrinter.ml +++ b/src/context/display/diagnosticsPrinter.ml @@ -2,17 +2,31 @@ open Globals open Common open Json open DisplayTypes -open DiagnosticsKind open DisplayTypes open Type open Genjson +open MessageKind -type t = DiagnosticsKind.t * pos +type t = { + diag_kind : MessageKind.t; + diag_pos : pos; + diag_severity : MessageSeverity.t; + diag_args : Json.t; + mutable diag_related_information : (pos * int * string) list; +} -let is_diagnostics_file file_key = - match (!Parser.display_mode) with - | DMDiagnostics [] -> true - | DMDiagnostics file_keys -> List.exists (fun key' -> file_key = key') file_keys +let make_diagnostic kd p sev args = { + diag_kind = kd; + diag_pos = p; + diag_severity = sev; + diag_args = args; + diag_related_information = []; +} + +let is_diagnostics_file com file_key = + match com.report_mode with + | RMDiagnostics [] -> true + | RMDiagnostics file_keys -> List.exists (fun key' -> file_key = key') file_keys | _ -> false module UnresolvedIdentifierSuggestion = struct @@ -29,19 +43,21 @@ open UnresolvedIdentifierSuggestion open CompletionItem open CompletionModuleType -let json_of_diagnostics dctx = - let diag = Hashtbl.create 0 in - let add append dk p sev args = +let json_of_diagnostics com dctx = + let diagnostics = Hashtbl.create 0 in + let current = ref None in + let add append diag = + let p = diag.diag_pos in let file = if p = null_pos then p.pfile else Path.get_real_path p.pfile in - let diag = try - Hashtbl.find diag file + let fdiag = try + Hashtbl.find diagnostics file with Not_found -> - let d = Hashtbl.create 0 in - Hashtbl.add diag file d; + let d = [] in + Hashtbl.add diagnostics file d; d in - if append || not (Hashtbl.mem diag p) then - Hashtbl.add diag p (dk,p,sev,args) + if append || (List.find_opt (fun diag -> diag.diag_pos = p) fdiag) = None then + Hashtbl.replace diagnostics file (diag :: fdiag) in let file_keys = new Common.file_keys in let add dk p sev args = @@ -52,12 +68,16 @@ let json_of_diagnostics dctx = | DKInactiveBlock -> false | DKUnresolvedIdentifier - | DKCompilerError + | DKCompilerMessage | DKParserError | DKMissingFields -> true in - if p = null_pos || is_diagnostics_file (file_keys#get p.pfile) then add append dk p sev args + if p = null_pos || is_diagnostics_file com (file_keys#get p.pfile) then begin + let diag = make_diagnostic dk p sev args in + current := Some diag; + add append diag + end else current := None in List.iter (fun (s,p,suggestions) -> let suggestions = ExtList.List.filter_map (fun (s,item,r) -> @@ -77,10 +97,24 @@ let json_of_diagnostics dctx = "name",JString s; ]) ) suggestions in - add DKUnresolvedIdentifier p DiagnosticsSeverity.Error (JArray suggestions); + add DKUnresolvedIdentifier p MessageSeverity.Error (JArray suggestions); ) dctx.unresolved_identifiers; - List.iter (fun (s,p,kind,sev) -> - add kind p sev (JString s) + List.iter (fun (s,p,kind,sev,depth) -> match (depth, !current) with + | d, Some diag when d > 0 -> + let lines = ExtString.String.nsplit s "\n" in + (match lines with + | [] -> () + | s :: sub -> + let related = List.fold_left (fun acc s -> (p,d,Error.compl_msg s) :: acc) diag.diag_related_information sub in + diag.diag_related_information <- (p,d,s) :: related; + ) + | 0, _ -> + add kind p sev (JString s) + | _ -> + (* Do not add errors with depth greater than one as top level diagnostic. *) + (* This could happen when running diagnostics for a file that is wentioned in *) + (* sub errors of a file not included for diagnostics. *) + () ) (List.rev dctx.diagnostics_messages); PMap.iter (fun p (mt,mfl) -> let jctx = create_context GMMinimum in @@ -143,17 +177,22 @@ let json_of_diagnostics dctx = "moduleFile",jstring (Path.UniqueKey.lazy_path (t_infos mt).mt_module.m_extra.m_file); "entries",jarray l ] in - add DKMissingFields p DiagnosticsSeverity.Error j + add DKMissingFields p MessageSeverity.Error j ) dctx.missing_fields; (* non-append from here *) - Hashtbl.iter (fun _ (s,p) -> - add DKDeprecationWarning p DiagnosticsSeverity.Warning (JString s); - ) DeprecationCheck.warned_positions; + begin match Warning.get_mode WDeprecated com.warning_options with + | WMEnable -> + Hashtbl.iter (fun _ (s,p) -> + add DKDeprecationWarning p MessageSeverity.Warning (JString s); + ) DeprecationCheck.warned_positions; + | WMDisable -> + () + end; PMap.iter (fun p r -> - if not !r then add DKUnusedImport p DiagnosticsSeverity.Warning (JArray []) + if not !r then add DKUnusedImport p MessageSeverity.Warning (JArray []) ) dctx.import_positions; List.iter (fun (s,p,prange) -> - add DKRemovableCode p DiagnosticsSeverity.Warning (JObject ["description",JString s;"range",if prange = null_pos then JNull else Genjson.generate_pos_as_range prange]) + add DKRemovableCode p MessageSeverity.Warning (JObject ["description",JString s;"range",if prange = null_pos then JNull else Genjson.generate_pos_as_range prange]) ) dctx.removable_code; Hashtbl.iter (fun file ranges -> List.iter (fun (p,e) -> @@ -162,22 +201,29 @@ let json_of_diagnostics dctx = "string",JString (Ast.Printer.s_expr e) ] ] in - add DKInactiveBlock p DiagnosticsSeverity.Hint jo + add DKInactiveBlock p MessageSeverity.Hint jo ) ranges ) dctx.dead_blocks; let jl = Hashtbl.fold (fun file diag acc -> - let jl = Hashtbl.fold (fun _ (dk,p,sev,jargs) acc -> + let jl = List.rev_map (fun diag -> (JObject [ - "kind",JInt (DiagnosticsKind.to_int dk); - "severity",JInt (DiagnosticsSeverity.to_int sev); - "range",Genjson.generate_pos_as_range p; - "args",jargs - ]) :: acc - ) diag [] in + "kind",JInt (MessageKind.to_int diag.diag_kind); + "severity",JInt (MessageSeverity.to_int diag.diag_severity); + "range",Genjson.generate_pos_as_range diag.diag_pos; + "args",diag.diag_args; + "relatedInformation",JArray ( + List.rev_map (fun (pos,depth,msg) -> (JObject [ + "location",Genjson.generate_pos_as_location pos; + "depth",JInt depth; + "message",JString msg; + ])) diag.diag_related_information + ) + ]) + ) diag in (JObject [ "file",if file = "?" then JNull else JString file; "diagnostics",JArray jl ]) :: acc - ) diag [] in + ) diagnostics [] in let js = JArray jl in - js \ No newline at end of file + js diff --git a/src/context/display/display.ml b/src/context/display/display.ml index 03c47de4..731f43e7 100644 --- a/src/context/display/display.ml +++ b/src/context/display/display.ml @@ -70,6 +70,8 @@ module ExprPreprocessing = struct let loop e = (* print_endline (Printf.sprintf "%i-%i: %s" (pos e).pmin (pos e).pmax (Ast.s_expr e)); *) match fst e with + | EFunction(FKNamed((_,p),_),_) when is_annotated p && is_completion -> + raise Exit | EVars vl when is_annotated (pos e) && is_completion -> let rec loop2 acc mark vl = match vl with | v :: vl -> diff --git a/src/context/display/displayEmitter.ml b/src/context/display/displayEmitter.ml index 9fc0209a..9609d681 100644 --- a/src/context/display/displayEmitter.ml +++ b/src/context/display/displayEmitter.ml @@ -109,7 +109,7 @@ let display_field ctx origin scope cf p = match ctx.com.display.dms_kind with (* For constructors, we care about the class name so we don't end up looking for "new". *) snd c.cl_path,SKConstructor cf | _,(Self (TClassDecl c) | Parent(TClassDecl c)) -> - cf.cf_name,SKField (cf,Some c.cl_path) + cf.cf_name,SKField (cf,Some c) | _ -> cf.cf_name,SKField (cf,None) in @@ -121,7 +121,7 @@ let display_field ctx origin scope cf p = match ctx.com.display.dms_kind with cf in let cf = match origin,scope,follow cf.cf_type with - | Self (TClassDecl c),CFSConstructor,TFun(tl,_) -> {cf with cf_type = TFun(tl,TInst(c,List.map snd c.cl_params))} + | Self (TClassDecl c),CFSConstructor,TFun(tl,_) -> {cf with cf_type = TFun(tl,TInst(c,extract_param_types c.cl_params))} | _ -> cf in let ct = CompletionType.from_type (get_import_status ctx) ~values:(get_value_meta cf.cf_meta) cf.cf_type in @@ -145,7 +145,7 @@ let display_meta com meta p = match com.display.dms_kind with begin match meta with | Meta.Custom _ | Meta.Dollar _ -> () | _ -> - if com.json_out = None then begin match Meta.get_documentation meta with + if com.json_out = None then begin match Meta.get_documentation com.user_metas meta with | None -> () | Some (_,s) -> raise_metadata ("" ^ s ^ "") @@ -153,9 +153,9 @@ let display_meta com meta p = match com.display.dms_kind with raise_hover (make_ci_metadata meta) None p end | DMDefault -> - let all = Meta.get_all() in + let all = Meta.get_all com.user_metas in let all = List.map make_ci_metadata all in - let subject = if meta = Meta.Last then None else Some (Meta.to_string meta) in + let subject = if meta = Meta.HxCompletion then None else Some (Meta.to_string meta) in raise_fields all CRMetadata (make_subject subject p); | _ -> () diff --git a/src/context/display/displayException.ml b/src/context/display/displayException.ml index 4eb7f5a0..a731f9a1 100644 --- a/src/context/display/displayException.ml +++ b/src/context/display/displayException.ml @@ -7,14 +7,12 @@ open Genjson exception DisplayException of display_exception_kind -let raise_diagnostics s = raise (DisplayException(DisplayDiagnostics s)) -let raise_statistics s = raise (DisplayException(Statistics s)) let raise_module_symbols s = raise (DisplayException(ModuleSymbols s)) let raise_metadata s = raise (DisplayException(Metadata s)) -let raise_signatures l isig iarg kind = raise (DisplayException(DisplaySignatures(Some(l,isig,iarg,kind)))) -let raise_hover item expected p = raise (DisplayException(DisplayHover(Some {hitem = item;hpos = p;hexpected = expected}))) +let raise_signatures l isig iarg kind = raise (DisplayException(DisplaySignatures((l,isig,iarg,kind)))) +let raise_hover item expected p = raise (DisplayException(DisplayHover({hitem = item;hpos = p;hexpected = expected}))) let raise_positions pl = raise (DisplayException(DisplayPositions pl)) -let raise_fields ckl cr subj = raise (DisplayException(DisplayFields(Some({fitems = ckl;fkind = cr;fsubject = subj})))) +let raise_fields ckl cr subj = raise (DisplayException(DisplayFields({fitems = ckl;fkind = cr;fsubject = subj}))) let raise_package sl = raise (DisplayException(DisplayPackage sl)) (* global state *) @@ -159,14 +157,9 @@ let arg_index signatures signature_index param_index = let to_json ctx de = match de with - | Statistics _ | ModuleSymbols _ | Metadata _ -> die "" __LOC__ - | DisplaySignatures None -> - jnull - | DisplayDiagnostics dctx -> - DiagnosticsPrinter.json_of_diagnostics dctx - | DisplaySignatures Some(sigs,isig,iarg,kind) -> + | DisplaySignatures(sigs,isig,iarg,kind) -> (* We always want full info for signatures *) let ctx = Genjson.create_context GMFull in let fsig ((_,signature),doc) = @@ -184,9 +177,7 @@ let to_json ctx de = "signatures",jlist fsig sigs; "kind",jint sigkind; ] - | DisplayHover None -> - jnull - | DisplayHover (Some hover) -> + | DisplayHover hover -> let named_source_kind = function | WithType.FunctionArgument name -> (0, name) | WithType.StructureField name -> (1, name) @@ -225,9 +216,9 @@ let to_json ctx de = ] | DisplayPositions pl -> jarray (List.map generate_pos_as_location pl) - | DisplayFields None -> - jnull - | DisplayFields Some r -> + | DisplayFields r -> fields_to_json ctx r.fitems r.fkind r.fsubject | DisplayPackage pack -> - jarray (List.map jstring pack) \ No newline at end of file + jarray (List.map jstring pack) + | DisplayNoResult -> + jnull \ No newline at end of file diff --git a/src/context/display/displayFields.ml b/src/context/display/displayFields.ml index 8d5f6098..98951773 100644 --- a/src/context/display/displayFields.ml +++ b/src/context/display/displayFields.ml @@ -28,7 +28,7 @@ open DisplayTypes open Display let get_submodule_fields ctx path = - let m = Hashtbl.find ctx.g.modules path in + let m = ctx.com.module_lut#find path in let tl = List.filter (fun t -> path <> (t_infos t).mt_path && not (t_infos t).mt_private) m.m_types in let tl = List.map (fun mt -> make_ci_type (CompletionItem.CompletionModuleType.of_module_type mt) ImportStatus.Imported None @@ -46,48 +46,50 @@ let collect_static_extensions ctx items e p = | _ -> t in + let rec dup t = Type.map dup t in + let handle_field c f acc = + let f = { f with cf_type = opt_type f.cf_type } in + let monos = List.map (fun _ -> spawn_monomorph ctx p) f.cf_params in + let map = apply_params f.cf_params monos in + match follow (map f.cf_type) with + | TFun((_,_,TType({t_path=["haxe";"macro"], "ExprOf"}, [t])) :: args, ret) + | TFun((_,_,t) :: args, ret) -> + begin try + let e = TyperBase.unify_static_extension ctx {e with etype = dup e.etype} t p in + List.iter2 (fun m tp -> match follow tp.ttp_type with + | TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] -> + List.iter (fun tc -> unify_raise m (map tc) e.epos) constr + | _ -> () + ) monos f.cf_params; + if not (can_access ctx c f true) || follow e.etype == t_dynamic && follow t != t_dynamic then + acc + else begin + let f = prepare_using_field f in + let f = { f with cf_params = []; cf_flags = set_flag f.cf_flags (int_of_class_field_flag CfPublic); cf_type = TFun(args,ret) } in + let decl = match c.cl_kind with + | KAbstractImpl a -> TAbstractDecl a + | _ -> TClassDecl c + in + let origin = StaticExtension(decl) in + let ct = CompletionType.from_type (get_import_status ctx) ~values:(get_value_meta f.cf_meta) f.cf_type in + let item = make_ci_class_field (CompletionClassField.make f CFSMember origin true) (f.cf_type,ct) in + PMap.add f.cf_name item acc + end + with Error (Unify _,_,_) | Unify_error _ -> + acc + end + | _ -> + acc + in let rec loop acc = function | [] -> acc | (c,_) :: l -> - let rec dup t = Type.map dup t in let acc = List.fold_left (fun acc f -> if Meta.has Meta.NoUsing f.cf_meta || Meta.has Meta.NoCompletion f.cf_meta || has_class_field_flag f CfImpl || PMap.mem f.cf_name acc then acc - else begin - let f = { f with cf_type = opt_type f.cf_type } in - let monos = List.map (fun _ -> spawn_monomorph ctx p) f.cf_params in - let map = apply_params f.cf_params monos in - match follow (map f.cf_type) with - | TFun((_,_,TType({t_path=["haxe";"macro"], "ExprOf"}, [t])) :: args, ret) - | TFun((_,_,t) :: args, ret) -> - begin try - let e = TyperBase.unify_static_extension ctx {e with etype = dup e.etype} t p in - List.iter2 (fun m (name,t) -> match follow t with - | TInst ({ cl_kind = KTypeParameter constr },_) when constr <> [] -> - List.iter (fun tc -> unify_raise ctx m (map tc) e.epos) constr - | _ -> () - ) monos f.cf_params; - if not (can_access ctx c f true) || follow e.etype == t_dynamic && follow t != t_dynamic then - acc - else begin - let f = prepare_using_field f in - let f = { f with cf_params = []; cf_flags = set_flag f.cf_flags (int_of_class_field_flag CfPublic); cf_type = TFun(args,ret) } in - let decl = match c.cl_kind with - | KAbstractImpl a -> TAbstractDecl a - | _ -> TClassDecl c - in - let origin = StaticExtension(decl) in - let ct = CompletionType.from_type (get_import_status ctx) ~values:(get_value_meta f.cf_meta) f.cf_type in - let item = make_ci_class_field (CompletionClassField.make f CFSMember origin true) (f.cf_type,ct) in - PMap.add f.cf_name item acc - end - with Error (Unify _,_) | Unify_error _ -> - acc - end - | _ -> - acc - end + else + List.fold_left (fun acc f -> handle_field c f acc) acc (f :: f.cf_overloads) ) acc c.cl_ordered_statics in loop acc l in diff --git a/src/context/display/displayJson.ml b/src/context/display/displayJson.ml index ba8bb1df..3792bf7b 100644 --- a/src/context/display/displayJson.ml +++ b/src/context/display/displayJson.ml @@ -8,6 +8,7 @@ open DisplayTypes.DisplayMode open Timer open Genjson open Type +open DisplayProcessingGlobals (* Generate the JSON of our times. *) let json_of_times root = @@ -39,12 +40,12 @@ let create_json_context jsonrpc may_resolve = Genjson.create_context ~jsonrpc:jsonrpc (if may_resolve && !supports_resolve then GMMinimum else GMFull) let send_string j = - raise (DisplayOutput.Completion j) + raise (Completion j) let send_json json = send_string (string_of_json json) -class display_handler (jsonrpc : jsonrpc_handler) com (cs : CompilationServer.t) = object(self) +class display_handler (jsonrpc : jsonrpc_handler) com (cs : CompilationCache.t) = object(self) val cs = cs; method get_cs = cs @@ -58,7 +59,7 @@ class display_handler (jsonrpc : jsonrpc_handler) com (cs : CompilationServer.t) let file = jsonrpc#get_opt_param (fun () -> let file = jsonrpc#get_string_param "file" in Path.get_full_path file - ) DisplayOutput.file_input_marker in + ) file_input_marker in let pos = if requires_offset then jsonrpc#get_int_param "offset" else (-1) in TypeloadParse.current_stdin := jsonrpc#get_opt_param (fun () -> let s = jsonrpc#get_string_param "contents" in @@ -82,7 +83,7 @@ type handler_context = { } let handler = - let open CompilationServer in + let open CompilationCache in let h = Hashtbl.create 0 in let l = [ "initialize", (fun hctx -> @@ -168,6 +169,7 @@ let handler = ); "server/contexts", (fun hctx -> let l = List.map (fun cc -> cc#get_json) hctx.display#get_cs#get_contexts in + let l = List.filter (fun json -> json <> JNull) l in hctx.send_result (jarray l) ); "server/modules", (fun hctx -> @@ -187,7 +189,59 @@ let handler = with Not_found -> hctx.send_error [jstring "No such module"] in - hctx.send_result (generate_module () m) + hctx.send_result (generate_module cc m) + ); + "server/type", (fun hctx -> + let sign = Digest.from_hex (hctx.jsonrpc#get_string_param "signature") in + let path = Path.parse_path (hctx.jsonrpc#get_string_param "modulePath") in + let typeName = hctx.jsonrpc#get_string_param "typeName" in + let cc = hctx.display#get_cs#get_context sign in + let m = try + cc#find_module path + with Not_found -> + hctx.send_error [jstring "No such module"] + in + let rec loop mtl = match mtl with + | [] -> + hctx.send_error [jstring "No such type"] + | mt :: mtl -> + begin match mt with + | TClassDecl c -> c.cl_restore() + | _ -> () + end; + let infos = t_infos mt in + if snd infos.mt_path = typeName then begin + let ctx = Genjson.create_context GMMinimum in + hctx.send_result (Genjson.generate_module_type ctx mt) + end else + loop mtl + in + loop m.m_types + ); + "server/typeContexts", (fun hctx -> + let path = Path.parse_path (hctx.jsonrpc#get_string_param "modulePath") in + let typeName = hctx.jsonrpc#get_string_param "typeName" in + let contexts = hctx.display#get_cs#get_contexts in + + hctx.send_result (jarray (List.fold_left (fun acc cc -> + match cc#find_module_opt path with + | None -> acc + | Some(m) -> + let rec loop mtl = match mtl with + | [] -> + acc + | mt :: mtl -> + begin match mt with + | TClassDecl c -> c.cl_restore() + | _ -> () + end; + if snd (t_infos mt).mt_path = typeName then + cc#get_json :: acc + else + loop mtl + in + loop m.m_types + ) [] contexts)) ); "server/moduleCreated", (fun hctx -> let file = hctx.jsonrpc#get_string_param "file" in @@ -218,7 +272,7 @@ let handler = let file = hctx.jsonrpc#get_string_param "file" in let fkey = hctx.com.file_keys#get file in let cs = hctx.display#get_cs in - cs#taint_modules fkey; + cs#taint_modules fkey "server/invalidate"; cs#remove_files fkey; hctx.send_result jnull ); @@ -279,6 +333,8 @@ let parse_input com input report_times = let jsonrpc = new jsonrpc_handler input in let send_result json = + flush stdout; + flush stderr; let fl = [ "result",json; "timestamp",jfloat (Unix.gettimeofday ()); @@ -310,10 +366,7 @@ let parse_input com input report_times = jsonrpc = jsonrpc }); - let cs = match CompilationServer.get() with - | Some cs -> cs - | None -> send_error [jstring "compilation server not running for some reason"]; - in + let cs = com.cs in let display = new display_handler jsonrpc com cs in diff --git a/src/context/display/displayPath.ml b/src/context/display/displayPath.ml index 47a6cc4f..6ad580d4 100644 --- a/src/context/display/displayPath.ml +++ b/src/context/display/displayPath.ml @@ -53,7 +53,7 @@ module TypePathHandler = struct else packages := f :: !packages end; - end else if file_extension f = "hx" && f <> "import.hx" then begin + end else if Path.file_extension f = "hx" && f <> "import.hx" then begin let c = Filename.chop_extension f in try ignore(String.index c '.') diff --git a/src/context/display/displayTexpr.ml b/src/context/display/displayTexpr.ml index e7a686d1..3a7fa3c3 100644 --- a/src/context/display/displayTexpr.ml +++ b/src/context/display/displayTexpr.ml @@ -5,7 +5,7 @@ open Type open Typecore open DisplayPosition open CompletionItem -open CompilationServer +open CompilationCache open ClassFieldOrigin let find_field_by_position sc p = @@ -18,49 +18,62 @@ let find_enum_field_by_position sc p = if pos eff.ec_name = p then true else false ) sc.d_data -let find_class_by_position cfile p = +let find_class_by_position decls p = let rec loop dl = match dl with | (EClass c,_) :: dl when pos c.d_name = p -> c | _ :: dl -> loop dl | [] -> raise Not_found in - loop cfile.c_decls + loop decls -let find_enum_by_position cfile p = +let find_module_static_by_position decls p = + let rec loop dl = match dl with + | (EStatic d,_) :: dl when pos d.d_name = p -> d + | _ :: dl -> loop dl + | [] -> raise Not_found + in + loop decls + +let find_enum_by_position decls p = let rec loop dl = match dl with | (EEnum en,_) :: dl when pos en.d_name = p -> en | _ :: dl -> loop dl | [] -> raise Not_found in - loop cfile.c_decls + loop decls -let find_typedef_by_position cfile p = +let find_typedef_by_position decls p = let rec loop dl = match dl with | (ETypedef td,_) :: dl when pos td.d_name = p -> td | _ :: dl -> loop dl | [] -> raise Not_found in - loop cfile.c_decls + loop decls -let find_abstract_by_position cfile p = +let find_abstract_by_position decls p = let rec loop dl = match dl with | (EAbstract a,_) :: dl when pos a.d_name = p -> a | _ :: dl -> loop dl | [] -> raise Not_found in - loop cfile.c_decls + loop decls -let check_display_field ctx sc c cf = - let cff = find_field_by_position sc cf.cf_name_pos in +let actually_check_display_field ctx c cff p = let context_init = new TypeloadFields.context_init in - let ctx,cctx = TypeloadFields.create_class_context ctx c context_init cf.cf_pos in + let cctx = TypeloadFields.create_class_context c context_init p in + let ctx = TypeloadFields.create_typer_context_for_class ctx cctx p in let cff = TypeloadFields.transform_field (ctx,cctx) c cff (ref []) (pos cff.cff_name) in - let ctx,fctx = TypeloadFields.create_field_context (ctx,cctx) c cff in + let display_modifier = Typeload.check_field_access ctx cff in + let fctx = TypeloadFields.create_field_context cctx cff true display_modifier in let cf = TypeloadFields.init_field (ctx,cctx,fctx) cff in flush_pass ctx PTypeField "check_display_field"; ignore(follow cf.cf_type) -let check_display_class ctx cc cfile c = +let check_display_field ctx sc c cf = + let cff = find_field_by_position sc cf.cf_name_pos in + actually_check_display_field ctx c cff cf.cf_pos + +let check_display_class ctx decls c = let check_field sc cf = if display_position#enclosed_in cf.cf_pos then check_display_field ctx sc c cf; @@ -72,12 +85,12 @@ let check_display_class ctx cc cfile c = in match c.cl_kind with | KAbstractImpl a -> - let sa = find_abstract_by_position cfile c.cl_name_pos in + let sa = find_abstract_by_position decls c.cl_name_pos in let check_field = check_field sa in List.iter check_field c.cl_ordered_statics; | _ -> - let sc = find_class_by_position cfile c.cl_name_pos in - ignore(Typeload.type_type_params ctx c.cl_path (fun() -> c.cl_params) null_pos sc.d_params); + let sc = find_class_by_position decls c.cl_name_pos in + ignore(Typeload.type_type_params ctx TPHType c.cl_path (fun() -> c.cl_params) null_pos sc.d_params); List.iter (function | (HExtends(ct,p) | HImplements(ct,p)) when display_position#enclosed_in p -> ignore(Typeload.load_instance ~allow_display:true ctx (ct,p) false) @@ -89,24 +102,24 @@ let check_display_class ctx cc cfile c = List.iter check_field c.cl_ordered_fields; Option.may check_field c.cl_constructor -let check_display_enum ctx cc cfile en = - let se = find_enum_by_position cfile en.e_name_pos in - ignore(Typeload.type_type_params ctx en.e_path (fun() -> en.e_params) null_pos se.d_params); +let check_display_enum ctx decls en = + let se = find_enum_by_position decls en.e_name_pos in + ignore(Typeload.type_type_params ctx TPHType en.e_path (fun() -> en.e_params) null_pos se.d_params); PMap.iter (fun _ ef -> if display_position#enclosed_in ef.ef_pos then begin let sef = find_enum_field_by_position se ef.ef_name_pos in - ignore(TypeloadModule.load_enum_field ctx en (TEnum (en,List.map snd en.e_params)) (ref false) (ref 0) sef) + ignore(TypeloadModule.TypeLevel.load_enum_field ctx en (TEnum (en,extract_param_types en.e_params)) (ref false) (ref 0) sef) end ) en.e_constrs -let check_display_typedef ctx cc cfile td = - let st = find_typedef_by_position cfile td.t_name_pos in - ignore(Typeload.type_type_params ctx td.t_path (fun() -> td.t_params) null_pos st.d_params); +let check_display_typedef ctx decls td = + let st = find_typedef_by_position decls td.t_name_pos in + ignore(Typeload.type_type_params ctx TPHType td.t_path (fun() -> td.t_params) null_pos st.d_params); ignore(Typeload.load_complex_type ctx true st.d_data) -let check_display_abstract ctx cc cfile a = - let sa = find_abstract_by_position cfile a.a_name_pos in - ignore(Typeload.type_type_params ctx a.a_path (fun() -> a.a_params) null_pos sa.d_params); +let check_display_abstract ctx decls a = + let sa = find_abstract_by_position decls a.a_name_pos in + ignore(Typeload.type_type_params ctx TPHType a.a_path (fun() -> a.a_params) null_pos sa.d_params); List.iter (function | (AbOver(ct,p) | AbFrom(ct,p) | AbTo(ct,p)) when display_position#enclosed_in p -> ignore(Typeload.load_complex_type ctx true (ct,p)) @@ -114,22 +127,23 @@ let check_display_abstract ctx cc cfile a = () ) sa.d_flags -let check_display_module_fields ctx cfile m = +let check_display_module_fields ctx decls m = Option.may (fun c -> - let sc = find_class_by_position cfile c.cl_name_pos in List.iter (fun cf -> - if display_position#enclosed_in cf.cf_pos then - check_display_field ctx sc c cf; + if display_position#enclosed_in cf.cf_pos then begin + let cff = find_module_static_by_position decls cf.cf_name_pos in + actually_check_display_field ctx c (TypeloadModule.field_of_static_definition cff cf.cf_pos) cf.cf_pos; + end; DisplayEmitter.check_display_metadata ctx cf.cf_meta ) c.cl_ordered_statics ) m.m_statics -let check_display_module ctx cc cfile m = +let check_display_module ctx decls m = let imports = List.filter (function | (EImport _ | EUsing _),_ -> true | _ -> false - ) cfile.c_decls in - let imports = TypeloadModule.handle_import_hx ctx m imports null_pos in + ) decls in + let imports = TypeloadModule.ModuleLevel.handle_import_hx ctx m imports null_pos in let ctx = TypeloadModule.type_types_into_module ctx m imports null_pos in List.iter (fun md -> let infos = t_infos md in @@ -137,17 +151,17 @@ let check_display_module ctx cc cfile m = DisplayEmitter.display_module_type ctx md infos.mt_name_pos; begin if display_position#enclosed_in infos.mt_pos then match md with | TClassDecl c -> - check_display_class ctx cc cfile c + check_display_class ctx decls c | TEnumDecl en -> - check_display_enum ctx cc cfile en + check_display_enum ctx decls en | TTypeDecl td -> - check_display_typedef ctx cc cfile td + check_display_typedef ctx decls td | TAbstractDecl a -> - check_display_abstract ctx cc cfile a + check_display_abstract ctx decls a end; DisplayEmitter.check_display_metadata ctx infos.mt_meta ) m.m_types; - check_display_module_fields ctx cfile m + check_display_module_fields ctx decls m let check_display_file ctx cs = match ctx.com.cache with @@ -161,15 +175,13 @@ let check_display_file ctx cs = invalid (issue #8991). *) begin match !TypeloadModule.type_module_hook ctx path null_pos with | None -> raise Not_found - | Some m -> check_display_module ctx cc cfile m + | Some m -> check_display_module ctx cfile.c_decls m end with Not_found -> - if ctx.com.display.dms_display then begin - let fkey = DisplayPosition.display_position#get_file_key in - (* force parsing again : if the completion point have been changed *) - cs#remove_files fkey; - cs#taint_modules fkey; - end; + let fkey = DisplayPosition.display_position#get_file_key in + (* force parsing again : if the completion point have been changed *) + cs#remove_files fkey; + cs#taint_modules fkey "check_display_file"; end | None -> () \ No newline at end of file diff --git a/src/context/display/displayToplevel.ml b/src/context/display/displayToplevel.ml index 3362383f..f1ab5eed 100644 --- a/src/context/display/displayToplevel.ml +++ b/src/context/display/displayToplevel.ml @@ -18,7 +18,7 @@ *) open Ast open Common -open CompilationServer +open CompilationCache open Type open Typecore open CompletionItem @@ -27,25 +27,45 @@ open DisplayTypes open Genjson open Globals +(* Merges argument and return types from macro and non-macro context, preferring the one that isn't Dynamic. + WARNING: Merging types from boths contexts like this is dangerous. The resuling type must never be + persisted on the compilation server, or else the compiler gets COVID. *) +let perform_type_voodoo t tl' tr' = + match t with + | TFun(tl,tr) -> + let rec loop acc tl tl' = match tl,tl' with + | ((_,_,t1) as a1 :: tl),((_,_,t1') as a1' :: tl') -> + let a = if t1 == t_dynamic then a1' else a1 in + loop (a :: acc) tl tl' + | _ -> (List.rev acc) @ tl' + in + let tl = loop [] tl tl' in + TFun(tl,if tr == t_dynamic then tr' else tr') + | _ -> + TFun(tl',tr') + let maybe_resolve_macro_field ctx t c cf = try if cf.cf_kind <> Method MethMacro then raise Exit; let (tl,tr,c,cf) = ctx.g.do_load_macro ctx false c.cl_path cf.cf_name null_pos in - (TFun(tl,tr)),c,cf + let t = perform_type_voodoo t tl tr in + t,{cf with cf_type = t} with _ -> - t,c,cf + t,cf let exclude : string list ref = ref [] -class explore_class_path_task cs com recursive f_pack f_module dir pack = object(self) +class explore_class_path_task com checked recursive f_pack f_module dir pack = object(self) inherit server_task ["explore";dir] 50 val platform_str = platform_name_macro com method private execute : unit = + let unique_dir = Path.UniqueKey.create dir in let dot_path = (String.concat "." (List.rev pack)) in - if (List.mem dot_path !exclude) then + if (List.mem dot_path !exclude) || Hashtbl.mem checked unique_dir then () else try + Hashtbl.add checked unique_dir true; let entries = Sys.readdir dir in Array.iter (fun file -> match file with @@ -60,11 +80,7 @@ class explore_class_path_task cs com recursive f_pack f_module dir pack = object with Not_found -> f_pack (List.rev pack,file); if recursive then begin - let task = new explore_class_path_task cs com recursive f_pack f_module (dir ^ file ^ "/") (file :: pack) in - begin match cs with - | None -> task#run - | Some cs' -> cs'#add_task task - end + (new explore_class_path_task com checked recursive f_pack f_module (dir ^ file ^ "/") (file :: pack))#run end end | _ -> @@ -94,15 +110,16 @@ class explore_class_path_task cs com recursive f_pack f_module dir pack = object end let explore_class_paths com timer class_paths recursive f_pack f_module = - let cs = CompilationServer.get() in + let cs = com.cs in let t = Timer.timer (timer @ ["class path exploration"]) in + let checked = Hashtbl.create 0 in let tasks = List.map (fun dir -> - new explore_class_path_task cs com recursive f_pack f_module dir [] + new explore_class_path_task com checked recursive f_pack f_module dir [] ) class_paths in - begin match cs with - | None -> List.iter (fun task -> task#run) tasks - | Some cs -> List.iter (fun task -> cs#add_task task) tasks - end; + let task = new arbitrary_task ["explore"] 50 (fun () -> + List.iter (fun task -> task#run) tasks + ) in + cs#add_task task; t() let read_class_paths com timer = @@ -110,17 +127,15 @@ let read_class_paths com timer = (* Don't parse the display file as that would maybe overwrite the content from stdin with the file contents. *) if not (DisplayPosition.display_position#is_in_file (com.file_keys#get file)) then begin let file,_,pack,_ = Display.parse_module' com path Globals.null_pos in - match CompilationServer.get() with - | Some cs when pack <> fst path -> + if pack <> fst path then begin let file_key = com.file_keys#get file in - (CommonCache.get_cache cs com)#remove_file_for_real file_key - | _ -> - () + (CommonCache.get_cache com)#remove_file_for_real file_key + end end ) let init_or_update_server cs com timer_name = - let cc = CommonCache.get_cache cs com in + let cc = CommonCache.get_cache com in if not cc#is_initialized then begin cc#set_initialized true; read_class_paths com timer_name @@ -289,16 +304,16 @@ let collect ctx tk with_type sort = let add_field scope origin cf = let origin,cf = match origin with | Self (TClassDecl c) -> - let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in + let _,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in Self (TClassDecl c),cf | StaticImport (TClassDecl c) -> - let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in + let _,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in StaticImport (TClassDecl c),cf | Parent (TClassDecl c) -> - let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in + let _,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in Parent (TClassDecl c),cf | StaticExtension (TClassDecl c) -> - let _,c,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in + let _,cf = maybe_resolve_macro_field ctx cf.cf_type c cf in StaticExtension (TClassDecl c),cf | _ -> origin,cf @@ -311,7 +326,7 @@ let collect ctx tk with_type sort = in (* member fields *) if ctx.curfun <> FunStatic then begin - let all_fields = Type.TClass.get_all_fields ctx.curclass (List.map snd ctx.curclass.cl_params) in + let all_fields = Type.TClass.get_all_fields ctx.curclass (extract_param_types ctx.curclass.cl_params) in PMap.iter (fun _ (c,cf) -> let origin = if c == ctx.curclass then Self (TClassDecl c) else Parent (TClassDecl c) in maybe_add_field CFSMember origin cf @@ -363,7 +378,7 @@ let collect ctx tk with_type sort = () in List.iter enum_ctors ctx.m.curmod.m_types; - List.iter enum_ctors (List.map fst ctx.m.module_types); + List.iter enum_ctors (List.map fst ctx.m.module_imports); (* enum constructors of expected type *) begin match with_type with @@ -385,7 +400,9 @@ let collect ctx tk with_type sort = | _ -> TClassDecl c,make_ci_class_field in let origin = StaticImport decl in - add (make (CompletionClassField.make cf CFSStatic origin is_qualified) (tpair ~values:(get_value_meta cf.cf_meta) cf.cf_type)) (Some name) + if can_access ctx c cf true && not (Meta.has Meta.NoCompletion cf.cf_meta) then begin + add (make (CompletionClassField.make cf CFSStatic origin is_qualified) (tpair ~values:(get_value_meta cf.cf_meta) cf.cf_type)) (Some name) + end in match resolve_typedef mt with | TClassDecl c -> class_import c; @@ -406,12 +423,15 @@ let collect ctx tk with_type sort = add (make_ci_literal "false" (tpair ctx.com.basic.tbool)) (Some "false"); begin match ctx.curfun with | FunMember | FunConstructor | FunMemberClassLocal -> - let t = TInst(ctx.curclass,List.map snd ctx.curclass.cl_params) in + let t = TInst(ctx.curclass,extract_param_types ctx.curclass.cl_params) in add (make_ci_literal "this" (tpair t)) (Some "this"); begin match ctx.curclass.cl_super with | Some(c,tl) -> add (make_ci_literal "super" (tpair (TInst(c,tl)))) (Some "super") | None -> () end + | FunMemberAbstract -> + let t = TInst(ctx.curclass,extract_param_types ctx.curclass.cl_params) in + add (make_ci_literal "abstract" (tpair t)) (Some "abstract"); | _ -> () end; @@ -430,9 +450,9 @@ let collect ctx tk with_type sort = end; (* type params *) - List.iter (fun (s,t) -> match follow t with + List.iter (fun tp -> match follow tp.ttp_type with | TInst(c,_) -> - add (make_ci_type_param c (tpair t)) (Some (snd c.cl_path)) + add (make_ci_type_param c (tpair tp.ttp_type)) (Some (snd c.cl_path)) | _ -> die "" __LOC__ ) ctx.type_params; @@ -440,61 +460,49 @@ let collect ctx tk with_type sort = List.iter add_type ctx.m.curmod.m_types; (* module imports *) - List.iter add_type (List.rev_map fst ctx.m.module_types); (* reverse! *) + List.iter add_type (List.rev_map fst ctx.m.module_imports); (* reverse! *) (* types from files *) - begin match !CompilationServer.instance with - | None -> - (* offline: explore class paths *) - let class_paths = ctx.com.class_path in - let class_paths = List.filter (fun s -> s <> "") class_paths in - explore_class_paths ctx.com ["display";"toplevel"] class_paths true add_package (fun file path -> - if not (path_exists cctx path) then begin - let _,decls = Display.parse_module ctx path Globals.null_pos in - ignore(process_decls (fst path) (snd path) decls) - end - ) - | Some cs -> - (* online: iter context files *) - init_or_update_server cs ctx.com ["display";"toplevel"]; - let cc = CommonCache.get_cache cs ctx.com in - let files = cc#get_files in - (* Sort files by reverse distance of their package to our current package. *) - let files = Hashtbl.fold (fun file cfile acc -> - let i = pack_similarity curpack cfile.c_package in - ((file,cfile),i) :: acc - ) files [] in - let files = List.sort (fun (_,i1) (_,i2) -> -compare i1 i2) files in - let check_package pack = match List.rev pack with - | [] -> () - | s :: sl -> add_package (List.rev sl,s) - in - List.iter (fun ((file_key,cfile),_) -> - let module_name = CompilationServer.get_module_name_of_cfile cfile.c_file_path cfile in - let dot_path = s_type_path (cfile.c_package,module_name) in - (* In legacy mode we only show toplevel types. *) - if is_legacy_completion && cfile.c_package <> [] then begin - (* And only toplevel packages. *) - match cfile.c_package with - | [s] -> add_package ([],s) - | _ -> () - end else if (List.exists (fun e -> ExtString.String.starts_with dot_path (e ^ ".")) !exclude) then - () - else begin - Hashtbl.replace ctx.com.module_to_file (cfile.c_package,module_name) cfile.c_file_path; - if process_decls cfile.c_package module_name cfile.c_decls then check_package cfile.c_package; - end - ) files; - List.iter (fun file -> - match cs#get_native_lib file with - | Some lib -> - Hashtbl.iter (fun path (pack,decls) -> - if process_decls pack (snd path) decls then check_package pack; - ) lib.c_nl_files - | None -> - () - ) ctx.com.native_libs.all_libs - end; + let cs = ctx.com.cs in + (* online: iter context files *) + init_or_update_server cs ctx.com ["display";"toplevel"]; + let cc = CommonCache.get_cache ctx.com in + let files = cc#get_files in + (* Sort files by reverse distance of their package to our current package. *) + let files = Hashtbl.fold (fun file cfile acc -> + let i = pack_similarity curpack cfile.c_package in + ((file,cfile),i) :: acc + ) files [] in + let files = List.sort (fun (_,i1) (_,i2) -> -compare i1 i2) files in + let check_package pack = match List.rev pack with + | [] -> () + | s :: sl -> add_package (List.rev sl,s) + in + List.iter (fun ((file_key,cfile),_) -> + let module_name = CompilationCache.get_module_name_of_cfile cfile.c_file_path cfile in + let dot_path = s_type_path (cfile.c_package,module_name) in + (* In legacy mode we only show toplevel types. *) + if is_legacy_completion && cfile.c_package <> [] then begin + (* And only toplevel packages. *) + match cfile.c_package with + | [s] -> add_package ([],s) + | _ -> () + end else if (List.exists (fun e -> ExtString.String.starts_with dot_path (e ^ ".")) !exclude) then + () + else begin + ctx.com.module_to_file#add (cfile.c_package,module_name) cfile.c_file_path; + if process_decls cfile.c_package module_name cfile.c_decls then check_package cfile.c_package; + end + ) files; + List.iter (fun file -> + match cs#get_native_lib file with + | Some lib -> + Hashtbl.iter (fun path (pack,decls) -> + if process_decls pack (snd path) decls then check_package pack; + ) lib.c_nl_files + | None -> + () + ) ctx.com.native_libs.all_libs; (* packages *) Hashtbl.iter (fun path _ -> diff --git a/src/context/display/findReferences.ml b/src/context/display/findReferences.ml index b688cc08..2e69599c 100644 --- a/src/context/display/findReferences.ml +++ b/src/context/display/findReferences.ml @@ -4,7 +4,6 @@ open DisplayTypes open Common open Type open Typecore -open CompilationServer open ImportHandling let find_possible_references tctx cs = @@ -30,15 +29,45 @@ let find_references tctx com with_definition pos_filters = Display.ReferencePosition.reset(); usages -let collect_reference_positions com = - let name,pos,kind = Display.ReferencePosition.get () in +(* This probably exists somewhere already but I can't find it *) +let is_var_field cf = match cf.cf_kind with + | Method _ -> false + | Var _ -> true + +let rec collect_reference_positions com (name,pos,kind) = match kind, com.display.dms_kind with - | SKField (cf,Some cl_path), DMUsage (_,find_descendants,find_base) when (find_descendants || find_base) && not (has_class_field_flag cf CfStatic) -> + | SKField (cf,Some c),DMUsage (_,find_descendants,_) when find_descendants && is_var_field cf -> + let d = DynArray.create () in + DynArray.add d (name,pos,kind); + begin match cf.cf_kind with + | Var vk -> + let host = FieldAccess.get_host c cf in + let check r mode = match r with + | AccCall -> + begin match FieldAccess.find_accessor_for_field host cf cf.cf_type mode with + | AccessorFound (cf_acc,new_host) -> + let c_host = FieldAccess.get_host_class_raise new_host in + let new_ref = (cf_acc.cf_name,cf_acc.cf_name_pos,SKField(cf_acc,Some c_host)) in + DynArray.add d new_ref; + List.iter (DynArray.add d) (collect_reference_positions com new_ref) + | _ -> + () + end + | _ -> + () + in + check vk.v_read MGet; + check vk.v_write (MSet None); + | _ -> + () + end; + DynArray.to_list d + | SKField (cf,Some c), DMUsage (_,find_descendants,find_base) when (find_descendants || find_base) && not (has_class_field_flag cf CfStatic) -> let collect() = let c = let rec loop = function | [] -> raise Exit - | TClassDecl c :: _ when c.cl_path = cl_path -> c + | TClassDecl c' :: _ when c.cl_path = c'.cl_path -> c' | _ :: types -> loop types in loop com.types @@ -72,7 +101,7 @@ let collect_reference_positions com = | [] -> [cf,c] | pairs -> pairs in - let full_pos p = { p with pfile = Path.get_full_path p.pfile } in + let full_pos p = if p = null_pos then null_pos else { p with pfile = Path.get_full_path p.pfile } in if find_descendants then let extends child_cls (_,c) = extends child_cls c in List.fold_left (fun acc t -> @@ -80,14 +109,14 @@ let collect_reference_positions com = | TClassDecl child_cls when List.exists (extends child_cls) field_class_pairs -> (try let cf = PMap.find cf.cf_name child_cls.cl_fields in - (name,full_pos cf.cf_name_pos,SKField (cf,Some child_cls.cl_path)) :: acc + (name,full_pos cf.cf_name_pos,SKField (cf,Some child_cls)) :: acc with Not_found -> acc ) | _ -> acc ) [] com.types else - List.map (fun (cf,c) -> name,full_pos cf.cf_name_pos,SKField (cf,Some c.cl_path)) field_class_pairs; + List.map (fun (cf,c) -> name,full_pos cf.cf_name_pos,SKField (cf,Some c)) field_class_pairs; in (try collect() with Exit -> [name,pos,kind]) @@ -99,7 +128,7 @@ let find_references tctx com with_definition = List.fold_left (fun acc (_,p,_) -> if p = null_pos then acc else Statistics.SFPos p :: acc - ) [] (collect_reference_positions com) + ) [] (collect_reference_positions com (Display.ReferencePosition.get ())) in let usages = find_references tctx com with_definition pos_filters in let usages = diff --git a/src/context/display/importHandling.ml b/src/context/display/importHandling.ml index cbc21f8f..e03ec2ad 100644 --- a/src/context/display/importHandling.ml +++ b/src/context/display/importHandling.ml @@ -2,6 +2,8 @@ open Globals open Ast open DisplayPosition open Common +open Type +open Error open Typecore type import_display_kind = @@ -53,4 +55,239 @@ let mark_import_position ctx p = let r = PMap.find p ctx.m.curmod.m_extra.m_display.m_import_positions in r := true with Not_found -> - () \ No newline at end of file + () + +let commit_import ctx path mode p = + ctx.m.import_statements <- (path,mode) :: ctx.m.import_statements; + if Filename.basename p.pfile <> "import.hx" then add_import_position ctx p path + +let init_import ctx context_init path mode p = + let rec loop acc = function + | x :: l when is_lower_ident (fst x) -> loop (x::acc) l + | rest -> List.rev acc, rest + in + let pack, rest = loop [] path in + (match rest with + | [] -> + (match mode with + | IAll -> + ctx.m.wildcard_packages <- (List.map fst pack,p) :: ctx.m.wildcard_packages + | _ -> + (match List.rev path with + (* p spans `import |` (to the display position), so we take the pmax here *) + | [] -> DisplayException.raise_fields (DisplayToplevel.collect ctx TKType NoValue true) CRImport (DisplayTypes.make_subject None {p with pmin = p.pmax}) + | (_,p) :: _ -> Error.typing_error "Module name must start with an uppercase letter" p)) + | (tname,p2) :: rest -> + let p1 = (match pack with [] -> p2 | (_,p1) :: _ -> p1) in + let p_type = punion p1 p2 in + let md = ctx.g.do_load_module ctx (List.map fst pack,tname) p_type in + let types = md.m_types in + let no_private (t,_) = not (t_infos t).mt_private in + let error_private p = typing_error "Importing private declarations from a module is not allowed" p in + let chk_private t p = if ctx.m.curmod != (t_infos t).mt_module && (t_infos t).mt_private then error_private p in + let has_name name t = snd (t_infos t).mt_path = name in + + let fail_usefully name p = + let target_kind,candidates = match String.get name 0 with + (* TODO: cleaner way to get module fields? *) + | 'a'..'z' -> "field", PMap.foldi (fun n _ acc -> n :: acc) (try (Option.get md.m_statics).cl_statics with | _ -> PMap.empty) [] + | _ -> "type", List.map (fun mt -> snd (t_infos mt).mt_path) types + in + typing_error (StringError.string_error name + candidates + ("Module " ^ s_type_path md.m_path ^ " does not define " ^ target_kind ^ " " ^ name) + ) p + in + + let find_type tname = List.find (has_name tname) types in + let get_type tname = + let t = try + find_type tname + with Not_found -> + fail_usefully tname p_type + in + chk_private t p_type; + t + in + let rebind t name p = + if not (name.[0] >= 'A' && name.[0] <= 'Z') then + typing_error "Type aliases must start with an uppercase letter" p; + let _, _, f = ctx.g.do_build_instance ctx t p_type in + (* create a temp private typedef, does not register it in module *) + let t_path = (fst md.m_path @ ["_" ^ snd md.m_path],name) in + let t_type = f (extract_param_types (t_infos t).mt_params) in + let mt = TTypeDecl {(mk_typedef ctx.m.curmod t_path p p t_type) with + t_private = true; + t_params = (t_infos t).mt_params + } in + if ctx.is_display_file && DisplayPosition.display_position#enclosed_in p then + DisplayEmitter.display_module_type ctx mt p; + mt + in + let add_static_init t name s = + let name = (match name with None -> s | Some (n,_) -> n) in + match resolve_typedef t with + | TClassDecl c | TAbstractDecl {a_impl = Some c} -> + ignore(c.cl_build()); + ignore(PMap.find s c.cl_statics); + ctx.m.module_globals <- PMap.add name (TClassDecl c,s,p) ctx.m.module_globals + | TEnumDecl e -> + ignore(PMap.find s e.e_constrs); + ctx.m.module_globals <- PMap.add name (TEnumDecl e,s,p) ctx.m.module_globals + | _ -> + raise Not_found + in + (match mode with + | INormal | IAsName _ -> + let name = (match mode with IAsName n -> Some n | _ -> None) in + (match rest with + | [] -> + (match name with + | None -> + ctx.m.module_imports <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_imports; + Option.may (fun c -> + context_init#add (fun () -> + ignore(c.cl_build()); + List.iter (fun cf -> + if has_class_field_flag cf CfPublic then + ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals + ) c.cl_ordered_statics + ); + ) md.m_statics + | Some(newname,pname) -> + ctx.m.module_imports <- (rebind (get_type tname) newname pname,p) :: ctx.m.module_imports); + | [tsub,p2] -> + let pu = punion p1 p2 in + (try + let tsub = List.find (has_name tsub) types in + chk_private tsub pu; + ctx.m.module_imports <- ((match name with None -> tsub | Some(n,pname) -> rebind tsub n pname),p) :: ctx.m.module_imports + with Not_found -> + (* this might be a static property, wait later to check *) + let find_main_type_static () = + try + let tmain = find_type tname in + begin try + add_static_init tmain name tsub + with Not_found -> + let parent,target_kind,candidates = match resolve_typedef tmain with + | TClassDecl c -> + "Class<" ^ (s_type_path c.cl_path) ^ ">", + "field", + PMap.foldi (fun name _ acc -> name :: acc) c.cl_statics [] + | TAbstractDecl {a_impl = Some c} -> + "Abstract<" ^ (s_type_path md.m_path) ^ ">", + "field", + PMap.foldi (fun name _ acc -> name :: acc) c.cl_statics [] + | TEnumDecl e -> + "Enum<" ^ s_type_path md.m_path ^ ">", + "field", + PMap.foldi (fun name _ acc -> name :: acc) e.e_constrs [] + | _ -> + "Module " ^ s_type_path md.m_path, + "field or subtype", + (* TODO: cleaner way to get module fields? *) + PMap.foldi (fun n _ acc -> n :: acc) (try (Option.get md.m_statics).cl_statics with | _ -> PMap.empty) [] + in + + display_error ctx.com (StringError.string_error tsub candidates (parent ^ " has no " ^ target_kind ^ " " ^ tsub)) p + end + with Not_found -> + fail_usefully tsub p + in + context_init#add (fun() -> + match md.m_statics with + | Some c -> + (try + ignore(c.cl_build()); + let rec loop fl = + match fl with + | [] -> raise Not_found + | cf :: rest -> + if cf.cf_name = tsub then + if not (has_class_field_flag cf CfPublic) then + error_private p + else + let imported_name = match name with None -> tsub | Some (n,pname) -> n in + ctx.m.module_globals <- PMap.add imported_name (TClassDecl c,tsub,p) ctx.m.module_globals; + else + loop rest + in + loop c.cl_ordered_statics + with Not_found -> + find_main_type_static ()) + | None -> + find_main_type_static () + ) + ) + | (tsub,p2) :: (fname,p3) :: rest -> + (match rest with + | [] -> () + | (n,p) :: _ -> typing_error ("Unexpected " ^ n) p); + let tsub = get_type tsub in + context_init#add (fun() -> + try + add_static_init tsub name fname + with Not_found -> + display_error ctx.com (s_type_path (t_infos tsub).mt_path ^ " has no field " ^ fname) (punion p p3) + ); + ) + | IAll -> + let t = (match rest with + | [] -> get_type tname + | [tsub,_] -> get_type tsub + | _ :: (n,p) :: _ -> typing_error ("Unexpected " ^ n) p + ) in + context_init#add (fun() -> + match resolve_typedef t with + | TClassDecl c + | TAbstractDecl {a_impl = Some c} -> + ignore(c.cl_build()); + PMap.iter (fun _ cf -> if not (has_meta Meta.NoImportGlobal cf.cf_meta) then ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals) c.cl_statics + | TEnumDecl e -> + PMap.iter (fun _ c -> if not (has_meta Meta.NoImportGlobal c.ef_meta) then ctx.m.module_globals <- PMap.add c.ef_name (TEnumDecl e,c.ef_name,p) ctx.m.module_globals) e.e_constrs + | _ -> + typing_error "No statics to import from this type" p + ) + )) + +let handle_using ctx path p = + let t = match List.rev path with + | (s1,_) :: (s2,_) :: sl -> + if is_lower_ident s2 then mk_type_path ((List.rev (s2 :: List.map fst sl)),s1) + else mk_type_path ~sub:s1 (List.rev (List.map fst sl),s2) + | (s1,_) :: sl -> + mk_type_path (List.rev (List.map fst sl),s1) + | [] -> + DisplayException.raise_fields (DisplayToplevel.collect ctx TKType NoValue true) CRUsing (DisplayTypes.make_subject None {p with pmin = p.pmax}); + in + let types = (match t.tsub with + | None -> + let md = ctx.g.do_load_module ctx (t.tpackage,t.tname) p in + let types = List.filter (fun t -> not (t_infos t).mt_private) md.m_types in + Option.map_default (fun c -> (TClassDecl c) :: types) types md.m_statics + | Some _ -> + let t = ctx.g.do_load_type_def ctx p t in + [t] + ) in + (* delay the using since we need to resolve typedefs *) + let filter_classes types = + let rec loop acc types = match types with + | td :: l -> + (match resolve_typedef td with + | TClassDecl c | TAbstractDecl({a_impl = Some c}) -> + loop ((c,p) :: acc) l + | td -> + loop acc l) + | [] -> + acc + in + loop [] types + in + types,filter_classes + +let init_using ctx context_init path p = + let types,filter_classes = handle_using ctx path p in + (* do the import first *) + ctx.m.module_imports <- (List.map (fun t -> t,p) types) @ ctx.m.module_imports; + context_init#add (fun() -> ctx.m.module_using <- filter_classes types @ ctx.m.module_using) \ No newline at end of file diff --git a/src/context/display/statistics.ml b/src/context/display/statistics.ml index b855660a..a3c20a30 100644 --- a/src/context/display/statistics.ml +++ b/src/context/display/statistics.ml @@ -99,7 +99,6 @@ let collect_statistics ctx pos_filters with_expressions = | _,None -> raise Not_found in let var_decl v = declare (SKVariable v) v.v_pos in - let patch_string_pos p s = { p with pmin = p.pmax - String.length s } in let related_fields = Hashtbl.create 0 in let field_reference co cf p = let p1 = patch_string_pos p cf.cf_name in @@ -235,7 +234,7 @@ let collect_statistics ctx pos_filters with_expressions = if (has_class_flag c CInterface) then collect_implementations c; let field cf = - if cf.cf_pos.pmin > c.cl_name_pos.pmin then declare (SKField (cf,Some c.cl_path)) cf.cf_name_pos; + if cf.cf_pos.pmin > c.cl_name_pos.pmin then declare (SKField (cf,Some c)) cf.cf_name_pos; if with_expressions then begin let _ = follow cf.cf_type in match cf.cf_expr with None -> () | Some e -> collect_references c e diff --git a/src/context/display/syntaxExplorer.ml b/src/context/display/syntaxExplorer.ml index 3d86e1cf..e3389d77 100644 --- a/src/context/display/syntaxExplorer.ml +++ b/src/context/display/syntaxExplorer.ml @@ -56,7 +56,7 @@ let find_in_syntax symbols (pack,decls) = begin match e with | EConst(Ident s) -> check KIdent s - | EField(e1,s) -> + | EField(e1,s,_) -> expr e1; check KAnyField s; | EVars vl -> @@ -160,13 +160,14 @@ let find_in_syntax symbols (pack,decls) = ) decls let explore_uncached_modules tctx cs symbols = + let open CompilationCache in DisplayToplevel.init_or_update_server cs tctx.com ["display";"references"]; - let cc = CommonCache.get_cache cs tctx.com in + let cc = CommonCache.get_cache tctx.com in let files = cc#get_files in let modules = cc#get_modules in let t = Timer.timer ["display";"references";"candidates"] in let acc = Hashtbl.fold (fun file_key cfile acc -> - let module_name = CompilationServer.get_module_name_of_cfile cfile.CompilationServer.c_file_path cfile in + let module_name = get_module_name_of_cfile cfile.c_file_path cfile in if Hashtbl.mem modules (cfile.c_package,module_name) then acc else try diff --git a/src/context/memory.ml b/src/context/memory.ml index 6be1380f..d91e521c 100644 --- a/src/context/memory.ml +++ b/src/context/memory.ml @@ -1,5 +1,5 @@ open Globals -open CompilationServer +open CompilationCache open Common open Type open Genjson @@ -93,7 +93,7 @@ let fmt_word f = let size v = fmt_size (mem_size v) -let get_memory_json (cs : CompilationServer.t) mreq = +let get_memory_json (cs : CompilationCache.t) mreq = begin match mreq with | MCache -> let old_gc = Gc.get() in @@ -237,53 +237,50 @@ let display_memory com = let mem = Gc.stat() in print ("Total Allocated Memory " ^ fmt_size (mem.Gc.heap_words * (Sys.word_size asr 8))); print ("Free Memory " ^ fmt_size (mem.Gc.free_words * (Sys.word_size asr 8))); - (match get() with - | None -> - print "No cache found"; - | Some c -> - print ("Total cache size " ^ size c); - (* print (" haxelib " ^ size c.c_haxelib); *) - (* print (" parsed ast " ^ size c.c_files ^ " (" ^ string_of_int (Hashtbl.length c.c_files) ^ " files stored)"); *) - (* print (" typed modules " ^ size c.c_modules ^ " (" ^ string_of_int (Hashtbl.length c.c_modules) ^ " modules stored)"); *) - let module_list = c#get_modules in - let all_modules = List.fold_left (fun acc m -> PMap.add m.m_id m acc) PMap.empty module_list in - let modules = List.fold_left (fun acc m -> - let (size,r) = get_module_memory c all_modules m in - (m,size,r) :: acc - ) [] module_list in - let cur_key = ref "" and tcount = ref 0 and mcount = ref 0 in - List.iter (fun (m,size,(reached,deps,out,leaks)) -> - let key = m.m_extra.m_sign in - if key <> !cur_key then begin - print (Printf.sprintf (" --- CONFIG %s ----------------------------") (Digest.to_hex key)); - cur_key := key; - end; - print (Printf.sprintf " %s : %s" (s_type_path m.m_path) (fmt_size size)); - (if reached then try - incr mcount; - let lcount = ref 0 in - let leak l = - incr lcount; - incr tcount; - print (Printf.sprintf " LEAK %s" l); - if !lcount >= 3 && !tcount >= 100 && not verbose then begin - print (Printf.sprintf " ..."); - raise Exit; - end; - in - List.iter leak leaks; - with Exit -> - ()); - if verbose then begin - print (Printf.sprintf " %d total deps" (List.length deps)); - PMap.iter (fun _ md -> - print (Printf.sprintf " dep %s%s" (s_type_path md.m_path) (module_sign key md)); - ) m.m_extra.m_deps; - end; - flush stdout - ) (List.sort (fun (m1,s1,_) (m2,s2,_) -> - let k1 = m1.m_extra.m_sign and k2 = m2.m_extra.m_sign in - if k1 = k2 then s1 - s2 else if k1 > k2 then 1 else -1 - ) modules); - if !mcount > 0 then print ("*** " ^ string_of_int !mcount ^ " modules have leaks !"); - print "Cache dump complete") \ No newline at end of file + let c = com.cs in + print ("Total cache size " ^ size c); + (* print (" haxelib " ^ size c.c_haxelib); *) + (* print (" parsed ast " ^ size c.c_files ^ " (" ^ string_of_int (Hashtbl.length c.c_files) ^ " files stored)"); *) + (* print (" typed modules " ^ size c.c_modules ^ " (" ^ string_of_int (Hashtbl.length c.c_modules) ^ " modules stored)"); *) + let module_list = c#get_modules in + let all_modules = List.fold_left (fun acc m -> PMap.add m.m_id m acc) PMap.empty module_list in + let modules = List.fold_left (fun acc m -> + let (size,r) = get_module_memory c all_modules m in + (m,size,r) :: acc + ) [] module_list in + let cur_key = ref "" and tcount = ref 0 and mcount = ref 0 in + List.iter (fun (m,size,(reached,deps,out,leaks)) -> + let key = m.m_extra.m_sign in + if key <> !cur_key then begin + print (Printf.sprintf (" --- CONFIG %s ----------------------------") (Digest.to_hex key)); + cur_key := key; + end; + print (Printf.sprintf " %s : %s" (s_type_path m.m_path) (fmt_size size)); + (if reached then try + incr mcount; + let lcount = ref 0 in + let leak l = + incr lcount; + incr tcount; + print (Printf.sprintf " LEAK %s" l); + if !lcount >= 3 && !tcount >= 100 && not verbose then begin + print (Printf.sprintf " ..."); + raise Exit; + end; + in + List.iter leak leaks; + with Exit -> + ()); + if verbose then begin + print (Printf.sprintf " %d total deps" (List.length deps)); + PMap.iter (fun _ md -> + print (Printf.sprintf " dep %s%s" (s_type_path md.m_path) (module_sign key md)); + ) m.m_extra.m_deps; + end; + flush stdout + ) (List.sort (fun (m1,s1,_) (m2,s2,_) -> + let k1 = m1.m_extra.m_sign and k2 = m2.m_extra.m_sign in + if k1 = k2 then s1 - s2 else if k1 > k2 then 1 else -1 + ) modules); + if !mcount > 0 then print ("*** " ^ string_of_int !mcount ^ " modules have leaks !"); + print "Cache dump complete" \ No newline at end of file diff --git a/src/context/purityState.ml b/src/context/purityState.ml index a29613cc..fdfabc0e 100644 --- a/src/context/purityState.ml +++ b/src/context/purityState.ml @@ -29,12 +29,12 @@ let get_purity_from_meta meta = | "true" | "inferredPure" -> Pure | "false" -> Impure | "expect" -> ExpectPure p - | _ -> error ("Unsupported purity value " ^ s ^ ", expected true or false") p + | _ -> typing_error ("Unsupported purity value " ^ s ^ ", expected true or false") p end | (_,[],_) -> Pure | (_,_,p) -> - error "Unsupported purity value" p + typing_error "Unsupported purity value" p end with Not_found -> MaybePure diff --git a/src/context/typecore.ml b/src/context/typecore.ml index 7911346b..c0f707dd 100644 --- a/src/context/typecore.ml +++ b/src/context/typecore.ml @@ -60,23 +60,21 @@ type typer_pass = type typer_module = { curmod : module_def; - mutable module_types : (module_type * pos) list; + mutable module_imports : (module_type * pos) list; mutable module_using : (tclass * pos) list; mutable module_globals : (string, (module_type * string * pos)) PMap.t; mutable wildcard_packages : (string list * pos) list; - mutable module_imports : import list; + mutable import_statements : import list; } type typer_globals = { - types_module : (path, path) Hashtbl.t; - modules : (path , module_def) Hashtbl.t; mutable delayed : (typer_pass * (unit -> unit) list) list; mutable debug_delayed : (typer_pass * ((unit -> unit) * string * typer) list) list; doinline : bool; + retain_meta : bool; mutable core_api : typer option; mutable macros : ((unit -> unit) * typer) option; mutable std : module_def; - mutable hook_generate : (unit -> unit) list; type_patches : (path, (string * bool, type_patch) Hashtbl.t * type_patch) Hashtbl.t; mutable global_metadata : (string list * metadata_entry * (bool * bool * bool)) list; mutable module_check_policies : (string list * module_check_policy list * bool) list; @@ -84,6 +82,8 @@ type typer_globals = { (* Indicates that Typer.create() finished building this instance *) mutable complete : bool; mutable type_hints : (module_def_display * pos * t) list; + mutable load_only_cached_modules : bool; + functional_interface_lut : (path,tclass_field) lookup; (* api *) do_inherit : typer -> Type.tclass -> pos -> (bool * placed_type_path) -> bool; do_create : Common.context -> typer; @@ -91,11 +91,8 @@ type typer_globals = { do_load_macro : typer -> bool -> path -> string -> pos -> ((string * bool * t) list * t * tclass * Type.tclass_field); do_load_module : typer -> path -> pos -> module_def; do_load_type_def : typer -> pos -> type_path -> module_type; - do_optimize : typer -> texpr -> texpr; - do_build_instance : typer -> module_type -> pos -> ((string * t) list * path * (t list -> t)); + do_build_instance : typer -> module_type -> pos -> (typed_type_param list * path * (t list -> t)); do_format_string : typer -> string -> pos -> Ast.expr; - do_finalize : typer -> unit; - do_generate : typer -> (texpr option * module_type list * module_def list); do_load_core_class : typer -> tclass -> tclass; } @@ -106,7 +103,6 @@ and typer = { g : typer_globals; mutable bypass_accessor : int; mutable meta : metadata; - mutable this_stack : texpr list; mutable with_type_stack : WithType.t list; mutable call_argument_stack : expr list list; (* variable *) @@ -117,15 +113,16 @@ and typer = { (* per-class *) mutable curclass : tclass; mutable tthis : t; - mutable type_params : (string * t) list; + mutable type_params : type_params; mutable get_build_infos : unit -> (module_type * t list * class_field list) option; (* per-function *) + mutable allow_inline : bool; + mutable allow_transform : bool; mutable curfield : tclass_field; mutable untyped : bool; mutable in_function : bool; mutable in_loop : bool; mutable in_display : bool; - mutable in_macro : bool; mutable macro_depth : int; mutable curfun : current_fun; mutable ret : t; @@ -137,7 +134,6 @@ and typer = { mutable delayed_display : DisplayTypes.display_exception_kind option; mutable monomorphs : monomorphs; (* events *) - mutable on_error : typer -> string -> pos -> unit; memory_marker : float array; } @@ -173,7 +169,8 @@ type field_access = { (* The expression on which the field is accessed. For abstracts, this is a type expression to the implementation class. *) fa_on : texpr; - (* The field being accessed. *) + (* The field being accessed. Note that in case of overloads, this might refer to the main field which + hosts other overloads in its cf_overloads. *) fa_field : tclass_field; (* The host of the field. *) fa_host : field_host; @@ -190,9 +187,19 @@ type static_extension_access = { se_access : field_access; } +type dot_path_part_case = + | PUppercase + | PLowercase + +type dot_path_part = { + name : string; + case : dot_path_part_case; + pos : pos +} + exception Forbid_package of (string * path * pos) * pos list * string -exception WithTypeError of error_msg * pos +exception WithTypeError of error_msg * pos * int (* depth *) let memory_marker = [|Unix.time()|] @@ -203,9 +210,9 @@ let type_expr_ref : (?mode:access_mode -> typer -> expr -> WithType.t -> texpr) let type_block_ref : (typer -> expr list -> WithType.t -> pos -> texpr) ref = ref (fun _ _ _ _ -> die "" __LOC__) let unify_min_ref : (typer -> texpr list -> t) ref = ref (fun _ _ -> die "" __LOC__) let unify_min_for_type_source_ref : (typer -> texpr list -> WithType.with_type_source option -> t) ref = ref (fun _ _ _ -> die "" __LOC__) -let analyzer_run_on_expr_ref : (Common.context -> texpr -> texpr) ref = ref (fun _ _ -> die "" __LOC__) +let analyzer_run_on_expr_ref : (Common.context -> string -> texpr -> texpr) ref = ref (fun _ _ _ -> die "" __LOC__) let cast_or_unify_raise_ref : (typer -> ?uctx:unification_context option -> Type.t -> texpr -> pos -> texpr) ref = ref (fun _ ?uctx _ _ _ -> assert false) -let type_generic_function_ref : (typer -> field_access -> texpr list -> expr list -> WithType.t -> pos -> texpr) ref = ref (fun _ _ _ _ _ _ -> assert false) +let type_generic_function_ref : (typer -> field_access -> (unit -> texpr) field_call_candidate -> WithType.t -> pos -> texpr) ref = ref (fun _ _ _ _ _ -> assert false) let pass_name = function | PBuildModule -> "build-module" @@ -216,9 +223,9 @@ let pass_name = function | PForce -> "force" | PFinal -> "final" -let display_error ctx msg p = match ctx.com.display.DisplayMode.dms_error_policy with - | DisplayMode.EPShow | DisplayMode.EPIgnore -> ctx.on_error ctx msg p - | DisplayMode.EPCollect -> add_diagnostics_message ctx.com msg p DisplayTypes.DiagnosticsKind.DKCompilerError DisplayTypes.DiagnosticsSeverity.Error +let warning ?(depth=0) ctx w msg p = + let options = (Warning.from_meta ctx.curclass.cl_meta) @ (Warning.from_meta ctx.curfield.cf_meta) in + ctx.com.warning ~depth w options msg p let make_call ctx e el t p = (!make_call_ref) ctx e el t p @@ -251,12 +258,12 @@ let make_static_call ctx c cf map args t p = let raise_or_display ctx l p = if ctx.untyped then () - else if ctx.in_call_args then raise (WithTypeError(Unify l,p)) - else display_error ctx (error_msg (Unify l)) p + else if ctx.in_call_args then raise (WithTypeError(Unify l,p,0)) + else located_display_error ctx.com (error_msg p (Unify l)) let raise_or_display_message ctx msg p = - if ctx.in_call_args then raise (WithTypeError (Custom msg,p)) - else display_error ctx msg p + if ctx.in_call_args then raise (WithTypeError (Custom msg,p,0)) + else display_error ctx.com msg p let unify ctx t1 t2 p = try @@ -265,13 +272,13 @@ let unify ctx t1 t2 p = Unify_error l -> raise_or_display ctx l p -let unify_raise_custom uctx (ctx : typer) t1 t2 p = +let unify_raise_custom uctx t1 t2 p = try Type.unify_custom uctx t1 t2 with Unify_error l -> (* no untyped check *) - raise (Error (Unify l,p)) + raise (Error (Unify l,p,0)) let unify_raise = unify_raise_custom default_unification_context @@ -282,14 +289,19 @@ let save_locals ctx = let add_local ctx k n t p = let v = alloc_var k n t p in if Define.defined ctx.com.defines Define.WarnVarShadowing && n <> "_" then begin - try - let v' = PMap.find n ctx.locals in - (* ignore std lib *) - if not (List.exists (ExtLib.String.starts_with p.pfile) ctx.com.std_path) then begin - ctx.com.warning "This variable shadows a previously declared variable" p; - ctx.com.warning (compl_msg "Previous variable was here") v'.v_pos + match k with + | VUser _ -> + begin try + let v' = PMap.find n ctx.locals in + (* ignore std lib *) + if not (List.exists (ExtLib.String.starts_with p.pfile) ctx.com.std_path) then begin + warning ctx WVarShadow "This variable shadows a previously declared variable" p; + warning ~depth:1 ctx WVarShadow (compl_msg "Previous variable was here") v'.v_pos + end + with Not_found -> + () end - with Not_found -> + | _ -> () end; ctx.locals <- PMap.add n v ctx.locals; @@ -297,7 +309,7 @@ let add_local ctx k n t p = let display_identifier_error ctx ?prepend_msg msg p = let prepend = match prepend_msg with Some s -> s ^ " " | _ -> "" in - display_error ctx (prepend ^ msg) p + display_error ctx.com (prepend ^ msg) p let check_identifier_name ?prepend_msg ctx name kind p = if starts_with name '$' then @@ -324,8 +336,8 @@ let check_module_path ctx (pack,name) p = try List.iter (fun part -> Path.check_package_name part) pack; with Failure msg -> - display_error ctx ("\"" ^ (StringHelper.s_escape (String.concat "." pack)) ^ "\" is not a valid package name:") p; - display_error ctx msg p + display_error ctx.com ("\"" ^ (StringHelper.s_escape (String.concat "." pack)) ^ "\" is not a valid package name:") p; + display_error ctx.com msg p let check_local_variable_name ctx name origin p = match name with @@ -349,15 +361,7 @@ let add_local_with_origin ctx origin n t p = let gen_local_prefix = "`" let gen_local ctx t p = - (* ensure that our generated local does not mask an existing one *) - let rec loop n = - let nv = (if n = 0 then gen_local_prefix else gen_local_prefix ^ string_of_int n) in - if PMap.mem nv ctx.locals then - loop (n+1) - else - nv - in - add_local ctx VGenerated (loop 0) t p + add_local ctx VGenerated "`" t p let is_gen_local v = String.unsafe_get v.v_name 0 = String.unsafe_get gen_local_prefix 0 @@ -386,6 +390,12 @@ let delay_late ctx p f = in ctx.g.delayed <- loop ctx.g.delayed +let delay_if_mono ctx p t f = match follow t with + | TMono _ -> + delay ctx p f + | _ -> + f() + let rec flush_pass ctx p (where:string) = match ctx.g.delayed with | (p2,l) :: rest when p2 <= p -> @@ -412,8 +422,8 @@ let exc_protect ?(force=true) ctx f (where:string) = r := lazy_available t; t with - | Error (m,p) -> - raise (Fatal_error ((error_msg m),p)) + | Error (m,p,depth) -> + raise (Fatal_error ((error_msg p m),depth)) ); if force then delay ctx PForce (fun () -> ignore(lazy_type r)); r @@ -433,23 +443,15 @@ let create_fake_module ctx file = Hashtbl.add fake_modules key mdep; mdep ) in - Hashtbl.replace ctx.g.modules mdep.m_path mdep; + ctx.com.module_lut#add mdep.m_path mdep; mdep -let push_this ctx e = match e.eexpr with - | TConst ((TInt _ | TFloat _ | TString _ | TBool _) as ct) -> - (EConst (tconst_to_const ct),e.epos),fun () -> () - | _ -> - ctx.this_stack <- e :: ctx.this_stack; - let er = EMeta((Meta.This,[],e.epos), (EConst(Ident "this"),e.epos)),e.epos in - er,fun () -> ctx.this_stack <- List.tl ctx.this_stack - -let is_removable_field ctx f = +let is_removable_field com f = not (has_class_field_flag f CfOverride) && ( has_class_field_flag f CfExtern || has_class_field_flag f CfGeneric || (match f.cf_kind with | Var {v_read = AccRequire (s,_)} -> true - | Method MethMacro -> not ctx.in_macro + | Method MethMacro -> not com.is_macro_context | _ -> false) ) @@ -472,7 +474,7 @@ let rec can_access ctx c cf stat = in let rec expr_path acc e = match fst e with - | EField (e,f) -> expr_path (f :: acc) e + | EField (e,f,_) -> expr_path (f :: acc) e | EConst (Ident n) -> n :: acc | _ -> [] in @@ -510,7 +512,8 @@ let rec can_access ctx c cf stat = in loop c.cl_meta || loop f.cf_meta in - let cur_paths = ref [] in + let module_path = ctx.curclass.cl_module.m_path in + let cur_paths = ref [fst module_path @ [snd module_path], false] in let rec loop c is_current_path = cur_paths := (make_path c ctx.curfield, is_current_path) :: !cur_paths; begin match c.cl_super with @@ -527,15 +530,16 @@ let rec can_access ctx c cf stat = || ( (* if our common ancestor declare/override the field, then we can access it *) let allowed f = extends ctx.curclass c || (List.exists (has Meta.Allow c f) !cur_paths) in - if is_constr - then (match c.cl_constructor with + if is_constr then ( + match c.cl_constructor with | Some cf -> if allowed cf then true else if cf.cf_expr = None then false (* maybe it's an inherited auto-generated constructor *) else raise Not_found | _ -> false - ) - else try allowed (PMap.find cf.cf_name (if stat then c.cl_statics else c.cl_fields)) with Not_found -> false + ) else + try allowed (PMap.find cf.cf_name (if stat then c.cl_statics else c.cl_fields)) + with Not_found -> false ) || (match c.cl_super with | Some (csup,_) -> loop csup @@ -552,14 +556,14 @@ let rec can_access ctx c cf stat = let check_field_access ctx c f stat p = if not ctx.untyped && not (can_access ctx c f stat) then - display_error ctx ("Cannot access private field " ^ f.cf_name) p + display_error ctx.com ("Cannot access private field " ^ f.cf_name) p (** removes the first argument of the class field's function type and all its overloads *) let prepare_using_field cf = match follow cf.cf_type with | TFun((_,_,tf) :: args,ret) -> let rec loop acc overloads = match overloads with | ({cf_type = TFun((_,_,tfo) :: args,ret)} as cfo) :: l -> - let tfo = apply_params cfo.cf_params (List.map snd cfo.cf_params) tfo in + let tfo = apply_params cfo.cf_params (extract_param_types cfo.cf_params) tfo in (* ignore overloads which have a different first argument *) if type_iseq tf tfo then loop ({cfo with cf_type = TFun(args,ret)} :: acc) l else loop acc l | _ :: l -> @@ -586,6 +590,40 @@ let merge_core_doc ctx mt = end | _ -> ()) +let field_to_type_path com e = + let rec loop e pack name = match e with + | EField(e,f,_),p when Char.lowercase (String.get f 0) <> String.get f 0 -> (match name with + | [] | _ :: [] -> + loop e pack (f :: name) + | _ -> (* too many name paths *) + display_error com ("Unexpected " ^ f) p; + raise Exit) + | EField(e,f,_),_ -> + loop e (f :: pack) name + | EConst(Ident f),_ -> + let pack, name, sub = match name with + | [] -> + let fchar = String.get f 0 in + if Char.uppercase fchar = fchar then + pack, f, None + else begin + display_error com "A class name must start with an uppercase letter" (snd e); + raise Exit + end + | [name] -> + f :: pack, name, None + | [name; sub] -> + f :: pack, name, Some sub + | _ -> + die "" __LOC__ + in + { tpackage=pack; tname=name; tparams=[]; tsub=sub } + | _,pos -> + display_error com "Unexpected expression when building strict meta" pos; + raise Exit + in + loop e [] [] + let safe_mono_close ctx m p = try Monomorph.close m @@ -612,6 +650,76 @@ let s_field_call_candidate fcc = "fc_field",Printf.sprintf "%s: %s" fcc.fc_field.cf_name (s_type pctx fcc.fc_field.cf_type) ] + +let relative_path ctx file = + let slashes path = String.concat "/" (ExtString.String.nsplit path "\\") in + let fpath = slashes (Path.get_full_path file) in + let fpath_lower = String.lowercase fpath in + let flen = String.length fpath_lower in + let rec loop = function + | [] -> file + | path :: l -> + let spath = String.lowercase (slashes path) in + let slen = String.length spath in + if slen > 0 && slen < flen && String.sub fpath_lower 0 slen = spath then String.sub fpath slen (flen - slen) else loop l + in + loop ctx.com.Common.class_path + +let mk_infos ctx p params = + let file = if ctx.com.is_macro_context then p.pfile else if Common.defined ctx.com Define.AbsolutePath then Path.get_full_path p.pfile else relative_path ctx p.pfile in + (EObjectDecl ( + (("fileName",null_pos,NoQuotes) , (EConst (String(file,SDoubleQuotes)) , p)) :: + (("lineNumber",null_pos,NoQuotes) , (EConst (Int (string_of_int (Lexer.get_error_line p), None)),p)) :: + (("className",null_pos,NoQuotes) , (EConst (String (s_type_path ctx.curclass.cl_path,SDoubleQuotes)),p)) :: + if ctx.curfield.cf_name = "" then + params + else + (("methodName",null_pos,NoQuotes), (EConst (String (ctx.curfield.cf_name,SDoubleQuotes)),p)) :: params + ) ,p) + +let rec is_pos_infos = function + | TMono r -> + (match r.tm_type with + | Some t -> is_pos_infos t + | _ -> false) + | TLazy f -> + is_pos_infos (lazy_type f) + | TType ({ t_path = ["haxe"] , "PosInfos" },[]) -> + true + | TType (t,tl) -> + is_pos_infos (apply_typedef t tl) + | TAbstract({a_path=[],"Null"},[t]) -> + is_pos_infos t + | _ -> + false + +let is_empty_or_pos_infos args = + match args with + | [_,true,t] -> is_pos_infos t + | [] -> true + | _ -> false + +let get_next_stored_typed_expr_id = + let uid = ref 0 in + (fun() -> incr uid; !uid) + +let get_stored_typed_expr com id = + let e = com.stored_typed_exprs#find id in + Texpr.duplicate_tvars e + +let store_typed_expr com te p = + let id = get_next_stored_typed_expr_id() in + com.stored_typed_exprs#add id te; + let eid = (EConst (Int (string_of_int id, None))), p in + id,((EMeta ((Meta.StoredTypedExpr,[],null_pos), eid)),p) + +let push_this ctx e = match e.eexpr with +| TConst ((TInt _ | TFloat _ | TString _ | TBool _) as ct) -> + (EConst (tconst_to_const ct),e.epos),fun () -> () +| _ -> + let id,er = store_typed_expr ctx.com e e.epos in + er,fun () -> ctx.com.stored_typed_exprs#remove id + (* -------------- debug functions to activate when debugging typer passes ------------------------------- *) (*/* @@ -685,9 +793,13 @@ let pending_passes ctx = | [] -> "" | l -> " ??PENDING[" ^ String.concat ";" (List.map (fun (_,i,_) -> i) l) ^ "]" -let display_error ctx msg p = +let display_error ctx.com msg p = + debug ctx ("ERROR " ^ msg); + display_error ctx.com msg p + +let located_display_error ctx.com msg = debug ctx ("ERROR " ^ msg); - display_error ctx msg p + located_display_error ctx.com msg let make_pass ?inf ctx f = let inf = (match inf with None -> pass_infos ctx ctx.pass | Some inf -> inf) in @@ -748,8 +860,8 @@ let exc_protect ?(force=true) ctx f (where:string) = r := lazy_available t; t with - | Error (m,p) -> - raise (Fatal_error ((error_msg m),p)) + | Error (m,p,depth) -> + raise (Fatal_error ((error_msg m),p,depth)) )); if force then delay ctx PForce (fun () -> ignore(lazy_type r)); r diff --git a/src/core/abstract.ml b/src/core/abstract.ml index 3bf96ac9..409fe735 100644 --- a/src/core/abstract.ml +++ b/src/core/abstract.ml @@ -83,14 +83,14 @@ let rec find_multitype_params a pl = | EMeta((Meta.Custom ":followWithAbstracts",_,_),e1) -> loop follow_with_abstracts e1; | _ -> - error "Type parameter expected" (pos e) + typing_error "Type parameter expected" (pos e) in loop (fun t -> t) e ) el; let definitive_types = ref [] in - let tl = List.map2 (fun (n,_) t -> + let tl = List.map2 (fun tp t -> try - let t = (Hashtbl.find relevant n) t in + let t = (Hashtbl.find relevant tp.ttp_name) t in definitive_types := t :: !definitive_types; t with Not_found -> @@ -119,12 +119,12 @@ and get_underlying_type ?(return_first=false) a pl = | TAbstract({a_path=([],"Null")} as a,[t1]) -> TAbstract(a,[loop t1]) | TType (t,tl) -> - loop (apply_params t.t_params tl t.t_type) + loop (apply_typedef t tl) | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> if rec_stack_exists (fast_eq t) underlying_type_stack then begin let pctx = print_context() in let s = String.concat " -> " (List.map (fun t -> s_type pctx t) (List.rev (t :: underlying_type_stack.rec_stack))) in - error ("Abstract chain detected: " ^ s) a.a_pos + typing_error ("Abstract chain detected: " ^ s) a.a_pos end; get_underlying_type a tl | _ -> diff --git a/src/core/ast.ml b/src/core/ast.ml index c0db2756..dea57ed1 100644 --- a/src/core/ast.ml +++ b/src/core/ast.ml @@ -91,6 +91,7 @@ type binop = | OpInterval | OpArrow | OpIn + | OpNullCoal type unop = | Increment @@ -106,8 +107,8 @@ type string_literal_kind = (* | SMarkup *) type constant = - | Int of string - | Float of string + | Int of string * string option + | Float of string * string option | String of string * string_literal_kind | Ident of string | Regexp of string * string @@ -130,6 +131,7 @@ type token = | PClose | Dot | DblDot + | QuestionDot | Arrow | IntInterval of string | Sharp of string @@ -196,11 +198,15 @@ and display_kind = | DKMarked | DKPattern of bool +and efield_kind = + | EFNormal + | EFSafe + and expr_def = | EConst of constant | EArray of expr * expr | EBinop of binop * expr * expr - | EField of expr * string + | EField of expr * string * efield_kind | EParenthesis of expr | EObjectDecl of ((string * pos * quote_status) * expr) list | EArrayDecl of expr list @@ -223,7 +229,6 @@ and expr_def = | ECast of expr * type_hint option | EIs of expr * type_hint | EDisplay of expr * display_kind - | EDisplayNew of placed_type_path | ETernary of expr * expr * expr | ECheckType of expr * type_hint | EMeta of metadata_entry * expr @@ -234,6 +239,7 @@ and type_param = { tp_name : placed_name; tp_params : type_param list; tp_constraints : type_hint option; + tp_default : type_hint option; tp_meta : metadata; } @@ -314,6 +320,7 @@ and class_field = { and evar = { ev_name : placed_name; ev_final : bool; + ev_static : bool; ev_type : type_hint option; ev_expr : expr option; ev_meta : metadata; @@ -387,10 +394,11 @@ let mk_type_path ?(params=[]) ?sub (pack,name) = raise (Invalid_argument "Empty module name is not allowed"); { tpackage = pack; tname = name; tsub = sub; tparams = params; } -let mk_evar ?(final=false) ?(t:type_hint option) ?eo ?(meta=[]) name = +let mk_evar ?(final=false) ?(static=false) ?(t:type_hint option) ?eo ?(meta=[]) name = { ev_name = name; ev_final = final; + ev_static = static; ev_type = t; ev_expr = eo; ev_meta = meta; @@ -459,8 +467,10 @@ let parse_path s = | x :: l -> List.rev l, x let s_constant = function - | Int s -> s - | Float s -> s + | Int (s, None) -> s + | Int (s, Some suffix) -> s ^ suffix + | Float (s, None) -> s + | Float (s, Some suffix) -> s ^ suffix | String(s,qs) -> begin match qs with | SDoubleQuotes -> "\"" ^ StringHelper.s_escape s ^ "\"" @@ -556,6 +566,7 @@ let rec s_binop = function | OpInterval -> "..." | OpArrow -> "=>" | OpIn -> " in " + | OpNullCoal -> "??" let s_unop = function | Increment -> "++" @@ -583,6 +594,7 @@ let s_token = function | PClose -> ")" | Dot -> "." | DblDot -> ":" + | QuestionDot -> "?." | Arrow -> "->" | IntInterval s -> s ^ "..." | Sharp s -> "#" ^ s @@ -593,6 +605,9 @@ let s_token = function exception Invalid_escape_sequence of char * int * (string option) +let efield (e,s) = + EField(e,s,EFNormal) + let unescape s = let b = Buffer.create 0 in let rec loop esc i = @@ -692,8 +707,9 @@ let map_expr loop (e,p) = ),p and tparamdecl t = let constraints = opt type_hint t.tp_constraints in + let default = opt type_hint t.tp_default in let params = List.map tparamdecl t.tp_params in - { tp_name = t.tp_name; tp_constraints = constraints; tp_params = params; tp_meta = t.tp_meta } + { tp_name = t.tp_name; tp_constraints = constraints; tp_default = default; tp_params = params; tp_meta = t.tp_meta } and func f = let params = List.map tparamdecl f.f_params in let args = List.map (fun (n,o,m,t,e) -> @@ -721,7 +737,7 @@ let map_expr loop (e,p) = let e1 = loop e1 in let e2 = loop e2 in EBinop (op,e1,e2) - | EField (e,f) -> EField (loop e, f) + | EField (e,f,efk) -> EField (loop e, f, efk) | EParenthesis e -> EParenthesis (loop e) | EObjectDecl fl -> EObjectDecl (List.map (fun (k,e) -> k,loop e) fl) | EArrayDecl el -> EArrayDecl (List.map loop el) @@ -783,7 +799,6 @@ let map_expr loop (e,p) = let t = type_hint t in EIs (e,t) | EDisplay (e,f) -> EDisplay (loop e,f) - | EDisplayNew t -> EDisplayNew (tpath t) | ETernary (e1,e2,e3) -> let e1 = loop e1 in let e2 = loop e2 in @@ -801,8 +816,8 @@ let iter_expr loop (e,p) = let opt eo = match eo with None -> () | Some e -> loop e in let exprs = List.iter loop in match e with - | EConst _ | EContinue | EBreak | EDisplayNew _ | EReturn None -> () - | EParenthesis e1 | EField(e1,_) | EUnop(_,_,e1) | EReturn(Some e1) | EThrow e1 | EMeta(_,e1) + | EConst _ | EContinue | EBreak | EReturn None -> () + | EParenthesis e1 | EField(e1,_,_) | EUnop(_,_,e1) | EReturn(Some e1) | EThrow e1 | EMeta(_,e1) | ECheckType(e1,_) | EDisplay(e1,_) | ECast(e1,_) | EIs(e1,_) | EUntyped e1 -> loop e1; | EArray(e1,e2) | EBinop(_,e1,e2) | EFor(e1,e2) | EWhile(e1,e2,_) | EIf(e1,e2,None) -> loop e1; loop e2; | ETernary(e1,e2,e3) | EIf(e1,e2,Some e3) -> loop e1; loop e2; loop e3; @@ -842,7 +857,7 @@ module Printer = struct | EConst c -> s_constant c | EArray (e1,e2) -> s_expr_inner tabs e1 ^ "[" ^ s_expr_inner tabs e2 ^ "]" | EBinop (op,e1,e2) -> s_expr_inner tabs e1 ^ " " ^ s_binop op ^ " " ^ s_expr_inner tabs e2 - | EField (e,f) -> s_expr_inner tabs e ^ "." ^ f + | EField (e,f,efk) -> s_expr_inner tabs e ^ (match efk with EFNormal -> "." | EFSafe -> "?.") ^ f | EParenthesis e -> "(" ^ (s_expr_inner tabs e) ^ ")" | EObjectDecl fl -> "{ " ^ (String.concat ", " (List.map (fun ((n,_,qs),e) -> (s_object_key_name n qs) ^ " : " ^ (s_expr_inner tabs e)) fl)) ^ " }" | EArrayDecl el -> "[" ^ s_expr_list tabs el ", " ^ "]" @@ -877,7 +892,6 @@ module Printer = struct | ECheckType (e,(t,_)) -> "(" ^ s_expr_inner tabs e ^ " : " ^ s_complex_type tabs t ^ ")" | EMeta (m,e) -> s_metadata tabs m ^ " " ^ s_expr_inner tabs e | EDisplay (e1,dk) -> Printf.sprintf "#DISPLAY(%s, %s)" (s_expr_inner tabs e1) (s_display_kind dk) - | EDisplayNew tp -> Printf.sprintf "#DISPLAY_NEW(%s)" (s_complex_type_path tabs tp) and s_expr_list tabs el sep = (String.concat sep (List.map (s_expr_inner tabs) el)) and s_complex_type_path tabs (t,_) = @@ -995,13 +1009,13 @@ let get_value_meta meta = let rec string_list_of_expr_path_raise (e,p) = match e with | EConst (Ident i) -> [i] - | EField (e,f) -> f :: string_list_of_expr_path_raise e + | EField (e,f,_) -> f :: string_list_of_expr_path_raise e | _ -> raise Exit let rec string_pos_list_of_expr_path_raise (e,p) = match e with | EConst (Ident i) -> [i,p] - | EField (e,f) -> (f,p) :: string_pos_list_of_expr_path_raise e (* wrong p? *) + | EField (e,f,_) -> (f,p) :: string_pos_list_of_expr_path_raise e (* wrong p? *) | _ -> raise Exit let expr_of_type_path (sl,s) p = @@ -1009,26 +1023,26 @@ let expr_of_type_path (sl,s) p = | [] -> (EConst(Ident s),p) | s1 :: sl -> let e1 = (EConst(Ident s1),p) in - let e = List.fold_left (fun e s -> (EField(e,s),p)) e1 sl in - EField(e,s),p + let e = List.fold_left (fun e s -> (efield(e,s),p)) e1 sl in + efield(e,s),p let match_path recursive sl sl_pattern = - let rec loop sl1 sl2 = match sl1,sl2 with + let rec loop top sl1 sl2 = match sl1,sl2 with | [],[] -> true (* always recurse into types of package paths *) | (s1 :: s11 :: _),[s2] when is_lower_ident s2 && not (is_lower_ident s11)-> s1 = s2 - | [_],[""] -> + | [_],[] when top -> true - | _,([] | [""]) -> + | _,[] -> recursive | [],_ -> false | (s1 :: sl1),(s2 :: sl2) -> - s1 = s2 && loop sl1 sl2 + s1 = s2 && loop false sl1 sl2 in - loop sl sl_pattern + loop true sl sl_pattern let full_dot_path2 mpath tpath = if mpath = tpath then @@ -1081,7 +1095,7 @@ module Expr = struct add ("EBinop " ^ (s_binop op)); loop e1; loop e2; - | EField(e1,s) -> + | EField(e1,s,_) -> add ("EField " ^ s); loop e1 | EParenthesis e1 -> @@ -1184,10 +1198,8 @@ module Expr = struct add "ECheckType"; loop e1; | EMeta((m,_,_),e1) -> - add ("EMeta " ^ fst (Meta.get_info m)); + add ("EMeta " ^ (Meta.to_string m)); loop e1 - | EDisplayNew _ -> - die "" __LOC__ in loop' "" e; Buffer.contents buf @@ -1238,4 +1250,12 @@ let get_meta_options metas meta = | [] -> [] in - loop metas \ No newline at end of file + loop metas + +let get_meta_string meta key = + let rec loop = function + | [] -> None + | (k,[EConst (String(name,_)),_],_) :: _ when k = key -> Some name + | _ :: l -> loop l + in + loop meta \ No newline at end of file diff --git a/src/core/define.ml b/src/core/define.ml index ed4176e3..4c5804ff 100644 --- a/src/core/define.ml +++ b/src/core/define.ml @@ -6,62 +6,104 @@ type define = { mutable defines_signature : string option; } -let get_documentation_list() = +type user_define = { + doc : string; + flags : define_parameter list; + source : string option; +} + +let register_user_define user_defines s data = + Hashtbl.replace user_defines s data + +type define_origin = + | Compiler + | UserDefined of string option + +let infos ?user_defines d = match (user_defines,d) with + | (Some(user_defines), Custom(s)) when (Hashtbl.mem user_defines s) -> + let infos = Hashtbl.find user_defines s in + (s, (infos.doc, infos.flags), (UserDefined infos.source)) + | (_, Custom(s)) -> + (s, ("", []), Compiler) + | _ -> + let def,infos = DefineList.infos d in + (def, infos, Compiler) + +let get_define_key d = + match (infos d) with (s,_,_) -> s + +let get_documentation user_defines d = + let t, (doc,flags), origin = infos ~user_defines:user_defines d in + let params = ref [] and pfs = ref [] in + List.iter (function + | HasParam s -> params := s :: !params + | Platforms fl -> pfs := fl @ !pfs + | Link _ -> () + ) flags; + let params = (match List.rev !params with + | [] -> "" + | l -> "<" ^ String.concat ">, <" l ^ "> " + ) in + let origin = match origin with + | UserDefined Some s -> " (from " ^ s ^ ")" + | Compiler | UserDefined None -> "" + in + let pfs = platform_list_help (List.rev !pfs) in + (String.concat "-" (ExtString.String.nsplit t "_")), params ^ doc ^ pfs ^ origin + +let get_documentation_list user_defines = let m = ref 0 in let rec loop i = let d = Obj.magic i in if d <> Last then begin - let t, (doc,flags) = infos d in - let params = ref [] and pfs = ref [] in - List.iter (function - | HasParam s -> params := s :: !params - | Platforms fl -> pfs := fl @ !pfs - | Link _ -> () - ) flags; - let params = (match List.rev !params with - | [] -> "" - | l -> "<" ^ String.concat ">, <" l ^ "> " - ) in - let pfs = platform_list_help (List.rev !pfs) in - if String.length t > !m then m := String.length t; - ((String.concat "-" (ExtString.String.nsplit t "_")),params ^ doc ^ pfs) :: (loop (i + 1)) + let (str,desc) = get_documentation user_defines d in + if String.length str > !m then m := String.length str; + (str,desc) :: loop (i + 1) end else [] in let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) (loop 0) in all,!m -let raw_defined ctx v = - PMap.mem v ctx.values +let get_user_documentation_list user_defines = + let m = ref 0 in + let user_defines_list = (Hashtbl.fold (fun d _ acc -> + let (str,desc) = get_documentation user_defines (Custom d) in + if String.length str > !m then m := String.length str; + (str,desc) :: acc + ) user_defines []) in + + let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) user_defines_list in + all,!m + +let raw_defined ctx k = + PMap.mem k ctx.values -let defined ctx v = - raw_defined ctx (fst (infos v)) +let defined ctx k = + raw_defined ctx (get_define_key k) let raw_defined_value ctx k = PMap.find k ctx.values let defined_value ctx v = - raw_defined_value ctx (fst (infos v)) + raw_defined_value ctx (get_define_key v) let defined_value_safe ?default ctx v = try defined_value ctx v with Not_found -> match default with Some s -> s | None -> "" let raw_define_value ctx k v = - ctx.values <- PMap.add k v ctx.values; - let k = String.concat "_" (ExtString.String.nsplit k "-") in ctx.values <- PMap.add k v ctx.values; ctx.defines_signature <- None -let raw_define ctx v = - let k,v = try ExtString.String.split v "=" with _ -> v,"1" in - raw_define_value ctx k v - let define_value ctx k v = - raw_define ctx (fst (infos k) ^ "=" ^ v) + raw_define_value ctx (get_define_key k) v + +let raw_define ctx k = + raw_define_value ctx k "1" -let define ctx v = - raw_define ctx (fst (infos v)) +let define ctx k = + raw_define_value ctx (get_define_key k) "1" let get_signature def = match def.defines_signature with @@ -75,6 +117,7 @@ let get_signature def = Note that we should removed flags like use_rtti_doc here. *) | "display" | "use_rtti_doc" | "macro_times" | "display_details" | "no_copt" | "display_stdin" + | "message_reporting" | "messages_log_file" | "messages_log_format" | "no_color" | "dump" | "dump_dependencies" | "dump_ignore_var_ids" -> acc | _ -> (k ^ "=" ^ v) :: acc ) def.values [] in diff --git a/src/core/display/completionItem.ml b/src/core/display/completionItem.ml index 26c1dc15..bc2de80c 100644 --- a/src/core/display/completionItem.ml +++ b/src/core/display/completionItem.ml @@ -214,7 +214,7 @@ module CompletionModuleType = struct false,false,false,kind,ctor | TAbstractDecl a -> let kind = if a.a_enum then EnumAbstract else Abstract in - let is_extern,is_final,is_abstract,ctor = match Abstract.follow_with_forward_ctor (TAbstract(a,List.map snd a.a_params)) with + let is_extern,is_final,is_abstract,ctor = match Abstract.follow_with_forward_ctor (TAbstract(a,extract_param_types a.a_params)) with | TInst(c,_) -> let is_extern,is_final,is_abstract,_,ctor = ctor_info (TClassDecl c) in is_extern,is_final,is_abstract,ctor | TAbstract(a,_) -> false,false,false,actor a | _ -> false,false,false,No @@ -223,11 +223,12 @@ module CompletionModuleType = struct in let is_extern,is_final,is_abstract,kind,ctor = ctor_info mt in let infos = t_infos mt in - let convert_type_param (s,t) = match follow t with + let convert_type_param tp = match follow tp.ttp_type with | TInst(c,_) -> { - tp_name = s,null_pos; + tp_name = tp.ttp_name,null_pos; tp_params = []; tp_constraints = None; (* TODO? *) + tp_default = None; (* TODO? *) tp_meta = c.cl_meta } | _ -> @@ -505,8 +506,10 @@ module CompletionType = struct ct_fields = PMap.fold (fun cf acc -> afield cf :: acc) an.a_fields []; ct_status = !(an.a_status); } - | TDynamic t -> - CTDynamic (if t == t_dynamic then None else Some (from_type PMap.empty t)) + | TDynamic None -> + CTDynamic None + | TDynamic (Some t) -> + CTDynamic (Some (from_type PMap.empty t)) in from_type values t end @@ -758,7 +761,7 @@ let to_json ctx index item = ] | ITMetadata meta -> let open Meta in - let name,(doc,params) = Meta.get_info meta in + let name,(doc,params),origin = Meta.get_info meta in let name = "@" ^ name in let usage_to_string = function | TClass -> "TClass" @@ -772,6 +775,10 @@ let to_json ctx index item = | TTypeParameter -> "TTypeParameter" | TVariable -> "TVariable" in + let origin = match origin with + | Compiler -> Some "haxe compiler" + | UserDefined s -> s + in let rec loop internal params platforms targets links l = match l with | HasParam s :: l -> loop internal (s :: params) platforms targets links l | Platforms pls :: l -> loop internal params ((List.map platform_name pls) @ platforms) targets links l @@ -789,6 +796,7 @@ let to_json ctx index item = "targets",jlist jstring targets; "internal",jbool internal; "links",jlist jstring links; + "origin",jopt jstring origin; ] | ITKeyword kwd ->"Keyword",jobject [ "name",jstring (s_keyword kwd) @@ -827,4 +835,4 @@ let to_json ctx index item = | Some t -> ("type",CompletionType.to_json ctx (snd t)) :: jindex ) - ) \ No newline at end of file + ) diff --git a/src/core/displayTypes.ml b/src/core/displayTypes.ml index 737cb166..40fe0700 100644 --- a/src/core/displayTypes.ml +++ b/src/core/displayTypes.ml @@ -63,42 +63,6 @@ module SymbolInformation = struct } end -module DiagnosticsSeverity = struct - type t = - | Error - | Warning - | Information - | Hint - - let to_int = function - | Error -> 1 - | Warning -> 2 - | Information -> 3 - | Hint -> 4 -end - -module DiagnosticsKind = struct - type t = - | DKUnusedImport - | DKUnresolvedIdentifier - | DKCompilerError - | DKRemovableCode - | DKParserError - | DKDeprecationWarning - | DKInactiveBlock - | DKMissingFields - - let to_int = function - | DKUnusedImport -> 0 - | DKUnresolvedIdentifier -> 1 - | DKCompilerError -> 2 - | DKRemovableCode -> 3 - | DKParserError -> 4 - | DKDeprecationWarning -> 5 - | DKInactiveBlock -> 6 - | DKMissingFields -> 7 -end - module CompletionResultKind = struct type expected_type_completion = { expected_type : CompletionItem.CompletionType.t; @@ -211,17 +175,13 @@ module DisplayMode = struct | DMDefinition | DMTypeDefinition | DMImplementation - | DMResolve of string | DMPackage | DMHover | DMModuleSymbols of string option - | DMDiagnostics of Path.UniqueKey.t list - | DMStatistics | DMSignature type error_policy = | EPIgnore - | EPCollect | EPShow type display_file_policy = @@ -231,7 +191,6 @@ module DisplayMode = struct type settings = { dms_kind : t; - dms_display : bool; dms_full_typing : bool; dms_force_macro_typing : bool; dms_error_policy : error_policy; @@ -244,7 +203,6 @@ module DisplayMode = struct let default_display_settings = { dms_kind = DMDefault; - dms_display = true; dms_full_typing = false; dms_force_macro_typing = false; dms_error_policy = EPIgnore; @@ -257,7 +215,6 @@ module DisplayMode = struct let default_compilation_settings = { dms_kind = DMNone; - dms_display = false; dms_full_typing = true; dms_force_macro_typing = true; dms_error_policy = EPShow; @@ -272,7 +229,7 @@ module DisplayMode = struct let settings = { default_display_settings with dms_kind = dm } in match dm with | DMNone -> default_compilation_settings - | DMDefault | DMDefinition | DMTypeDefinition | DMResolve _ | DMPackage | DMHover | DMSignature -> settings + | DMDefault | DMDefinition | DMTypeDefinition | DMPackage | DMHover | DMSignature -> settings | DMUsage _ | DMImplementation -> { settings with dms_full_typing = true; dms_force_macro_typing = true; @@ -285,20 +242,6 @@ module DisplayMode = struct dms_force_macro_typing = false; dms_per_file = true; } - | DMDiagnostics files -> { default_compilation_settings with - dms_kind = DMDiagnostics files; - dms_error_policy = EPCollect; - dms_display_file_policy = if files = [] then DFPNo else DFPAlso; - dms_per_file = true; - } - | DMStatistics -> { settings with - dms_full_typing = true; - dms_inline = false; - dms_display_file_policy = DFPAlso; - dms_exit_during_typing = false; - dms_force_macro_typing = true; - dms_per_file = true; - } let to_string = function | DMNone -> "none" @@ -306,15 +249,12 @@ module DisplayMode = struct | DMDefinition -> "position" | DMTypeDefinition -> "type-definition" | DMImplementation -> "implementation" - | DMResolve s -> "resolve " ^ s | DMPackage -> "package" | DMHover -> "type" | DMUsage (true,_,_) -> "rename" | DMUsage (false,_,_) -> "references" | DMModuleSymbols None -> "module-symbols" | DMModuleSymbols (Some s) -> "workspace-symbols " ^ s - | DMDiagnostics _ -> "diagnostics" - | DMStatistics -> "statistics" | DMSignature -> "signature" end @@ -324,7 +264,7 @@ type symbol = | SKEnum of tenum | SKTypedef of tdef | SKAbstract of tabstract - | SKField of tclass_field * path option (* path - class path *) + | SKField of tclass_field * tclass option | SKConstructor of tclass_field | SKEnumField of tenum_field | SKVariable of tvar @@ -392,17 +332,16 @@ type diagnostics_context = { mutable import_positions : (pos,bool ref) PMap.t; mutable dead_blocks : (Path.UniqueKey.t,(pos * expr) list) Hashtbl.t; mutable unresolved_identifiers : (string * pos * (string * CompletionItem.t * int) list) list; - mutable diagnostics_messages : (string * pos * DiagnosticsKind.t * DiagnosticsSeverity.t) list; + mutable diagnostics_messages : (string * pos * MessageKind.t * MessageSeverity.t * int (* depth *)) list; mutable missing_fields : (pos,(module_type * (missing_fields_diagnostics list ref))) PMap.t; } type display_exception_kind = - | DisplayDiagnostics of diagnostics_context - | Statistics of string | ModuleSymbols of string | Metadata of string - | DisplaySignatures of (((tsignature * CompletionItem.CompletionType.ct_function) * documentation) list * int * int * signature_kind) option - | DisplayHover of hover_result option + | DisplaySignatures of (((tsignature * CompletionItem.CompletionType.ct_function) * documentation) list * int * int * signature_kind) + | DisplayHover of hover_result | DisplayPositions of pos list - | DisplayFields of fields_result option - | DisplayPackage of string list \ No newline at end of file + | DisplayFields of fields_result + | DisplayPackage of string list + | DisplayNoResult \ No newline at end of file diff --git a/src/core/dune b/src/core/dune index 7218ebea..e4faf920 100644 --- a/src/core/dune +++ b/src/core/dune @@ -8,4 +8,10 @@ (targets defineList.ml) (deps ../../src-json/define.json) (action (with-stdout-to defineList.ml (run %{bin:haxe_prebuild} define ../../src-json/define.json))) +) + +(rule + (targets warningList.ml) + (deps ../../src-json/warning.json) + (action (with-stdout-to warningList.ml (run %{bin:haxe_prebuild} warning ../../src-json/warning.json))) ) \ No newline at end of file diff --git a/src/core/error.ml b/src/core/error.ml index 2550979e..d2361277 100644 --- a/src/core/error.ml +++ b/src/core/error.ml @@ -17,7 +17,7 @@ and error_msg = | Unify of unify_error list | Custom of string | Unknown_ident of string - | Stack of error_msg * error_msg + | Stack of (error_msg * Globals.pos) list | Call_error of call_error | No_constructor of module_type | Abstract_class of module_type @@ -26,8 +26,8 @@ and type_not_found_reason = | Private_type | Not_defined -exception Fatal_error of string * Globals.pos -exception Error of error_msg * Globals.pos +exception Fatal_error of Globals.located * int (* depth *) +exception Error of error_msg * Globals.pos * int (* depth *) let string_source t = match follow t with | TInst(c,tl) -> PMap.foldi (fun s _ acc -> s :: acc) (TClass.get_all_fields c tl) [] @@ -43,6 +43,9 @@ let short_type ctx t = Should be called for each complementary error message. *) let compl_msg s = "... " ^ s +let rec compl_located_msg = function + | Message (s,p) -> Message (compl_msg s,p) + | Stack stack -> Stack (List.map compl_located_msg stack) let unify_error_msg ctx err = match err with | Cannot_unify (t1,t2) -> @@ -102,6 +105,7 @@ module BetterErrors = struct mutable acc_expected : TType.t; mutable acc_actual : TType.t; mutable acc_messages : unify_error list; + mutable acc_extra : unify_error list; mutable acc_next : access option; } @@ -118,6 +122,7 @@ module BetterErrors = struct acc_expected = expected; acc_actual = actual; acc_messages = []; + acc_extra = []; acc_next = None; } in let root_acc = make_acc Root t_dynamic t_dynamic in @@ -125,6 +130,9 @@ module BetterErrors = struct let add_message msg = !current_acc.acc_messages <- msg :: !current_acc.acc_messages in + let add_extra msg = + !current_acc.acc_extra <- msg :: !current_acc.acc_extra + in let add_access kind = let acc = make_acc kind t_dynamic t_dynamic in !current_acc.acc_next <- Some acc; @@ -143,6 +151,8 @@ module BetterErrors = struct add_access FunctionReturn; | Invariant_parameter i -> add_access (TypeParameter i); + | Unify_custom _ -> + add_extra err | _ -> add_message err ) l; @@ -188,8 +198,10 @@ module BetterErrors = struct let fl = PMap.fold (fun f acc -> ((if Meta.has Meta.Optional f.cf_meta then " ?" else " ") ^ f.cf_name) :: acc) a.a_fields [] in "{" ^ String.concat "," fl ^ " }" end - | TDynamic t2 -> - "Dynamic" ^ s_type_params ctx (if t == t2 then [] else [t2]) + | TDynamic None -> + "Dynamic" + | TDynamic (Some t2) -> + "Dynamic" ^ s_type_params ctx [t2] | TLazy f -> s_type ctx (lazy_type f) @@ -262,39 +274,45 @@ module BetterErrors = struct in match access.acc_next with | None -> - String.concat "\n" (List.rev_map (unify_error_msg ctx) access.acc_messages) + String.concat "\n" (List.rev_map (unify_error_msg ctx) (access.acc_extra @ access.acc_messages)) | Some access_next -> let slhs,srhs = loop access_next access in Printf.sprintf "error: %s\nhave: %s\nwant: %s" (Buffer.contents message_buffer) slhs srhs end -let rec error_msg = function - | Module_not_found m -> "Type not found : " ^ s_type_path m - | Type_not_found (m,t,Private_type) -> "Cannot access private type " ^ t ^ " in module " ^ s_type_path m - | Type_not_found (m,t,Not_defined) -> "Module " ^ s_type_path m ^ " does not define type " ^ t - | Unify l -> BetterErrors.better_error_message l - | Unknown_ident s -> "Unknown identifier : " ^ s - | Custom s -> s - | Stack (m1,m2) -> error_msg m1 ^ "\n" ^ error_msg m2 - | Call_error err -> s_call_error err - | No_constructor mt -> (s_type_path (t_infos mt).mt_path ^ " does not have a constructor") - | Abstract_class mt -> (s_type_path (t_infos mt).mt_path) ^ " is abstract and cannot be constructed" +let rec error_msg p = function + | Module_not_found m -> located ("Type not found : " ^ s_type_path m) p + | Type_not_found (m,t,Private_type) -> located ("Cannot access private type " ^ t ^ " in module " ^ s_type_path m) p + | Type_not_found (m,t,Not_defined) -> located ("Module " ^ s_type_path m ^ " does not define type " ^ t) p + | Unify l -> located (BetterErrors.better_error_message l) p + | Unknown_ident s -> located ("Unknown identifier : " ^ s) p + | Custom s -> located s p + | Stack stack -> located_stack (List.map (fun (e,p) -> error_msg p e) stack) + | Call_error err -> s_call_error p err + | No_constructor mt -> located (s_type_path (t_infos mt).mt_path ^ " does not have a constructor") p + | Abstract_class mt -> located (s_type_path (t_infos mt).mt_path ^ " is abstract and cannot be constructed") p -and s_call_error = function +and s_call_error p = function | Not_enough_arguments tl -> let pctx = print_context() in - "Not enough arguments, expected " ^ (String.concat ", " (List.map (fun (n,_,t) -> n ^ ":" ^ (short_type pctx t)) tl)) - | Too_many_arguments -> "Too many arguments" - | Could_not_unify err -> error_msg err - | Cannot_skip_non_nullable s -> "Cannot skip non-nullable argument " ^ s + located ("Not enough arguments, expected " ^ (String.concat ", " (List.map (fun (n,_,t) -> n ^ ":" ^ (short_type pctx t)) tl))) p + | Too_many_arguments -> located "Too many arguments" p + | Could_not_unify err -> error_msg p err + | Cannot_skip_non_nullable s -> located ("Cannot skip non-nullable argument " ^ s) p -let error msg p = raise (Error (Custom msg,p)) +let typing_error ?(depth=0) msg p = raise (Error (Custom msg,p,depth)) +let located_typing_error ?(depth=0) msg = + let err = match msg with + | Message (msg,p) -> Custom msg + | Stack _ -> Stack (List.map (fun (msg,p) -> (Custom msg,p)) (extract_located msg)) + in + raise (Error (err,(extract_located_pos msg),depth)) -let raise_error err p = raise (Error(err,p)) +let raise_typing_error ?(depth=0) err p = raise (Error(err,p,depth)) let error_require r p = if r = "" then - error "This field is not available with the current compilation flags" p + typing_error "This field is not available with the current compilation flags" p else let r = if r = "sys" then "a system platform (php,neko,cpp,etc.)" @@ -305,6 +323,6 @@ let error_require r p = with _ -> "'" ^ r ^ "' to be enabled" in - error ("Accessing this field requires " ^ r) p + typing_error ("Accessing this field requires " ^ r) p -let invalid_assign p = error "Invalid assign" p \ No newline at end of file +let invalid_assign p = typing_error "Invalid assign" p diff --git a/src/core/globals.ml b/src/core/globals.ml index 81f70e8c..d3b7f642 100644 --- a/src/core/globals.ml +++ b/src/core/globals.ml @@ -5,6 +5,9 @@ type pos = { } type path = string list * string +type located = + | Message of string * pos + | Stack of located list module IntMap = Ptmap module StringMap = Map.Make(struct type t = string let compare = String.compare end) @@ -24,12 +27,31 @@ type platform = | Hl | Eval -let version = 4205 +let version = 4300 let version_major = version / 1000 let version_minor = (version mod 1000) / 100 let version_revision = (version mod 100) let version_pre = None +let null_pos = { pfile = "?"; pmin = -1; pmax = -1 } + +let located msg p = Message (msg,p) +let located_stack stack = Stack stack + +let rec extract_located = function + | Message (msg,p) -> [(msg, p)] + | Stack stack -> List.fold_left (fun acc s -> acc @ (extract_located s)) [] stack + +let rec relocate msg p = match msg with + | Message (msg,_) -> Message (msg,p) + | Stack [] -> Stack [] + | Stack (hd :: tl) -> Stack ((relocate hd p) :: tl) + +let rec extract_located_pos = function + | Message (_,p) -> p + | Stack [] -> null_pos + | Stack (hd :: _) -> extract_located_pos hd + let macro_platform = ref Neko let return_partial_type = ref false @@ -50,6 +72,7 @@ let platforms = [ Eval; ] +(** Expected to match `haxe.display.Display.Platform`. *) let platform_name = function | Cross -> "cross" | Js -> "js" @@ -64,13 +87,26 @@ let platform_name = function | Hl -> "hl" | Eval -> "eval" +let parse_platform = function + | "cross" -> Cross + | "js" -> Js + | "lua" -> Lua + | "neko" -> Neko + | "flash" -> Flash + | "php" -> Php + | "cpp" -> Cpp + | "cs" -> Cs + | "java" -> Java + | "python" -> Python + | "hl" -> Hl + | "eval" -> Eval + | p -> raise (failwith ("invalid platform " ^ p)) + let platform_list_help = function | [] -> "" | [p] -> " (" ^ platform_name p ^ " only)" | pl -> " (for " ^ String.concat "," (List.map platform_name pl) ^ ")" -let null_pos = { pfile = "?"; pmin = -1; pmax = -1 } - let mk_zero_range_pos p = { p with pmax = p.pmin } let s_type_path (p,s) = match p with [] -> s | _ -> String.concat "." p ^ "." ^ s @@ -82,14 +118,17 @@ let get_error_pos_ref : ((string -> int -> string) -> pos -> string) ref = ref ( Printf.sprintf "%s: characters %d-%d" p.pfile p.pmin p.pmax ) -let s_version with_build = +let s_version = let pre = Option.map_default (fun pre -> "-" ^ pre) "" version_pre in - let build = - match with_build, Version.version_extra with - | true, Some (_,build) -> "+" ^ build - | _, _ -> "" - in - Printf.sprintf "%d.%d.%d%s%s" version_major version_minor version_revision pre build + Printf.sprintf "%d.%d.%d%s" version_major version_minor version_revision pre + +let s_version_full = + match Version.version_extra with + | Some (_,build) -> s_version ^ "+" ^ build + | _ -> s_version + + +let patch_string_pos p s = { p with pmin = p.pmax - String.length s } (** Terminates compiler process and prints user-friendly instructions about filing an issue. @@ -111,7 +150,61 @@ let die ?p msg ml_loc = try snd (ExtString.String.split backtrace "\n") with ExtString.Invalid_string -> backtrace in - let ver = s_version true + let ver = s_version_full and os_type = if Sys.unix then "unix" else "windows" in Printf.eprintf "%s\nHaxe: %s; OS type: %s;\n%s\n%s" msg ver os_type ml_loc backtrace; - assert false \ No newline at end of file + assert false + +module MessageSeverity = struct + type t = + | Error + | Warning + | Information + | Hint + + let to_int = function + | Error -> 1 + | Warning -> 2 + | Information -> 3 + | Hint -> 4 +end + +module MessageKind = struct + type t = + | DKUnusedImport + | DKUnresolvedIdentifier + | DKCompilerMessage + | DKRemovableCode + | DKParserError + | DKDeprecationWarning + | DKInactiveBlock + | DKMissingFields + + let to_int = function + | DKUnusedImport -> 0 + | DKUnresolvedIdentifier -> 1 + | DKCompilerMessage -> 2 + | DKRemovableCode -> 3 + | DKParserError -> 4 + | DKDeprecationWarning -> 5 + | DKInactiveBlock -> 6 + | DKMissingFields -> 7 +end + +type compiler_message = { + cm_message : string; + cm_pos : pos; + cm_depth : int; + cm_kind : MessageKind.t; + cm_severity : MessageSeverity.t; +} + +let make_compiler_message msg p depth kind sev = { + cm_message = msg; + cm_pos = p; + cm_depth = depth; + cm_kind = kind; + cm_severity = sev; +} + +let i32_31 = Int32.of_int 31 diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 66a26463..8201fc5b 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -7,8 +7,8 @@ let expr_to_target e = let rec loop (e,p) = match e with | EConst (Ident s) when s <> "" -> [s] - | EField (e,s) -> s :: loop e - | _ -> Error.error "Invalid target expression for @:inheritDoc" p + | EField (e,s,_) -> s :: loop e + | _ -> Error.typing_error "Invalid target expression for @:inheritDoc" p in match loop e with | sub_name :: type_name :: pack when not (is_lower_ident type_name) -> @@ -16,7 +16,7 @@ let expr_to_target e = | type_name :: pack -> (List.rev pack, type_name), None | [] -> - Error.error "Invalid target path for @:inheritDoc" (snd e) + Error.typing_error "Invalid target path for @:inheritDoc" (snd e) let rec get_constructor c = match c.cl_constructor, c.cl_super with diff --git a/src/core/json/genjson.ml b/src/core/json/genjson.ml index e41b9351..ed85b04a 100644 --- a/src/core/json/genjson.ml +++ b/src/core/json/genjson.ml @@ -148,6 +148,7 @@ let rec generate_binop ctx op = | OpInterval -> "OpInterval",None | OpArrow -> "OpArrow",None | OpIn -> "OpIn",None + | OpNullCoal -> "OpNullCoal",None in generate_adt ctx (Some (["haxe";"macro"],"Binop")) name args @@ -176,7 +177,7 @@ and generate_metadata_entry ctx (m,el,p) = and generate_metadata ctx ml = let ml = List.filter (fun (m,_,_) -> - let (_,(_,flags)) = Meta.get_info m in + let (_,(_,flags),_) = Meta.get_info m in not (List.mem UsedInternally flags) ) ml in jlist (generate_metadata_entry ctx) ml @@ -218,7 +219,8 @@ let rec generate_type ctx t = let t = lazy_type f in (* return_partial_type := false; *) loop t - | TDynamic t -> "TDynamic",Some (if t == t_dynamic then jnull else generate_type ctx t) + | TDynamic None -> "TDynamic", Some jnull + | TDynamic (Some t) -> "TDynamic",Some (generate_type ctx t) | TInst(c,tl) -> "TInst",Some (generate_type_path_with_params ctx c.cl_module.m_path c.cl_path tl c.cl_meta) | TEnum(en,tl) -> "TEnum",Some (generate_type_path_with_params ctx en.e_module.m_path en.e_path tl en.e_meta) | TType(td,tl) -> "TType",Some (generate_type_path_with_params ctx td.t_module.m_path td.t_path tl td.t_meta) @@ -274,14 +276,15 @@ and generate_type_path_with_params ctx mpath tpath tl meta = (* type parameter *) -and generate_type_parameter ctx (s,t) = - let generate_constraints () = match follow t with +and generate_type_parameter ctx tp = + let generate_constraints () = match follow tp.ttp_type with | TInst({cl_kind = KTypeParameter tl},_) -> generate_types ctx tl | _ -> die "" __LOC__ in jobject [ - "name",jstring s; + "name",jstring tp.ttp_name; "constraints",generate_constraints (); + "defaultType",jopt (generate_type ctx) tp.ttp_default; ] (* texpr *) @@ -704,17 +707,28 @@ let generate_module_type ctx mt = (* module *) -let generate_module ctx m = +let generate_module cc m = jobject [ "id",jint m.m_id; "path",generate_module_path m.m_path; "types",jlist (fun mt -> generate_type_path m.m_path (t_infos mt).mt_path (t_infos mt).mt_meta) m.m_types; "file",jstring (Path.UniqueKey.lazy_path m.m_extra.m_file); "sign",jstring (Digest.to_hex m.m_extra.m_sign); + "cacheState",jstring (match m.m_extra.m_cache_state with + | MSGood -> "Good" + | MSBad reason -> Printer.s_module_skip_reason reason + | MSUnknown -> "Unknown"); "dependencies",jarray (PMap.fold (fun m acc -> (jobject [ "path",jstring (s_type_path m.m_path); "sign",jstring (Digest.to_hex m.m_extra.m_sign); ]) :: acc) m.m_extra.m_deps []); + "dependents",jarray (List.map (fun m -> (jobject [ + "path",jstring (s_type_path m.m_path); + "sign",jstring (Digest.to_hex m.m_extra.m_sign); + ])) (Hashtbl.fold (fun _ m' acc -> + if PMap.mem m.m_id m'.m_extra.m_deps then m' :: acc + else acc + ) cc#get_modules [])); ] let create_context ?jsonrpc gm = { @@ -732,4 +746,4 @@ let generate types file = let ch = open_out_bin file in Json.write_json (output_string ch) json; close_out ch; - t() \ No newline at end of file + t() diff --git a/src/core/meta.ml b/src/core/meta.ml index 11e1850d..57d0b4db 100644 --- a/src/core/meta.ml +++ b/src/core/meta.ml @@ -10,14 +10,35 @@ let rec remove m = function | (m2,_,_) :: l when m = m2 -> l | x :: l -> x :: remove m l -let to_string m = fst (get_info m) +type user_meta = { + doc : string; + flags : meta_parameter list; + source : string option; +} + +type meta_origin = + | Compiler + | UserDefined of string option + +let register_user_meta user_metas s data = + Hashtbl.replace user_metas s data + +let get_info ?user_metas m = match (user_metas,m) with + | (Some(user_metas), Custom(s)) when (Hashtbl.mem user_metas s) -> + let infos = Hashtbl.find user_metas s in + (s, (infos.doc, infos.flags), (UserDefined infos.source)) + | _ -> + let meta,infos = MetaList.get_info m in + (meta, infos, Compiler) + +let to_string m = match (get_info m) with (s,_,_) -> s let hmeta = let h = Hashtbl.create 0 in let rec loop i = let m = Obj.magic i in if m <> Last then begin - Hashtbl.add h (fst (get_info m)) m; + Hashtbl.add h (to_string m) m; loop (i + 1); end; in @@ -32,8 +53,8 @@ let from_string s = | '$' -> Dollar (String.sub s 1 (String.length s - 1)) | _ -> Custom s -let get_documentation d = - let t, (doc,flags) = get_info d in +let get_documentation user_metas d = + let t, (doc,flags), origin = get_info ~user_metas:user_metas d in if not (List.mem UsedInternally flags) then begin let params = ref [] and used = ref [] and pfs = ref [] in List.iter (function @@ -48,16 +69,20 @@ let get_documentation d = | l -> "(<" ^ String.concat ">, <" l ^ ">) " ) in let pfs = platform_list_help (List.rev !pfs) in + let origin = match origin with + | UserDefined Some s -> " (from " ^ s ^ ")" + | Compiler | UserDefined None -> "" + in let str = "@" ^ t in - Some (str,params ^ doc ^ pfs) + Some (str,params ^ doc ^ pfs ^ origin) end else None -let get_documentation_list () = +let get_documentation_list user_metas = let m = ref 0 in let rec loop i = let d = Obj.magic i in - if d <> Last then begin match get_documentation d with + if d <> Last then begin match get_documentation user_metas d with | None -> loop (i + 1) | Some (str,desc) -> if String.length str > !m then m := String.length str; @@ -68,14 +93,30 @@ let get_documentation_list () = let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) (loop 0) in all,!m -let get_all () = - let rec loop i = +let get_all user_metas = + let rec loop i acc = let d = Obj.magic i in - if d <> Last then d :: loop (i + 1) - else [] + if d <> Last then d :: loop (i + 1) acc + else acc in - loop 0 + + let all = loop 0 (Hashtbl.fold (fun str _ acc -> (Custom str) :: acc) user_metas []) in + List.sort (fun m1 m2 -> String.compare (to_string m1) (to_string m2)) all + +let get_user_documentation_list user_metas = + let m = ref 0 in + let user_meta_list = (Hashtbl.fold (fun meta _ acc -> + begin match get_documentation user_metas (Custom meta) with + | None -> acc + | Some (str, desc) -> + if String.length str > !m then m := String.length str; + (str,desc) :: acc + end + ) user_metas []) in + + let all = List.sort (fun (s1,_) (s2,_) -> String.compare s1 s2) user_meta_list in + all,!m let copy_from_to m src dst = try (get m src) :: dst - with Not_found -> dst \ No newline at end of file + with Not_found -> dst diff --git a/src/core/numeric.ml b/src/core/numeric.ml index a3070a5b..eb69967b 100644 --- a/src/core/numeric.ml +++ b/src/core/numeric.ml @@ -45,11 +45,15 @@ let float_repres f = Printf.sprintf "%.18g" f in valid_float_lexeme float_val +let is_whitespace code = + code > 8 && code < 14 + let parse_float s = let rec loop sp i = if i = String.length s then (if sp = 0 then s else String.sub s sp (i - sp)) else match String.unsafe_get s i with | ' ' when sp = i -> loop (sp + 1) (i + 1) + | c when sp = i && is_whitespace (Char.code c) -> loop (sp + 1) (i + 1) | '0'..'9' | '-' | '+' | 'e' | 'E' | '.' -> loop sp (i + 1) | _ -> String.sub s sp (i - sp) in @@ -64,14 +68,25 @@ let parse_int s = | '0'..'9' | 'a'..'f' | 'A'..'F' -> loop_hex sp (i + 1) | _ -> String.sub s sp (i - sp) in - let rec loop sp i digits_count = + let rec loop_dec sp i = if i = String.length s then (if sp = 0 then s else String.sub s sp (i - sp)) else match String.unsafe_get s i with - | '0'..'9' -> loop sp (i + 1) (digits_count + 1) - | ' ' | '+' when sp = i -> loop (sp + 1) (i + 1) digits_count - | c when sp = i && Char.code c > 8 && Char.code c < 14 -> loop (sp + 1) (i + 1) digits_count - | '-' when i = sp -> loop sp (i + 1) digits_count - | ('x' | 'X') when digits_count = 1 && String.get s (i - 1) = '0' -> loop_hex sp (i + 1) + | '0'..'9' -> loop_dec sp (i + 1) | _ -> String.sub s sp (i - sp) in - Int32.of_string (loop 0 0 0) \ No newline at end of file + let handle_digits sp i = + if i + 1 < String.length s && String.get s i = '0' && + (String.get s (i + 1) = 'x' || String.get s (i + 1) = 'X') + then loop_hex sp (i + 2) + else loop_dec sp i + in + let rec loop sp = + if sp = String.length s then "" else + match String.unsafe_get s sp with + | ' ' -> loop (sp + 1) + | '+' -> handle_digits (sp + 1) (sp + 1) + | '-' -> handle_digits sp (sp + 1) + | c when is_whitespace (Char.code c) -> loop (sp + 1) + | _ -> handle_digits sp sp + in + Int32.of_string (loop 0) diff --git a/src/core/path.ml b/src/core/path.ml index 3c0bdbf2..c5bf6d74 100644 --- a/src/core/path.ml +++ b/src/core/path.ml @@ -376,6 +376,11 @@ let mkdir_from_path path = let full_dot_path pack mname tname = if tname = mname then (pack,mname) else (pack @ [mname],tname) +let file_extension file = + match List.rev (ExtString.String.nsplit file ".") with + | e :: _ -> String.lowercase e + | [] -> "" + module FilePath = struct type t = { directory : string option; diff --git a/src/core/tFunctions.ml b/src/core/tFunctions.ml index 463cf302..2879e5ff 100644 --- a/src/core/tFunctions.ml +++ b/src/core/tFunctions.ml @@ -91,7 +91,7 @@ let null t p = mk (TConst TNull) t p let mk_mono() = TMono (!monomorph_create_ref ()) -let rec t_dynamic = TDynamic t_dynamic +let t_dynamic = TDynamic None let mk_anon ?fields status = let fields = match fields with Some fields -> fields | None -> PMap.empty in @@ -135,6 +135,21 @@ let mk_class m path pos name_pos = cl_descendants = []; } +let mk_typedef m path pos name_pos t = + { + t_path = path; + t_module = m; + t_pos = pos; + t_name_pos = name_pos; + t_private = false; + t_doc = None; + t_meta = []; + t_params = []; + t_using = []; + t_type = t; + t_restore = (fun () -> ()); + } + let module_extra file sign time kind policy = { m_file = Path.UniqueKey.create_lazy file; @@ -144,9 +159,9 @@ let module_extra file sign time kind policy = m_type_hints = []; m_import_positions = PMap.empty; }; - m_dirty = None; + m_cache_state = MSGood; m_added = 0; - m_mark = 0; + m_checked = 0; m_time = time; m_processed = 0; m_deps = PMap.empty; @@ -201,6 +216,7 @@ let null_abstract = { a_meta = []; a_params = []; a_using = []; + a_restore = (fun () -> ()); a_ops = []; a_unops = []; a_impl = None; @@ -212,11 +228,16 @@ let null_abstract = { a_array = []; a_read = None; a_write = None; + a_call = None; a_enum = false; } -let add_dependency m mdep = - if m != null_module && m != mdep then m.m_extra.m_deps <- PMap.add mdep.m_id mdep m.m_extra.m_deps +let add_dependency ?(skip_postprocess=false) m mdep = + if m != null_module && m != mdep then begin + m.m_extra.m_deps <- PMap.add mdep.m_id mdep m.m_extra.m_deps; + (* In case the module is cached, we'll have to run post-processing on it again (issue #10635) *) + if not skip_postprocess then m.m_extra.m_processed <- 0 + end let arg_name (a,_) = a.v_name @@ -273,8 +294,10 @@ let map loop t = let ft = lazy_type f in let ft2 = loop ft in if ft == ft2 then t else ft2 - | TDynamic t2 -> - if t == t2 then t else TDynamic (loop t2) + | TDynamic None -> + t + | TDynamic (Some t2) -> + TDynamic (Some (loop t2)) let iter loop t = match t with @@ -300,8 +323,10 @@ let iter loop t = | TLazy f -> let ft = lazy_type f in loop ft - | TDynamic t2 -> - if t != t2 then loop t2 + | TDynamic None -> + () + | TDynamic (Some t2) -> + loop t2 let duplicate t = let monos = ref [] in @@ -319,6 +344,16 @@ let duplicate t = in loop t +let dynamify_monos t = + let rec loop t = + match t with + | TMono { tm_type = None } -> + t_dynamic + | _ -> + map loop t + in + loop t + exception ApplyParamsRecursion (* substitute parameters with other types *) @@ -329,8 +364,8 @@ let apply_params ?stack cparams params t = let rec loop l1 l2 = match l1, l2 with | [] , [] -> [] - | (x,TLazy f) :: l1, _ -> loop ((x,lazy_type f) :: l1) l2 - | (_,t1) :: l1 , t2 :: l2 -> (t1,t2) :: loop l1 l2 + | {ttp_type = TLazy f} as tp :: l1, _ -> loop ({tp with ttp_type = lazy_type f} :: l1) l2 + | tp :: l1 , t2 :: l2 -> (tp.ttp_type,t2) :: loop l1 l2 | _ -> die "" __LOC__ in let subst = loop cparams params in @@ -427,14 +462,16 @@ let apply_params ?stack cparams params t = t else ft2 - | TDynamic t2 -> - if t == t2 then - t - else - TDynamic (loop t2) + | TDynamic None -> + t + | TDynamic (Some t2) -> + TDynamic (Some (loop t2)) in loop t +let apply_typedef td tl = + apply_params td.t_params tl td.t_type + let monomorphs eparams t = apply_params eparams (List.map (fun _ -> mk_mono()) eparams) t @@ -462,7 +499,7 @@ let rec follow t = | TLazy f -> follow (lazy_type f) | TType (t,tl) -> - follow (apply_params t.t_params tl t.t_type) + follow (apply_typedef t tl) | TAbstract({a_path = [],"Null"},[t]) -> follow t | _ -> t @@ -476,7 +513,7 @@ let follow_once t = | TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ -> t | TType (t,tl) -> - apply_params t.t_params tl t.t_type + apply_typedef t tl | TLazy f -> lazy_type f @@ -489,7 +526,7 @@ let rec follow_without_null t = | TLazy f -> follow_without_null (lazy_type f) | TType (t,tl) -> - follow_without_null (apply_params t.t_params tl t.t_type) + follow_without_null (apply_typedef t tl) | _ -> t let rec follow_without_type t = @@ -521,7 +558,7 @@ let rec is_nullable ?(no_lazy=false) = function | _ -> is_nullable (lazy_type f) ) | TType (t,tl) -> - is_nullable ~no_lazy (apply_params t.t_params tl t.t_type) + is_nullable ~no_lazy (apply_typedef t tl) | TFun _ -> false (* @@ -552,7 +589,7 @@ let rec is_null ?(no_lazy=false) = function | _ -> is_null (lazy_type f) ) | TType (t,tl) -> - is_null ~no_lazy (apply_params t.t_params tl t.t_type) + is_null ~no_lazy (apply_typedef t tl) | _ -> false @@ -565,7 +602,7 @@ let rec is_explicit_null = function | TLazy f -> is_explicit_null (lazy_type f) | TType (t,tl) -> - is_explicit_null (apply_params t.t_params tl t.t_type) + is_explicit_null (apply_typedef t tl) | _ -> false @@ -592,11 +629,29 @@ let concat e1 e2 = ) in mk e e2.etype (punion e1.epos e2.epos) +let extract_param_type tp = tp.ttp_type +let extract_param_types = List.map extract_param_type +let extract_param_name tp = tp.ttp_name +let lookup_param n l = + let rec loop l = match l with + | [] -> + raise Not_found + | tp :: l -> + if n = tp.ttp_name then tp.ttp_type else loop l + in + loop l + +let mk_type_param n t def = { + ttp_name = n; + ttp_type = t; + ttp_default = def; +} + let type_of_module_type = function - | TClassDecl c -> TInst (c,List.map snd c.cl_params) - | TEnumDecl e -> TEnum (e,List.map snd e.e_params) - | TTypeDecl t -> TType (t,List.map snd t.t_params) - | TAbstractDecl a -> TAbstract (a,List.map snd a.a_params) + | TClassDecl c -> TInst (c,extract_param_types c.cl_params) + | TEnumDecl e -> TEnum (e,extract_param_types e.e_params) + | TTypeDecl t -> TType (t,extract_param_types t.t_params) + | TAbstractDecl a -> TAbstract (a,extract_param_types a.a_params) let rec module_type_of_type = function | TInst(c,_) -> TClassDecl c @@ -612,8 +667,8 @@ let rec module_type_of_type = function raise Exit let tconst_to_const = function - | TInt i -> Int (Int32.to_string i) - | TFloat s -> Float s + | TInt i -> Int (Int32.to_string i, None) + | TFloat s -> Float (s, None) | TString s -> String(s,SDoubleQuotes) | TBool b -> Ident (if b then "true" else "false") | TNull -> Ident "null" @@ -800,4 +855,4 @@ let type_has_meta t m = let var_extra params e = { v_params = params; v_expr = e; -} \ No newline at end of file +} diff --git a/src/core/tOther.ml b/src/core/tOther.ml index 9851f9c8..fb1eca39 100644 --- a/src/core/tOther.ml +++ b/src/core/tOther.ml @@ -35,25 +35,61 @@ module TExprToExpr = struct | TAbstract (a,pl) -> tpath a.a_path a.a_module.m_path (List.map tparam pl) | TFun (args,ret) -> - CTFunction (List.map (fun (_,_,t) -> convert_type' t) args, (convert_type' ret)) + CTFunction (List.map (fun (n,o,t) -> + let ct = convert_type' t in + let ct = if n = "" then ct else CTNamed((n,null_pos),ct),null_pos in + if o then CTOptional ct,null_pos else ct + ) args, (convert_type' ret)) | TAnon a -> begin match !(a.a_status) with | Statics c -> tpath ([],"Class") ([],"Class") [TPType (tpath c.cl_path c.cl_path [],null_pos)] | EnumStatics e -> tpath ([],"Enum") ([],"Enum") [TPType (tpath e.e_path e.e_path [],null_pos)] | _ -> CTAnonymous (PMap.foldi (fun _ f acc -> + let access = ref [] in + let add flag = + access := (flag,null_pos) :: !access; + in + if has_class_field_flag f CfPublic then add APublic else add APrivate; + if has_class_field_flag f CfFinal then add AFinal; + if has_class_field_flag f CfExtern then add AExtern; + let kind = match (f.cf_kind,follow f.cf_type) with + | (Var v,ret) -> + let var_access_to_string va get_or_set = match va with + | AccNormal | AccCtor | AccInline | AccRequire _ -> "default" + | AccNo -> "null" + | AccNever -> "never" + | AccCall -> get_or_set + in + let read = (var_access_to_string v.v_read "get",null_pos) in + let write = (var_access_to_string v.v_write "set",null_pos) in + FProp (read,write,mk_type_hint f.cf_type null_pos,None) + | Method _,TFun(args,ret) -> + FFun({ + f_params = []; + f_args = List.map (fun (n,o,t) -> + ((n,null_pos),o,[],Some (convert_type t,null_pos),None) + ) args; + f_type = Some (convert_type ret,null_pos); + f_expr = None; + }) + | _ -> + die "" __LOC__ + in { cff_name = f.cf_name,null_pos; - cff_kind = FVar (mk_type_hint f.cf_type null_pos,None); + cff_kind = kind; cff_pos = f.cf_pos; cff_doc = f.cf_doc; cff_meta = f.cf_meta; - cff_access = []; + cff_access = !access; } :: acc ) a.a_fields []) end - | (TDynamic t2) as t -> - tpath ([],"Dynamic") ([],"Dynamic") (if t == t_dynamic then [] else [tparam t2]) + | TDynamic None -> + tpath ([],"Dynamic") ([],"Dynamic") [] + | TDynamic (Some t2) -> + tpath ([],"Dynamic") ([],"Dynamic") [tparam t2] | TLazy f -> convert_type (lazy_type f) @@ -91,7 +127,7 @@ module TExprToExpr = struct | TLocal v -> EConst (mk_ident v.v_name) | TArray (e1,e2) -> EArray (convert_expr e1,convert_expr e2) | TBinop (op,e1,e2) -> EBinop (op, convert_expr e1, convert_expr e2) - | TField (e,f) -> EField (convert_expr e, field_name f) + | TField (e,f) -> EField (convert_expr e, field_name f, EFNormal) | TTypeExpr t -> fst (mk_path (full_type_path t) e.epos) | TParenthesis e -> EParenthesis (convert_expr e) | TObjectDecl fl -> EObjectDecl (List.map (fun (k,e) -> k, convert_expr e) fl) @@ -208,7 +244,7 @@ module ExtType = struct let rec loop t = match t with | TInst(c,_) -> check c.cl_meta | TEnum(en,_) -> check en.e_meta - | TType(t,tl) -> check t.t_meta || (loop (apply_params t.t_params tl t.t_type)) + | TType(t,tl) -> check t.t_meta || (loop (apply_typedef t tl)) | TAbstract(a,_) -> check a.a_meta | TLazy f -> loop (lazy_type f) | TMono r -> @@ -227,44 +263,20 @@ end let no_meta = [] -let class_module_type c = { - t_path = [],"Class<" ^ (s_type_path c.cl_path) ^ ">" ; - t_module = c.cl_module; - t_doc = None; - t_pos = c.cl_pos; - t_name_pos = null_pos; - t_type = mk_anon ~fields:c.cl_statics (ref (Statics c)); - t_private = true; - t_params = []; - t_using = []; - t_meta = no_meta; -} +let class_module_type c = + let path = ([],"Class<" ^ (s_type_path c.cl_path) ^ ">") in + let t = mk_anon ~fields:c.cl_statics (ref (Statics c)) in + { (mk_typedef c.cl_module path c.cl_pos null_pos t) with t_private = true} -let enum_module_type m path p = { - t_path = [], "Enum<" ^ (s_type_path path) ^ ">"; - t_module = m; - t_doc = None; - t_pos = p; - t_name_pos = null_pos; - t_type = mk_mono(); - t_private = true; - t_params = []; - t_using = []; - t_meta = []; -} +let enum_module_type m path p = + let path = ([], "Enum<" ^ (s_type_path path) ^ ">") in + let t = mk_mono() in + {(mk_typedef m path p null_pos t) with t_private = true} -let abstract_module_type a tl = { - t_path = [],Printf.sprintf "Abstract<%s%s>" (s_type_path a.a_path) (s_type_params (ref []) tl); - t_module = a.a_module; - t_doc = None; - t_pos = a.a_pos; - t_name_pos = null_pos; - t_type = mk_anon (ref (AbstractStatics a)); - t_private = true; - t_params = []; - t_using = []; - t_meta = no_meta; -} +let abstract_module_type a tl = + let path = ([],Printf.sprintf "Abstract<%s%s>" (s_type_path a.a_path) (s_type_params (ref []) tl)) in + let t = mk_anon (ref (AbstractStatics a)) in + {(mk_typedef a.a_module path a.a_pos null_pos t) with t_private = true} module TClass = struct let get_member_fields' self_too c0 tl = @@ -287,7 +299,7 @@ module TClass = struct loop PMap.empty c0 tl let get_all_super_fields c = - get_member_fields' false c (List.map snd c.cl_params) + get_member_fields' false c (extract_param_types c.cl_params) let get_all_fields c tl = get_member_fields' true c tl diff --git a/src/core/tPrinting.ml b/src/core/tPrinting.ml index a7fd4e7d..de17dc97 100644 --- a/src/core/tPrinting.ml +++ b/src/core/tPrinting.ml @@ -87,8 +87,10 @@ let rec s_type ctx t = let fl = PMap.fold (fun f acc -> ((if Meta.has Meta.Optional f.cf_meta then " ?" else " ") ^ f.cf_name ^ " : " ^ s_type ctx f.cf_type) :: acc) a.a_fields [] in "{" ^ String.concat "," fl ^ " }" end - | TDynamic t2 -> - "Dynamic" ^ s_type_params ctx (if t == t2 then [] else [t2]) + | TDynamic None -> + "Dynamic" + | TDynamic (Some t2) -> + "Dynamic" ^ s_type_params ctx [t2] | TLazy f -> s_type ctx (lazy_type f) @@ -487,17 +489,32 @@ module Printer = struct let s_metadata metadata = s_list " " s_metadata_entry metadata - let s_type_param (s,t) = match follow t with + let s_type_param tp = match follow tp.ttp_type with | TInst({cl_kind = KTypeParameter tl1},tl2) -> - begin match tl1 with - | [] -> s - | _ -> Printf.sprintf "%s:%s" s (String.concat " & " (List.map s_type tl1)) + let s = match tl1 with + | [] -> tp.ttp_name + | _ -> Printf.sprintf "%s:%s" tp.ttp_name (String.concat " & " (List.map s_type tl1)) + in + begin match tp.ttp_default with + | None -> + s + | Some t -> + Printf.sprintf "%s = %s" s (s_type t) end | _ -> die "" __LOC__ let s_type_params tl = s_list ", " s_type_param tl + let s_flags flags all_flags = + let _,l = List.fold_left (fun (i,acc) name -> + if has_flag flags i then (i + 1,name :: acc) else (i + 1,acc) + ) (0,[]) all_flags in + String.concat " " l + + let s_tclass_field_flags flags = + s_flags flags flag_tclass_field_names + let s_tclass_field tabs cf = s_record_fields tabs [ "cf_name",cf.cf_name; @@ -509,6 +526,7 @@ module Printer = struct "cf_kind",s_kind cf.cf_kind; "cf_params",s_type_params cf.cf_params; "cf_expr",s_opt (s_expr_ast true "\t\t" s_type) cf.cf_expr; + "cf_flags",s_tclass_field_flags cf.cf_flags; ] let s_tclass tabs c = @@ -616,14 +634,30 @@ module Printer = struct | MExtern -> "MExtern" | MImport -> "MImport" + let s_module_skip_reason reason = + let rec loop stack = function + | DependencyDirty(path,reason) -> + (Printf.sprintf "%s%s - %s" (if stack = [] then "DependencyDirty " else "") (s_type_path path) (if List.mem path stack then "rec" else loop (path :: stack) reason)) + | Tainted cause -> "Tainted " ^ cause + | FileChanged file -> "FileChanged " ^ file + | Shadowed file -> "Shadowed " ^ file + | LibraryChanged -> "LibraryChanged" + in + loop [] reason + + let s_module_cache_state = function + | MSGood -> "Good" + | MSBad reason -> "Bad: " ^ (s_module_skip_reason reason) + | MSUnknown -> "Unknown" + let s_module_def_extra tabs me = s_record_fields tabs [ "m_file",Path.UniqueKey.lazy_path me.m_file; "m_sign",me.m_sign; "m_time",string_of_float me.m_time; - "m_dirty",s_opt s_type_path me.m_dirty; + "m_cache_state",s_module_cache_state me.m_cache_state; "m_added",string_of_int me.m_added; - "m_mark",string_of_int me.m_mark; + "m_checked",string_of_int me.m_checked; "m_deps",s_pmap string_of_int (fun m -> snd m.m_path) me.m_deps; "m_processed",string_of_int me.m_processed; "m_kind",s_module_kind me.m_kind; diff --git a/src/core/tType.ml b/src/core/tType.ml index 2be01bd8..5fdb0509 100644 --- a/src/core/tType.ml +++ b/src/core/tType.ml @@ -30,6 +30,19 @@ type module_check_policy = | CheckFileContentModification | NoCheckDependencies | NoCheckShadowing + | Retype + +type module_skip_reason = + | DependencyDirty of path * module_skip_reason + | Tainted of string + | FileChanged of string + | Shadowed of string + | LibraryChanged + +type module_cache_state = + | MSGood + | MSBad of module_skip_reason + | MSUnknown type t = | TMono of tmono @@ -38,7 +51,7 @@ type t = | TType of tdef * tparams | TFun of tsignature | TAnon of tanon - | TDynamic of t + | TDynamic of t option | TLazy of tlazy ref | TAbstract of tabstract * tparams @@ -77,7 +90,13 @@ and tsignature = (string * bool * t) list * t and tparams = t list -and type_params = (string * t) list +and typed_type_param = { + ttp_name : string; + ttp_type : t; + ttp_default : t option; +} + +and type_params = typed_type_param list and tconstant = | TInt of int32 @@ -107,6 +126,7 @@ and tvar_kind = | VInlined | VInlinedConstructorVariable | VExtractorVariable + | VAbstractThis and tvar = { mutable v_id : int; @@ -193,7 +213,7 @@ and tclass_field = { mutable cf_kind : field_kind; mutable cf_params : type_params; mutable cf_expr : texpr option; - mutable cf_expr_unoptimized : tfunc option; + mutable cf_expr_unoptimized : texpr option; mutable cf_overloads : tclass_field list; mutable cf_flags : int; } @@ -221,6 +241,7 @@ and tinfos = { mutable mt_meta : metadata; mt_params : type_params; mutable mt_using : (tclass * pos) list; + mutable mt_restore : unit -> unit; } and tclass = { @@ -233,6 +254,7 @@ and tclass = { mutable cl_meta : metadata; mutable cl_params : type_params; mutable cl_using : (tclass * pos) list; + mutable cl_restore : unit -> unit; (* do not insert any fields above *) mutable cl_kind : tclass_kind; mutable cl_flags : int; @@ -248,7 +270,6 @@ and tclass = { mutable cl_init : texpr option; mutable cl_build : unit -> build_state; - mutable cl_restore : unit -> unit; (* These are classes which directly extend or directly implement this class. Populated automatically in post-processing step (Filters.run) @@ -277,6 +298,7 @@ and tenum = { mutable e_meta : metadata; mutable e_params : type_params; mutable e_using : (tclass * pos) list; + mutable e_restore : unit -> unit; (* do not insert any fields above *) e_type : tdef; mutable e_extern : bool; @@ -294,6 +316,7 @@ and tdef = { mutable t_meta : metadata; mutable t_params : type_params; mutable t_using : (tclass * pos) list; + mutable t_restore : unit -> unit; (* do not insert any fields above *) mutable t_type : t; } @@ -308,6 +331,7 @@ and tabstract = { mutable a_meta : metadata; mutable a_params : type_params; mutable a_using : (tclass * pos) list; + mutable a_restore : unit -> unit; (* do not insert any fields above *) mutable a_ops : (Ast.binop * tclass_field) list; mutable a_unops : (Ast.unop * unop_flag * tclass_field) list; @@ -320,6 +344,7 @@ and tabstract = { mutable a_array : tclass_field list; mutable a_read : tclass_field option; mutable a_write : tclass_field option; + mutable a_call : tclass_field option; a_enum : bool; } @@ -349,11 +374,11 @@ and module_def_extra = { m_display : module_def_display; mutable m_check_policy : module_check_policy list; mutable m_time : float; - mutable m_dirty : path option; + mutable m_cache_state : module_cache_state; mutable m_added : int; - mutable m_mark : int; - mutable m_deps : (int,module_def) PMap.t; + mutable m_checked : int; mutable m_processed : int; + mutable m_deps : (int,module_def) PMap.t; mutable m_kind : module_kind; mutable m_binded_res : (string, string) PMap.t; mutable m_if_feature : (string *(tclass * tclass_field * bool)) list; @@ -392,6 +417,7 @@ type flag_tclass = | CFinal | CInterface | CAbstract + | CFunctionalInterface type flag_tclass_field = | CfPublic @@ -405,6 +431,13 @@ type flag_tclass_field = | CfImpl | CfEnum | CfGeneric + | CfDefault (* Interface field with default implementation (only valid on Java) *) + | CfPostProcessed (* Marker to indicate the field has been post-processed *) + +(* Order has to match declaration for printing*) +let flag_tclass_field_names = [ + "CfPublic";"CfStatic";"CfExtern";"CfFinal";"CfModifiesThis";"CfOverride";"CfAbstract";"CfOverload";"CfImpl";"CfEnum";"CfGeneric";"CfDefault" +] type flag_tvar = | VCaptured @@ -412,3 +445,4 @@ type flag_tvar = | VUsed (* used by the analyzer *) | VAssigned | VCaught + | VStatic diff --git a/src/core/tUnification.ml b/src/core/tUnification.ml index 33d445ef..c947fe36 100644 --- a/src/core/tUnification.ml +++ b/src/core/tUnification.ml @@ -260,9 +260,9 @@ module Monomorph = struct let spawn_constrained_monos map params = let checks = DynArray.create () in - let monos = List.map (fun (s,t) -> + let monos = List.map (fun tp -> let mono = create () in - begin match follow t with + begin match follow tp.ttp_type with | TInst ({ cl_kind = KTypeParameter constr; cl_path = path },_) when constr <> [] -> DynArray.add checks (mono,constr,s_type_path path) | _ -> @@ -296,11 +296,10 @@ let rec link e a b = | TEnum (_,tl) -> List.exists loop tl | TInst (_,tl) | TType (_,tl) | TAbstract (_,tl) -> List.exists loop tl | TFun (tl,t) -> List.exists (fun (_,_,t) -> loop t) tl || loop t - | TDynamic t2 -> - if t == t2 then - false - else - loop t2 + | TDynamic None -> + false + | TDynamic (Some t2) -> + loop t2 | TLazy f -> loop (lazy_type f) | TAnon a -> @@ -508,7 +507,9 @@ let rec type_eq uctx a b = (match t.tm_type with | None -> if param = EqCoreType || not (link t b a) then error [cannot_unify a b] | Some t -> type_eq uctx a t) - | TDynamic a , TDynamic b -> + | TDynamic None, TDynamic None -> + () + | TDynamic (Some a) , TDynamic (Some b) -> type_eq uctx a b | _ , _ when a == t_dynamic && param = EqBothDynamic -> () @@ -835,30 +836,34 @@ let rec unify (uctx : unification_context) a b = with Not_found -> error [has_no_field a "new"] end - | TDynamic t , _ -> - if t == a && uctx.allow_dynamic_to_cast then + | TDynamic None , _ -> + if uctx.allow_dynamic_to_cast then + () + else begin match b with + | TDynamic None -> + () + | _ -> + error [cannot_unify a b] + end + | TDynamic (Some t1) , _ -> + begin match b with + | TDynamic None -> () - else (match b with - | TDynamic t2 -> - if t2 != b then + | TDynamic (Some t2) -> + if t2 != t1 then (try - type_eq {uctx with equality_kind = EqRightDynamic} t t2 + type_eq {uctx with equality_kind = EqRightDynamic} t1 t2 with Unify_error l -> error (cannot_unify a b :: l)); | TAbstract(bb,tl) -> unify_from uctx a b bb tl | _ -> - error [cannot_unify a b]) - | _ , TDynamic t -> - if t == b then - () - else (match a with - | TDynamic t2 -> - if t2 != a then - (try - type_eq {uctx with equality_kind = EqRightDynamic} t t2 - with - Unify_error l -> error (cannot_unify a b :: l)); + error [cannot_unify a b] + end + | _ , TDynamic None -> + () + | _ , TDynamic (Some t1) -> + begin match a with | TAnon an -> (try (match !(an.a_status) with @@ -866,7 +871,7 @@ let rec unify (uctx : unification_context) a b = | _ -> ()); PMap.iter (fun _ f -> try - type_eq uctx (field_type f) t + type_eq uctx (field_type f) t1 with Unify_error l -> error (invalid_field f.cf_name :: l) ) an.a_fields @@ -875,7 +880,8 @@ let rec unify (uctx : unification_context) a b = | TAbstract(aa,tl) -> unify_to uctx a b aa tl | _ -> - error [cannot_unify a b]) + error [cannot_unify a b] + end | TAbstract (aa,tl), _ -> unify_to uctx a b aa tl | TInst ({ cl_kind = KTypeParameter ctl } as c,pl), TAbstract (bb,tl) -> @@ -912,7 +918,7 @@ and unify_anons uctx a b a1 a2 = Not_found -> match !(a1.a_status) with | Const when Meta.has Meta.Optional f2.cf_meta -> - () + a1.a_fields <- PMap.add f2.cf_name f2 a1.a_fields | _ -> error [has_no_field a n]; ) a2.a_fields; @@ -1012,6 +1018,7 @@ and unifies_to_field uctx a b ab tl (t,cf) = let unify_func = get_abstract_unify_func uctx EqStrict in let athis = map ab.a_this in (* we cannot allow implicit casts when the this type is not completely known yet *) + if Meta.has Meta.MultiType ab.a_meta && has_mono athis then raise (Unify_error []); with_variance uctx (type_eq {uctx with equality_kind = EqStrict}) athis (map ta); unify_func (map t) b; | _ -> die "" __LOC__) @@ -1024,7 +1031,7 @@ and unify_with_variance uctx f t1 t2 = let unify_nested t1 t2 = with_variance (get_nested_context uctx) f t1 t2 in let unify_tls tl1 tl2 = List.iter2 unify_nested tl1 tl2 in let get_this_type ab tl = follow_without_type (apply_params ab.a_params tl ab.a_this) in - let get_defined_type td tl = follow_without_type (apply_params td.t_params tl td.t_type) in + let get_defined_type td tl = follow_without_type (apply_typedef td tl) in let compare_underlying () = type_eq {uctx with equality_underlying = true; equality_kind = EqBothDynamic} t1 t2 in let unifies_abstract uctx a b ab tl ats = try @@ -1140,7 +1147,7 @@ module UnifyMinT = struct loop t); tl := t :: !tl; | TType (td,pl) -> - loop (apply_params td.t_params pl td.t_type); + loop (apply_typedef td pl); (* prioritize the most generic definition *) tl := t :: !tl; | TLazy f -> loop (lazy_type f) @@ -1184,6 +1191,41 @@ module UnifyMinT = struct let common_types = collect_base_types t0 in unify_min' uctx common_types tl end + +type unification_matrix_state = + | STop + | SType of t + | SBottom + +class unification_matrix (arity : int) = object(self) + val values = Array.make arity STop + + method join (t : t) (at : int) = + match values.(at) with + | STop -> + values.(at) <- SType t + | SBottom -> + () + | SType t' -> + if not (type_iseq t t') then values.(at) <- SBottom + + method get_type (at : int) = + match values.(at) with + | SType t -> + Some t + | _ -> + None + + method dump = + let pctx = print_context() in + let s_unification_matrix_state = function + | STop -> "STop" + | SType t -> "SType " ^ (s_type pctx t) + | SBottom -> "SBottom" + in + String.concat " ; " (List.map s_unification_matrix_state (Array.to_list values)) +end + ;; unify_ref := unify_custom;; unify_min_ref := UnifyMinT.unify_min;; diff --git a/src/core/texpr.ml b/src/core/texpr.ml index 6bf0c475..37a72d12 100644 --- a/src/core/texpr.ml +++ b/src/core/texpr.ml @@ -505,7 +505,7 @@ module Builder = struct | TFloat f -> mk (TConst (TFloat f)) basic.tfloat p | TBool b -> mk (TConst (TBool b)) basic.tbool p | TNull -> mk (TConst TNull) (basic.tnull (mk_mono())) p - | _ -> error "Unsupported constant" p + | _ -> typing_error "Unsupported constant" p let field e name t p = let f = @@ -566,19 +566,22 @@ let rec constructor_side_effects e = with Exit -> true +let replace_separators s c = + String.concat c (ExtString.String.nsplit s "_") + let type_constant basic c p = match c with - | Int s -> - if String.length s > 10 && String.sub s 0 2 = "0x" then error "Invalid hexadecimal integer" p; + | Int (s,_) -> + if String.length s > 10 && String.sub s 0 2 = "0x" then typing_error "Invalid hexadecimal integer" p; (try mk (TConst (TInt (Int32.of_string s))) basic.tint p with _ -> mk (TConst (TFloat s)) basic.tfloat p) - | Float f -> mk (TConst (TFloat f)) basic.tfloat p + | Float (f,_) -> mk (TConst (TFloat f)) basic.tfloat p | String(s,qs) -> mk (TConst (TString s)) basic.tstring p (* STRINGTODO: qs? *) | Ident "true" -> mk (TConst (TBool true)) basic.tbool p | Ident "false" -> mk (TConst (TBool false)) basic.tbool p | Ident "null" -> mk (TConst TNull) (basic.tnull (mk_mono())) p - | Ident t -> error ("Invalid constant : " ^ t) p - | Regexp _ -> error "Invalid constant" p + | Ident t -> typing_error ("Invalid constant : " ^ t) p + | Regexp _ -> typing_error "Invalid constant" p let rec type_constant_value basic (e,p) = match e with @@ -591,16 +594,16 @@ let rec type_constant_value basic (e,p) = | EArrayDecl el -> mk (TArrayDecl (List.map (type_constant_value basic) el)) (basic.tarray t_dynamic) p | _ -> - error "Constant value expected" p + typing_error "Constant value expected" p let is_constant_value basic e = - try (ignore (type_constant_value basic e); true) with Error (Custom _,_) -> false + try (ignore (type_constant_value basic e); true) with Error (Custom _,_,_) -> false let for_remap basic v e1 e2 p = let v' = alloc_var v.v_kind v.v_name e1.etype e1.epos in let ev' = mk (TLocal v') e1.etype e1.epos in let t1 = (Abstract.follow_with_abstracts e1.etype) in - let ehasnext = mk (TField(ev',try quick_field t1 "hasNext" with Not_found -> error (s_type (print_context()) t1 ^ "has no field hasNext()") p)) (tfun [] basic.tbool) e1.epos in + let ehasnext = mk (TField(ev',try quick_field t1 "hasNext" with Not_found -> typing_error (s_type (print_context()) t1 ^ "has no field hasNext()") p)) (tfun [] basic.tbool) e1.epos in let ehasnext = mk (TCall(ehasnext,[])) basic.tbool ehasnext.epos in let enext = mk (TField(ev',quick_field t1 "next")) (tfun [] v.v_type) e1.epos in let enext = mk (TCall(enext,[])) v.v_type e1.epos in @@ -635,7 +638,7 @@ let build_metadata api t = let make_meta_field ml = let h = Hashtbl.create 0 in mk (TObjectDecl (List.map (fun (f,el,p) -> - if Hashtbl.mem h f then error ("Duplicate metadata '" ^ f ^ "'") p; + if Hashtbl.mem h f then typing_error ("Duplicate metadata '" ^ f ^ "'") p; Hashtbl.add h f (); (f,null_pos,NoQuotes), mk (match el with [] -> TConst TNull | _ -> TArrayDecl (List.map (type_constant_value api) el)) (api.tarray t_dynamic) p ) ml)) t_dynamic p @@ -757,7 +760,7 @@ let dump_with_pos tabs e = add "TCast"; loop e1; | TMeta((m,_,_),e1) -> - add ("TMeta " ^ fst (Meta.get_info m)); + add ("TMeta " ^ (Meta.to_string m)); loop e1 in loop' tabs e; @@ -818,3 +821,103 @@ let reduce_unsafe_casts ?(require_cast=false) e t = | { eexpr = TCast _ } as result -> result | result when require_cast -> { e with eexpr = TCast(result,None) } | result -> result + +(** + Returns a position spanning from the first expr to the last expr in `el`. + Returns `default_pos` if `el` is empty or values of `pfile` of the first and + the last positions are different. +*) +let punion_el default_pos el = + match el with + | [] -> default_pos + | [{ epos = p }] -> p + | { epos = first } :: { epos = last } :: el -> + let rec loop = function + | [] -> last + | [{ epos = last }] -> last + | _ :: el -> loop el + in + let last = loop el in + if first.pfile <> last.pfile then + default_pos + else + punion first last + +let is_exhaustive e1 def = + let rec loop e1 = match e1.eexpr with + | TMeta((Meta.Exhaustive,_,_),_) -> true + | TMeta(_, e1) | TParenthesis e1 -> loop e1 + | _ -> false + in + def <> None || loop e1 + +let rec is_true_expr e1 = match e1.eexpr with + | TConst(TBool true) -> true + | TParenthesis e1 -> is_true_expr e1 + | _ -> false + +let rec is_false_expr e1 = match e1.eexpr with + | TConst(TBool false) -> true + | TParenthesis e1 -> is_false_expr e1 + | _ -> false + +module DeadEnd = struct + exception BreakOrContinue + + (* + Checks if execution of provided expression is guaranteed to be terminated with `return`, `throw`, `break` or `continue`. + *) + let has_dead_end e = + let rec loop e = + let in_loop e = + try + loop e + with BreakOrContinue -> + false + in + match e.eexpr with + | TContinue | TBreak -> + raise BreakOrContinue + | TThrow e1 -> + loop e1 || true + | TReturn (Some e1) -> + loop e1 || true (* recurse first, could be `return continue` *) + | TReturn None -> + true + | TFunction _ -> + false (* This isn't executed, so don't recurse *) + | TIf (cond, if_body, Some else_body) -> + loop cond || loop if_body && loop else_body + | TIf (cond, _, None) -> + loop cond + | TSwitch(e1, cases, def) -> + let check_exhaustive () = + (is_exhaustive e1 def) && List.for_all (fun (el,e) -> + List.exists loop el || + loop e + ) cases && + Option.map_default (loop ) true def (* true because we know it's exhaustive *) + in + loop e1 || check_exhaustive () + | TFor(_, e1, _) -> + loop e1 + | TBinop(OpBoolAnd, e1, e2) -> + loop e1 || is_true_expr e1 && loop e2 + | TBinop(OpBoolOr, e1, e2) -> + loop e1 || is_false_expr e1 && loop e2 + | TWhile(cond, body, flag) -> + loop cond || ((flag = DoWhile || is_true_expr cond) && in_loop body) + | TTry(e1,[]) -> + loop e1 + | TTry(_,catches) -> + (* The try expression is irrelevant because we have to conservatively assume that + anything could throw control flow into the catch expressions. *) + List.for_all (fun (_,e) -> loop e) catches + | _ -> + check_expr loop e + in + try + loop e + with BreakOrContinue -> + true +end diff --git a/src/core/warning.ml b/src/core/warning.ml new file mode 100644 index 00000000..ba83b9e2 --- /dev/null +++ b/src/core/warning.ml @@ -0,0 +1,95 @@ +open Globals +open Error +include WarningList + +type warning_mode = + | WMEnable + | WMDisable + +type warning_option = { + wo_warning : warning; + wo_mode : warning_mode; +} + +let parse_options s ps lexbuf = + let fail msg p = + Error.typing_error msg {p with pmin = ps.pmin + p.pmin; pmax = ps.pmin + p.pmax} + in + let parse_string s p = + begin try + from_string s + with Exit -> + fail (Printf.sprintf "Unknown warning: %s" s) p + end + in + let parse_warning () = match Lexer.token lexbuf with + | Const (Ident s),p -> + parse_string s p + | (_,p) -> + fail "Expected identifier" p + in + let add acc mode warning = + { wo_warning = warning; wo_mode = mode } :: acc + in + let rec next acc = match Lexer.token lexbuf with + | Binop OpAdd,_ -> + next (add acc WMEnable (parse_warning())) + | Binop OpSub,_ -> + next (add acc WMDisable (parse_warning())) + | Eof,_ -> + List.rev acc + | (_,p) -> + fail "Expected + or -" p + in + next [] + +let parse_options s ps = + let restore = Lexer.reinit ps.pfile in + Std.finally (fun () -> + restore() + ) (fun () -> + let lexbuf = Sedlexing.Utf8.from_string s in + parse_options s ps lexbuf + ) () + +let from_meta ml = + let parse_arg e = match fst e with + | Ast.EConst (String(s,_)) -> + let p = snd e in + parse_options s {p with pmin = p.pmin + 1; pmax = p.pmax - 1} (* pmin is on the quote *) + | _ -> + Error.typing_error "String expected" (snd e) + in + let rec loop acc ml = match ml with + | (Meta.HaxeWarning,args,_) :: ml -> + let acc = List.fold_left (fun acc arg -> + (parse_arg arg) :: acc + ) acc args in + loop acc ml + | _ :: ml -> + loop acc ml + | [] -> + List.rev acc + in + loop [] ml + +let get_mode w (l : warning_option list list) = + let rec matches w id = + id = w || match (warning_obj w).w_parent with + | None -> false + | Some w' -> matches w' id + in + let rec loop mode l = match l with + | [] -> + mode + | l2 :: l -> + let rec loop2 mode l = match l with + | [] -> + mode + | opt :: l -> + let mode = if matches w opt.wo_warning then opt.wo_mode else mode in + loop2 mode l + in + loop (loop2 mode l2) l + in + loop WMEnable (* ? *) l \ No newline at end of file diff --git a/src/dune b/src/dune index b88ba872..6e21a11d 100644 --- a/src/dune +++ b/src/dune @@ -9,9 +9,9 @@ (library (name haxe) (libraries - extc extproc extlib_leftovers ilib javalib mbedtls neko objsize pcre swflib ttflib ziplib + extc extproc extlib_leftovers ilib javalib mbedtls neko objsize pcre2 swflib ttflib ziplib json - unix str threads dynlink + unix str bigarray threads dynlink xml-light extlib ptmap sha luv ) diff --git a/src/filters/defaultArguments.ml b/src/filters/defaultArguments.ml index c660a690..5c0d2312 100644 --- a/src/filters/defaultArguments.ml +++ b/src/filters/defaultArguments.ml @@ -61,6 +61,8 @@ let rec change_func com cl cf = List.iter (change_func com cl) cf.cf_overloads; match cf.cf_kind, follow cf.cf_type with + | _ when has_class_field_flag cf CfPostProcessed -> + () | Var _, _ | Method MethDynamic, _ -> () | _, TFun(args, ret) -> diff --git a/src/filters/exceptions.ml b/src/filters/exceptions.ml index 45c06239..349b5178 100644 --- a/src/filters/exceptions.ml +++ b/src/filters/exceptions.ml @@ -8,6 +8,7 @@ open Fields open Error let haxe_exception_type_path = (["haxe"],"Exception") +let value_exception_type_path = (["haxe"],"ValueException") type context = { typer : typer; @@ -15,9 +16,13 @@ type context = { config : exceptions_config; wildcard_catch_type : Type.t; base_throw_type : Type.t; + throws_anything : bool; + catches_anything : bool; haxe_exception_class : tclass; haxe_exception_type : Type.t; haxe_native_stack_trace : tclass; + value_exception_type : Type.t; + value_exception_class : tclass; } let is_dynamic t = @@ -31,12 +36,12 @@ let is_dynamic t = let haxe_exception_static_call ctx method_name args p = let method_field = try PMap.find method_name ctx.haxe_exception_class.cl_statics - with Not_found -> error ("haxe.Exception has no field " ^ method_name) p + with Not_found -> typing_error ("haxe.Exception has no field " ^ method_name) p in let return_type = match follow method_field.cf_type with | TFun(_,t) -> t - | _ -> error ("haxe.Exception." ^ method_name ^ " is not a function and cannot be called") p + | _ -> typing_error ("haxe.Exception." ^ method_name ^ " is not a function and cannot be called") p in add_dependency ctx.typer.curclass.cl_module ctx.haxe_exception_class.cl_module; make_static_call ctx.typer ctx.haxe_exception_class method_field (fun t -> t) args return_type p @@ -52,10 +57,10 @@ let haxe_exception_instance_call ctx haxe_exception method_name args p = match follow cf.cf_type with | TFun(_,t) -> t | _ -> - error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is not a function and cannot be called") p + typing_error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is not a function and cannot be called") p in make_call ctx.typer efield args rt p - | _ -> error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is expected to be an instance method") p + | _ -> typing_error ((s_type (print_context()) haxe_exception.etype) ^ "." ^ method_name ^ " is expected to be an instance method") p (** Generate `Std.isOfType(e, t)` @@ -65,16 +70,16 @@ let std_is ctx e t p = let std_cls = match Typeload.load_type_raise ctx.typer ([],"Std") "Std" p with | TClassDecl cls -> cls - | _ -> error "Std is expected to be a class" p + | _ -> typing_error "Std is expected to be a class" p in let isOfType_field = try PMap.find "isOfType" std_cls.cl_statics - with Not_found -> error ("Std has no field isOfType") p + with Not_found -> typing_error ("Std has no field isOfType") p in let return_type = match follow isOfType_field.cf_type with | TFun(_,t) -> t - | _ -> error ("Std.isOfType is not a function and cannot be called") p + | _ -> typing_error ("Std.isOfType is not a function and cannot be called") p in let type_expr = { eexpr = TTypeExpr(module_type_of_type t); etype = t; epos = p } in make_static_call ctx.typer std_cls isOfType_field (fun t -> t) [e; type_expr] return_type p @@ -102,14 +107,22 @@ let is_in_list t lst = (** Check if `t` can be thrown without wrapping. *) -let rec is_native_throw cfg t = - is_in_list t cfg.ec_native_throws +let rec is_native_throw ctx t = + ctx.throws_anything || is_in_list t ctx.config.ec_native_throws (** Check if `t` can be caught without wrapping. *) -let rec is_native_catch cfg t = - is_in_list t cfg.ec_native_catches +let rec is_native_catch ctx t = + ctx.catches_anything || is_in_list t ctx.config.ec_native_catches + +(** + Check if `t` can be used for a Haxe-specific wildcard catch. + E.g `catch(e:Dynamic)` or `catch(e:haxe.Exception)` +*) +let is_haxe_wildcard_catch ctx t = + let t = Abstract.follow_with_abstracts t in + t == t_dynamic || fast_eq ctx.haxe_exception_type t (** Check if `cls` is or extends (if `check_parent=true`) `haxe.Exception` @@ -145,12 +158,20 @@ let rec contains_throw_or_try e = | TThrow _ | TTry _ -> true | _ -> check_expr contains_throw_or_try e +(** + Check if expression represents an exception wrapped with `haxe.Exception.thrown` +*) +let is_wrapped_exception e = + match e.eexpr with + | TMeta ((Meta.WrappedException, _, _), _) -> true + | _ -> false + (** Returns `true` if `e` has to be wrapped with `haxe.Exception.thrown(e)` to be thrown. *) -let requires_wrapped_throw cfg e = - if cfg.ec_special_throw e then +let requires_wrapped_throw ctx e = + if ctx.throws_anything || is_wrapped_exception e || ctx.config.ec_special_throw e then false else (* @@ -166,17 +187,20 @@ let requires_wrapped_throw cfg e = | _ -> true in is_stored_haxe_exception() - || (not (is_native_throw cfg e.etype) && not (is_haxe_exception e.etype)) + || (not (is_native_throw ctx e.etype) && not (is_haxe_exception e.etype)) (** Generate a throw of a native exception. *) let throw_native ctx e_thrown t p = let e_native = - if requires_wrapped_throw ctx.config e_thrown then + if requires_wrapped_throw ctx e_thrown then let thrown = haxe_exception_static_call ctx "thrown" [e_thrown] p in - if is_dynamic ctx.base_throw_type then thrown - else mk_cast thrown ctx.base_throw_type p + let wrapped = + if is_dynamic ctx.base_throw_type then thrown + else mk_cast thrown ctx.base_throw_type p + in + mk (TMeta ((Meta.WrappedException,[],p),wrapped)) thrown.etype p else e_thrown in @@ -231,6 +255,191 @@ class catch ctx catch_local catch_pos = mk (TBlock[]) ctx.basic.tvoid p end +(** + Transforms the set of catch-blocks into `if(Std.is(e, ExceptionType)`-fest. + + `t` - the type of `try...catch` expression under transformation. +*) +let catches_to_ifs ctx catches t p = + match catches with + | [] -> [] + | ((first_v, first_body) :: _) as rest -> + let catch_var = alloc_var VGenerated "`" ctx.wildcard_catch_type first_v.v_pos in + add_var_flag catch_var VCaught; + let catch_local = mk (TLocal catch_var) catch_var.v_type catch_var.v_pos in + let body = + let catch = new catch ctx catch_local p in + let rec transform = function + | (v, body) :: rest -> + let current_t = Abstract.follow_with_abstracts v.v_type in + let var_used = is_var_used v body in + (* catch(e:ExtendsHaxeError) *) + if is_haxe_exception current_t then + let condition = + (* catch(e:haxe.Exception) is a wildcard catch *) + if fast_eq ctx.haxe_exception_type current_t then + mk (TConst (TBool true)) ctx.basic.tbool v.v_pos + else + std_is ctx (catch#get_haxe_exception v.v_pos) v.v_type v.v_pos + in + let body = + if var_used then + mk (TBlock [ + (* var v:ExceptionType = cast haxe_exception_local; *) + mk (TVar (v, Some (mk_cast (catch#get_haxe_exception v.v_pos) v.v_type v.v_pos))) ctx.basic.tvoid v.v_pos; + body + ]) body.etype body.epos + else + body + in + compose condition body rest + (* catch(e:Dynamic) *) + else if current_t == t_dynamic then + begin + set_needs_exception_stack catch_var; + (* this is a wildcard catch *) + let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in + let body = + mk (TBlock [ + if var_used then + (* `var v:Dynamic = catch_local;` or `var v:Dynamic = haxe_exception_local.unwrap();` *) + let e = + if ctx.catches_anything then catch_local + else catch#unwrap v.v_pos + in + mk (TVar (v, Some e)) ctx.basic.tvoid v.v_pos + else + mk (TBlock[]) ctx.basic.tvoid v.v_pos; + body + ]) body.etype body.epos + in + compose condition body rest + end + (* catch(e:NativeWildcardException) *) + else if fast_eq ctx.wildcard_catch_type current_t then + begin + set_needs_exception_stack catch_var; + (* this is a wildcard catch *) + let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in + let body = + mk (TBlock [ + (* var v:NativeWildcardException = catch_var; *) + if var_used then + mk (TVar (v, Some catch_local)) ctx.basic.tvoid v.v_pos + else + mk (TBlock[]) ctx.basic.tvoid v.v_pos; + body + ]) body.etype body.epos + in + compose condition body rest + end + (* catch(e:AnythingElse) *) + else begin + set_needs_exception_stack catch_var; + let condition = + (* Std.isOfType(haxe_exception_local.unwrap(), ExceptionType) *) + std_is ctx (catch#unwrap v.v_pos) v.v_type v.v_pos + in + let body = + mk (TBlock [ + (* var v:ExceptionType = cast haxe_exception_local.unwrap() *) + if var_used then + mk (TVar (v, Some (mk_cast (catch#unwrap v.v_pos) v.v_type v.v_pos))) ctx.basic.tvoid v.v_pos + else + mk (TBlock[]) ctx.basic.tvoid v.v_pos; + body + ]) body.etype body.epos + in + compose condition body rest + end + | [] -> mk (TThrow catch_local) t p + and compose condition body rest_catches = + let else_body = + match rest_catches with + | [] -> mk (TThrow catch_local) (mk_mono()) p + | _ -> transform rest_catches + in + mk (TIf(condition, body, Some else_body)) t p + in + let transformed_catches = transform rest in + (* haxe.Exception.caught(catch_var) *) + let exprs = [ + (* var haxe_exception_local = haxe.Exception.caught(catch_var); *) + catch#declare_haxe_exception catch_var.v_pos; + (* var unwrapped_local = haxe_exception_local.unwrap(); *) + catch#declare_unwrap catch_var.v_pos; + transformed_catches + ] in + mk (TBlock exprs) t p + in (* let body = *) + [(catch_var,body)] + +(** + Transforms set of catches into + ``` + catch(e:ValueException) { + if(Std.isOfType(e.value, Exception1)) { + <...> + } else if(Std.isOfType(e.value, Exception2)) { + <...> + } else <...> + } + ``` +*) +let catches_as_value_exception ctx non_value_exception_catches value_exception_catch t p = + match non_value_exception_catches, value_exception_catch with + | [], None -> + die ~p "Nothing to transform into ValueException catch" __LOC__ + | [], Some catch -> + catch + | (first_v,_) :: _, _ -> + let catch_var = + match value_exception_catch with + | Some (catch_var, _) -> + catch_var + | None -> + let catch_var = alloc_var VGenerated "`" ctx.value_exception_type first_v.v_pos in + add_var_flag catch_var VCaught; + catch_var + in + let catch_local = + mk (TLocal catch_var) catch_var.v_type catch_var.v_pos + in + (* catch_local.value *) + let catch_local_value = + let cf = + try PMap.find "value" ctx.value_exception_class.cl_fields + with Not_found -> die "haxe.ValueException is missing field \"value\"" __LOC__ + in + mk (TField (catch_local, FInstance (ctx.value_exception_class,[],cf))) cf.cf_type catch_local.epos + in + let rec traverse catches final_else = + match catches with + | [] -> final_else + | (v,body) :: rest -> + set_needs_exception_stack catch_var; + (* Std.isOfType(catch_local.value, ExceptionType) *) + let condition = std_is ctx catch_local_value v.v_type v.v_pos in + let body = + mk (TBlock [ + (* var v:ExceptionType = cast catch_local.value *) + if is_var_used v body then + mk (TVar (v, Some (mk_cast catch_local_value v.v_type v.v_pos))) ctx.basic.tvoid v.v_pos + else + mk (TBlock[]) ctx.basic.tvoid v.v_pos; + body + ]) body.etype body.epos + in + mk (TIf (condition,body,Some (traverse rest final_else))) t p + in + let final_else = + Option.map_default + (fun (_,body) -> body) + (mk (TThrow catch_local) t_dynamic p) + value_exception_catch + in + (catch_var, traverse non_value_exception_catches final_else) + (** Transform user-written `catches` to a set of catches, which would not require special handling in the target generator. @@ -259,126 +468,52 @@ class catch ctx catch_local catch_pos = ``` *) let catch_native ctx catches t p = - let rec transform = function - | [] -> [] + let rec transform handle_as_value_exception value_exception_catch catches = + match catches with + | [] -> + (match handle_as_value_exception, value_exception_catch with + | [], None -> + [] + | [], Some catch -> + catches_to_ifs ctx [catch] t p + | _, _ -> + [catches_as_value_exception ctx handle_as_value_exception None t p] + @ Option.map_default (fun catch -> catches_to_ifs ctx [catch] t p) [] value_exception_catch + ) + (* Haxe-specific wildcard catches should go to if-fest because they need additional handling *) + | (v,_) :: _ when is_haxe_wildcard_catch ctx v.v_type -> + (match handle_as_value_exception with + | [] -> + catches_to_ifs ctx catches t p + | _ -> + catches_as_value_exception ctx handle_as_value_exception None t p + :: catches_to_ifs ctx catches t p + ) + | (v,_) as current :: rest when ctx.catches_anything && fast_eq ctx.value_exception_type (Abstract.follow_with_abstracts v.v_type) -> + catches_as_value_exception ctx handle_as_value_exception (Some current) t p + :: transform [] (Some (Option.default current value_exception_catch)) rest (* Keep catches for native exceptions intact *) - | (v,_) as current :: rest when (is_native_catch ctx.config v.v_type) - (* - In case haxe.Exception extends native exception on current target. - We don't want it to be generated as a native catch. - *) - && not (fast_eq ctx.haxe_exception_type (follow v.v_type)) -> - current :: (transform rest) - (* Everything else falls into `if(Std.is(e, ExceptionType)`-fest *) - | ((first_v, first_body) :: _) as rest -> - let catch_var = alloc_var VGenerated "`" ctx.wildcard_catch_type first_v.v_pos in - add_var_flag catch_var VCaught; - let catch_local = mk (TLocal catch_var) catch_var.v_type catch_var.v_pos in - let body = - let catch = new catch ctx catch_local p in - let rec transform = function - | (v, body) :: rest -> - let current_t = Abstract.follow_with_abstracts v.v_type in - let var_used = is_var_used v body in - (* catch(e:ExtendsHaxeError) *) - if is_haxe_exception current_t then - let condition = - (* catch(e:haxe.Exception) is a wildcard catch *) - if fast_eq ctx.haxe_exception_type current_t then - mk (TConst (TBool true)) ctx.basic.tbool v.v_pos - else begin - std_is ctx (catch#get_haxe_exception v.v_pos) v.v_type v.v_pos - end - in - let body = - if var_used then - mk (TBlock [ - (* var v:ExceptionType = cast haxe_exception_local; *) - mk (TVar (v, Some (mk_cast (catch#get_haxe_exception v.v_pos) v.v_type v.v_pos))) ctx.basic.tvoid v.v_pos; - body - ]) body.etype body.epos - else - body - in - compose condition body rest - (* catch(e:Dynamic) *) - else if current_t == t_dynamic then - begin - set_needs_exception_stack catch_var; - (* this is a wildcard catch *) - let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in - let body = - mk (TBlock [ - (* var v:Dynamic = haxe_exception_local.unwrap(); *) - if var_used then - mk (TVar (v, Some (catch#unwrap v.v_pos))) ctx.basic.tvoid v.v_pos - else - mk (TBlock[]) ctx.basic.tvoid v.v_pos; - body - ]) body.etype body.epos - in - compose condition body rest - end - (* catch(e:NativeWildcardException) *) - else if fast_eq ctx.wildcard_catch_type current_t then - begin - set_needs_exception_stack catch_var; - (* this is a wildcard catch *) - let condition = mk (TConst (TBool true)) ctx.basic.tbool v.v_pos in - let body = - mk (TBlock [ - (* var v:NativeWildcardException = catch_var; *) - if var_used then - mk (TVar (v, Some catch_local)) ctx.basic.tvoid v.v_pos - else - mk (TBlock[]) ctx.basic.tvoid v.v_pos; - body - ]) body.etype body.epos - in - compose condition body rest - end - (* catch(e:AnythingElse) *) - else begin - set_needs_exception_stack catch_var; - let condition = - (* Std.isOfType(haxe_exception_local.unwrap(), ExceptionType) *) - std_is ctx (catch#unwrap v.v_pos) v.v_type v.v_pos - in - let body = - mk (TBlock [ - (* var v:ExceptionType = cast haxe_exception_local.unwrap() *) - if var_used then - mk (TVar (v, Some (mk_cast (catch#unwrap v.v_pos) v.v_type v.v_pos))) ctx.basic.tvoid v.v_pos - else - mk (TBlock[]) ctx.basic.tvoid v.v_pos; - body - ]) body.etype body.epos - in - compose condition body rest - end - | [] -> mk (TThrow catch_local) t p - and compose condition body rest_catches = - let else_body = - match rest_catches with - | [] -> mk (TThrow catch_local) (mk_mono()) p - | _ -> transform rest_catches - in - mk (TIf(condition, body, Some else_body)) t p - in - let transformed_catches = transform rest in - (* haxe.Exception.caught(catch_var) *) - let exprs = [ - (* var haxe_exception_local = haxe.Exception.caught(catch_var); *) - catch#declare_haxe_exception catch_var.v_pos; - (* var unwrapped_local = haxe_exception_local.unwrap(); *) - catch#declare_unwrap catch_var.v_pos; - transformed_catches - ] in - mk (TBlock exprs) t p - in (* let body = *) - [(catch_var,body)] + | (v,_) as current :: rest when (is_native_catch ctx v.v_type) -> + let handle_as_value_exception = + (* + If current target can catch any type natively, then we also need + to check if `new haxe.ValueException(value)` with the same type of + `value` was thrown. That is, we should be able to catch `throw 'error'` + and `throw new ValueException('error')` with a single `catch(e:String)` + expression in user's code to be consistent with targets which can't + catch arbitrary types. + *) + if ctx.catches_anything && not (is_haxe_exception v.v_type) then + current :: handle_as_value_exception + else + handle_as_value_exception + in + current :: (transform handle_as_value_exception value_exception_catch rest) + (* everything else goes to if-fest *) + | catches -> + catches_to_ifs ctx (handle_as_value_exception @ catches) t p in - transform catches + transform [] None catches (** Transform `throw` and `try..catch` expressions. @@ -407,12 +542,19 @@ let filter tctx = and haxe_exception_type, haxe_exception_class = match Typeload.load_instance tctx (tp haxe_exception_type_path) true with | TInst(cls,_) as t -> t,cls - | _ -> error "haxe.Exception is expected to be a class" null_pos + | _ -> typing_error "haxe.Exception is expected to be a class" null_pos + and value_exception_type, value_exception_class = + match Typeload.load_instance tctx (tp value_exception_type_path) true with + | TInst(cls,_) as t -> t,cls + | _ -> typing_error "haxe.ValueException is expected to be a class" null_pos and haxe_native_stack_trace = match Typeload.load_instance tctx (tp (["haxe"],"NativeStackTrace")) true with | TInst(cls,_) -> cls | TAbstract({ a_impl = Some cls },_) -> cls - | _ -> error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos + | _ -> typing_error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos + in + let is_path_of_dynamic (pack,name) = + name = "Dynamic" && (pack = [] || pack = ["StdTypes"]) in let ctx = { typer = tctx; @@ -420,9 +562,13 @@ let filter tctx = config = config; wildcard_catch_type = wildcard_catch_type; base_throw_type = base_throw_type; + throws_anything = is_path_of_dynamic config.ec_base_throw && config.ec_avoid_wrapping; + catches_anything = is_path_of_dynamic config.ec_wildcard_catch && config.ec_avoid_wrapping; haxe_exception_class = haxe_exception_class; haxe_exception_type = haxe_exception_type; haxe_native_stack_trace = haxe_native_stack_trace; + value_exception_type = value_exception_type; + value_exception_class = value_exception_class; } in let rec run e = match e.eexpr with @@ -455,7 +601,7 @@ let insert_save_stacks tctx = match Typeload.load_type_def tctx null_pos tp with | TClassDecl cls -> cls | TAbstractDecl { a_impl = Some cls } -> cls - | _ -> error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos + | _ -> typing_error "haxe.NativeStackTrace is expected to be a class or an abstract" null_pos in let rec contains_insertion_points e = match e.eexpr with @@ -471,12 +617,12 @@ let insert_save_stacks tctx = if has_feature tctx.com "haxe.NativeStackTrace.exceptionStack" then let method_field = try PMap.find "saveStack" native_stack_trace_cls.cl_statics - with Not_found -> error ("haxe.NativeStackTrace has no field saveStack") null_pos + with Not_found -> typing_error ("haxe.NativeStackTrace has no field saveStack") null_pos in let return_type = match follow method_field.cf_type with | TFun(_,t) -> t - | _ -> error ("haxe.NativeStackTrace." ^ method_field.cf_name ^ " is not a function and cannot be called") null_pos + | _ -> typing_error ("haxe.NativeStackTrace." ^ method_field.cf_name ^ " is not a function and cannot be called") null_pos in let catch_local = mk (TLocal catch_var) catch_var.v_type catch_var.v_pos in begin @@ -530,7 +676,7 @@ let patch_constructors tctx = let this = { eexpr = TConst(TThis); etype = t; epos = p } in let faccess = try quick_field t "__shiftStack" - with Not_found -> error "haxe.Exception has no field __shiftStack" p + with Not_found -> typing_error "haxe.Exception has no field __shiftStack" p in match faccess with | FInstance (_,_,cf) -> @@ -539,10 +685,10 @@ let patch_constructors tctx = match follow cf.cf_type with | TFun(_,t) -> t | _ -> - error "haxe.Exception.__shiftStack is not a function and cannot be called" cf.cf_name_pos + typing_error "haxe.Exception.__shiftStack is not a function and cannot be called" cf.cf_name_pos in make_call tctx efield [] rt p - | _ -> error "haxe.Exception.__shiftStack is expected to be an instance method" p + | _ -> typing_error "haxe.Exception.__shiftStack is expected to be an instance method" p in TypeloadFunction.add_constructor tctx cls true cls.cl_name_pos; Option.may (fun cf -> ignore(follow cf.cf_type)) cls.cl_constructor; diff --git a/src/filters/filters.ml b/src/filters/filters.ml index d4ee0a3a..03ee1c35 100644 --- a/src/filters/filters.ml +++ b/src/filters/filters.ml @@ -66,10 +66,69 @@ let rec add_final_return e = { e with eexpr = TFunction f } | _ -> e +module LocalStatic = struct + let promote_local_static ctx lut v eo = + let name = Printf.sprintf "%s_%s" ctx.curfield.cf_name v.v_name in + begin try + let cf = PMap.find name ctx.curclass.cl_statics in + display_error ctx.com (Printf.sprintf "The expanded name of this local (%s) conflicts with another static field" name) v.v_pos; + typing_error ~depth:1 "Conflicting field was found here" cf.cf_name_pos; + with Not_found -> + let cf = mk_field name ~static:true v.v_type v.v_pos v.v_pos in + begin match eo with + | None -> + () + | Some e -> + let rec loop e = match e.eexpr with + | TLocal _ | TFunction _ -> + typing_error "Accessing local variables in static initialization is not allowed" e.epos + | TConst (TThis | TSuper) -> + typing_error "Accessing `this` in static initialization is not allowed" e.epos + | TReturn _ | TBreak | TContinue -> + typing_error "This kind of control flow in static initialization is not allowed" e.epos + | _ -> + iter loop e + in + loop e; + cf.cf_expr <- Some e + end; + TClass.add_field ctx.curclass cf; + Hashtbl.add lut v.v_id cf + end + + let find_local_static lut v = + Hashtbl.find lut v.v_id + + let run ctx e = + let local_static_lut = Hashtbl.create 0 in + let c = ctx.curclass in + let rec run e = match e.eexpr with + | TBlock el -> + let el = ExtList.List.filter_map (fun e -> match e.eexpr with + | TVar(v,eo) when has_var_flag v VStatic -> + promote_local_static ctx local_static_lut v eo; + None + | _ -> + Some (run e) + ) el in + { e with eexpr = TBlock el } + | TLocal v when has_var_flag v VStatic -> + begin try + let cf = find_local_static local_static_lut v in + Texpr.Builder.make_static_field c cf e.epos + with Not_found -> + typing_error (Printf.sprintf "Could not find local static %s (id %i)" v.v_name v.v_id) e.epos + end + | _ -> + Type.map_expr run e + in + run e +end + (* -------------------------------------------------------------------------- *) (* CHECK LOCAL VARS INIT *) -let check_local_vars_init com e = +let check_local_vars_init ctx e = let intersect vl1 vl2 = PMap.mapi (fun v t -> t && PMap.find v vl2) vl1 in @@ -84,17 +143,21 @@ let check_local_vars_init com e = in let declared = ref [] in let outside_vars = ref IntMap.empty in + (* Set variables which belong to current function *) + let set_all_vars vars = + vars := PMap.mapi (fun id is_set -> if IntMap.mem id !outside_vars then is_set else true) !vars + in let rec loop vars e = match e.eexpr with | TLocal v -> let init = (try PMap.find v.v_id !vars with Not_found -> true) in if not init then begin if IntMap.mem v.v_id !outside_vars then - if v.v_name = "this" then com.warning "this might be used before assigning a value to it" e.epos - else com.warning ("Local variable " ^ v.v_name ^ " might be used before being initialized") e.epos + if v.v_name = "this" then warning ctx WVarInit "this might be used before assigning a value to it" e.epos + else warning ctx WVarInit ("Local variable " ^ v.v_name ^ " might be used before being initialized") e.epos else - if v.v_name = "this" then error "Missing this = value" e.epos - else error ("Local variable " ^ v.v_name ^ " used without being initialized") e.epos + if v.v_name = "this" then typing_error "Missing this = value" e.epos + else typing_error ("Local variable " ^ v.v_name ^ " used without being initialized") e.epos end | TVar (v,eo) -> begin @@ -115,8 +178,16 @@ let check_local_vars_init com e = restore vars old_vars (List.rev !declared); declared := old; | TBinop (OpAssign,{ eexpr = TLocal v },e) when PMap.mem v.v_id !vars -> - loop vars e; - vars := PMap.add v.v_id true !vars + begin match (Texpr.skip e).eexpr with + | TFunction _ -> + (* We can be sure that the function doesn't execute immediately, so it's fine to + consider the local initialized (issue #9919). *) + vars := PMap.add v.v_id true !vars; + loop vars e; + | _ -> + loop vars e; + vars := PMap.add v.v_id true !vars + end | TIf (e1,e2,eo) -> loop vars e1; let vbase = !vars in @@ -174,10 +245,10 @@ let check_local_vars_init com e = join vars cvars) (* mark all reachable vars as initialized, since we don't exit the block *) | TBreak | TContinue | TReturn None -> - vars := PMap.map (fun _ -> true) !vars + set_all_vars vars | TThrow e | TReturn (Some e) -> loop vars e; - vars := PMap.map (fun _ -> true) !vars + set_all_vars vars | TFunction tf -> let old = !outside_vars in (* Mark all known variables as "outside" so we can ignore their initialization state within the function. @@ -194,7 +265,7 @@ let check_local_vars_init com e = let mark_switch_break_loops e = let add_loop_label n e = - { e with eexpr = TMeta ((Meta.LoopLabel,[(EConst(Int(string_of_int n)),e.epos)],e.epos), e) } + { e with eexpr = TMeta ((Meta.LoopLabel,[(EConst(Int(string_of_int n, None)),e.epos)],e.epos), e) } in let in_switch = ref false in let did_found = ref (-1) in @@ -274,7 +345,7 @@ let check_abstract_as_value e = match e.eexpr with | TField ({ eexpr = TTypeExpr _ }, _) -> () | TTypeExpr(TClassDecl {cl_kind = KAbstractImpl a}) when not (Meta.has Meta.RuntimeValue a.a_meta) -> - error "Cannot use abstract as value" e.epos + typing_error "Cannot use abstract as value" e.epos | _ -> Type.iter loop e in loop e; @@ -282,70 +353,9 @@ let check_abstract_as_value e = (* PASS 1 end *) -(* Saves a class state so it can be restored later, e.g. after DCE or native path rewrite *) -let save_class_state ctx t = match t with - | TClassDecl c -> - let vars = ref [] in - let rec save_vars e = - let add v = vars := (v, v.v_type) :: !vars in - match e.eexpr with - | TFunction fn -> - List.iter (fun (v, _) -> add v) fn.tf_args; - save_vars fn.tf_expr - | TVar (v, e) -> - add v; - Option.may save_vars e - | _ -> - iter save_vars e - in - let mk_field_restore f = - Option.may save_vars f.cf_expr; - let rec mk_overload_restore f = - f.cf_name,f.cf_kind,f.cf_expr,f.cf_type,f.cf_meta,f.cf_params - in - ( f,mk_overload_restore f, List.map (fun f -> f,mk_overload_restore f) f.cf_overloads ) - in - let restore_field (f,res,overloads) = - let restore_field (f,(name,kind,expr,t,meta,params)) = - f.cf_name <- name; f.cf_kind <- kind; f.cf_expr <- expr; f.cf_type <- t; f.cf_meta <- meta; f.cf_params <- params; - f - in - let f = restore_field (f,res) in - f.cf_overloads <- List.map restore_field overloads; - f - in - let mk_pmap lst = - List.fold_left (fun pmap f -> PMap.add f.cf_name f pmap) PMap.empty lst - in - - let meta = c.cl_meta and path = c.cl_path and ext = (has_class_flag c CExtern) in - let sup = c.cl_super and impl = c.cl_implements in - let csr = Option.map (mk_field_restore) c.cl_constructor in - let ofr = List.map (mk_field_restore) c.cl_ordered_fields in - let osr = List.map (mk_field_restore) c.cl_ordered_statics in - let init = c.cl_init in - Option.may save_vars init; - c.cl_restore <- (fun() -> - c.cl_super <- sup; - c.cl_implements <- impl; - c.cl_meta <- meta; - if ext then add_class_flag c CExtern else remove_class_flag c CExtern; - c.cl_path <- path; - c.cl_init <- init; - c.cl_ordered_fields <- List.map restore_field ofr; - c.cl_ordered_statics <- List.map restore_field osr; - c.cl_fields <- mk_pmap c.cl_ordered_fields; - c.cl_statics <- mk_pmap c.cl_ordered_statics; - c.cl_constructor <- Option.map restore_field csr; - c.cl_descendants <- []; - List.iter (fun (v, t) -> v.v_type <- t) !vars; - ) - | _ -> - () - (* PASS 2 begin *) -let remove_generic_base ctx t = match t with +let remove_generic_base t = match t with | TClassDecl c when is_removable_class c -> add_class_flag c CExtern; | _ -> @@ -353,16 +363,16 @@ let remove_generic_base ctx t = match t with (* Removes extern and macro fields, also checks for Void fields *) -let remove_extern_fields ctx t = match t with +let remove_extern_fields com t = match t with | TClassDecl c -> - if not (Common.defined ctx.com Define.DocGen) then begin + if not (Common.defined com Define.DocGen) then begin c.cl_ordered_fields <- List.filter (fun f -> - let b = is_removable_field ctx f in + let b = is_removable_field com f in if b then c.cl_fields <- PMap.remove f.cf_name c.cl_fields; not b ) c.cl_ordered_fields; c.cl_ordered_statics <- List.filter (fun f -> - let b = is_removable_field ctx f in + let b = is_removable_field com f in if b then c.cl_statics <- PMap.remove f.cf_name c.cl_statics; not b ) c.cl_ordered_statics; @@ -378,12 +388,12 @@ let remove_extern_fields ctx t = match t with let check_private_path ctx t = match t with | TClassDecl c when c.cl_private -> let rpath = (fst c.cl_module.m_path,"_" ^ snd c.cl_module.m_path) in - if Hashtbl.mem ctx.g.types_module rpath then error ("This private class name will clash with " ^ s_type_path rpath) c.cl_pos; + if ctx.com.type_to_module#mem rpath then typing_error ("This private class name will clash with " ^ s_type_path rpath) c.cl_pos; | _ -> () (* Rewrites class or enum paths if @:native metadata is set *) -let apply_native_paths ctx t = +let apply_native_paths t = let get_real_name meta name = let name',p = get_native_name meta in (Meta.RealPath,[Ast.EConst (Ast.String (name,SDoubleQuotes)), p], p), name' @@ -448,14 +458,14 @@ let apply_native_paths ctx t = () (* Adds the __rtti field if required *) -let add_rtti ctx t = +let add_rtti com t = let rec has_rtti c = Meta.has Meta.Rtti c.cl_meta || match c.cl_super with None -> false | Some (csup,_) -> has_rtti csup in match t with | TClassDecl c when has_rtti c && not (PMap.mem "__rtti" c.cl_statics) -> - let f = mk_field ~static:true "__rtti" ctx.t.tstring c.cl_pos null_pos in - let str = Genxml.gen_type_string ctx.com t in + let f = mk_field ~static:true "__rtti" com.basic.tstring c.cl_pos null_pos in + let str = Genxml.gen_type_string com t in f.cf_expr <- Some (mk (TConst (TString str)) f.cf_type c.cl_pos); c.cl_ordered_statics <- f :: c.cl_ordered_statics; c.cl_statics <- PMap.add f.cf_name f c.cl_statics; @@ -463,9 +473,9 @@ let add_rtti ctx t = () (* Adds member field initializations as assignments to the constructor *) -let add_field_inits locals ctx t = +let add_field_inits cl_path locals com t = let apply c = - let ethis = mk (TConst TThis) (TInst (c,List.map snd c.cl_params)) c.cl_pos in + let ethis = mk (TConst TThis) (TInst (c,extract_param_types c.cl_params)) c.cl_pos in (* TODO: we have to find a variable name which is not used in any of the functions *) let v = alloc_var VGenerated "_g" ethis.etype ethis.epos in let need_this = ref false in @@ -482,18 +492,18 @@ let add_field_inits locals ctx t = match cf.cf_expr with | None -> die "" __LOC__ | Some e -> - let lhs = mk (TField({ ethis with epos = cf.cf_pos },FInstance (c,List.map snd c.cl_params,cf))) cf.cf_type cf.cf_pos in + let lhs = mk (TField({ ethis with epos = cf.cf_pos },FInstance (c,extract_param_types c.cl_params,cf))) cf.cf_type cf.cf_pos in cf.cf_expr <- None; mk (TBinop(OpAssign,lhs,e)) cf.cf_type e.epos ) inits in let el = if !need_this then (mk (TVar((v, Some ethis))) ethis.etype ethis.epos) :: el else el in let cf = match c.cl_constructor with | None -> - let ct = TFun([],ctx.com.basic.tvoid) in + let ct = TFun([],com.basic.tvoid) in let ce = mk (TFunction { tf_args = []; - tf_type = ctx.com.basic.tvoid; - tf_expr = mk (TBlock el) ctx.com.basic.tvoid c.cl_pos; + tf_type = com.basic.tvoid; + tf_expr = mk (TBlock el) com.basic.tvoid c.cl_pos; }) ct c.cl_pos in let ctor = mk_field "new" ct c.cl_pos null_pos in ctor.cf_kind <- Method MethNormal; @@ -502,18 +512,20 @@ let add_field_inits locals ctx t = match cf.cf_expr with | Some { eexpr = TFunction f } -> let bl = match f.tf_expr with {eexpr = TBlock b } -> b | x -> [x] in - let ce = mk (TFunction {f with tf_expr = mk (TBlock (el @ bl)) ctx.com.basic.tvoid c.cl_pos }) cf.cf_type cf.cf_pos in + let ce = mk (TFunction {f with tf_expr = mk (TBlock (el @ bl)) com.basic.tvoid c.cl_pos }) cf.cf_type cf.cf_pos in {cf with cf_expr = Some ce }; | _ -> die "" __LOC__ in - let config = AnalyzerConfig.get_field_config ctx.com c cf in - Analyzer.Run.run_on_field ctx config c cf; + let config = AnalyzerConfig.get_field_config com c cf in + remove_class_field_flag cf CfPostProcessed; + Analyzer.Run.run_on_field com config c cf; + add_class_field_flag cf CfPostProcessed; (match cf.cf_expr with | Some e -> (* This seems a bit expensive, but hopefully constructor expressions aren't that massive. *) - let e = RenameVars.run ctx locals e in - let e = Optimizer.sanitize ctx.com e in + let e = RenameVars.run cl_path locals e in + let e = Optimizer.sanitize com e in cf.cf_expr <- Some e | _ -> ()); @@ -526,15 +538,15 @@ let add_field_inits locals ctx t = () (* Adds the __meta__ field if required *) -let add_meta_field ctx t = match t with +let add_meta_field com t = match t with | TClassDecl c -> - (match Texpr.build_metadata ctx.com.basic t with + (match Texpr.build_metadata com.basic t with | None -> () | Some e -> - add_feature ctx.com "has_metadata"; + add_feature com "has_metadata"; let cf = mk_field ~static:true "__meta__" e.etype e.epos null_pos in cf.cf_expr <- Some e; - let can_deal_with_interface_metadata () = match ctx.com.platform with + let can_deal_with_interface_metadata () = match com.platform with | Cs | Java -> false | _ -> true in @@ -544,7 +556,7 @@ let add_meta_field ctx t = match t with let ncls = mk_class c.cl_module path c.cl_pos null_pos in ncls.cl_ordered_statics <- cf :: ncls.cl_ordered_statics; ncls.cl_statics <- PMap.add cf.cf_name cf ncls.cl_statics; - ctx.com.types <- ctx.com.types @ [ TClassDecl ncls ]; + com.types <- com.types @ [ TClassDecl ncls ]; c.cl_meta <- (Meta.Custom ":hasMetadata",[],e.epos) :: c.cl_meta end else begin c.cl_ordered_statics <- cf :: c.cl_ordered_statics; @@ -562,8 +574,8 @@ let check_cs_events com t = match t with | TClassDecl cl when not (has_class_flag cl CExtern) -> let check fields f = match f.cf_kind with - | Var { v_read = AccNormal; v_write = AccNormal } when Meta.has Meta.Event f.cf_meta -> - if (has_class_field_flag f CfPublic) then error "@:event fields must be private" f.cf_pos; + | Var { v_read = AccNormal; v_write = AccNormal } when Meta.has Meta.Event f.cf_meta && not (has_class_field_flag f CfPostProcessed) -> + if (has_class_field_flag f CfPublic) then typing_error "@:event fields must be private" f.cf_pos; (* prevent generating reflect helpers for the event in gencommon *) f.cf_meta <- (Meta.SkipReflection, [], f.cf_pos) :: f.cf_meta; @@ -572,7 +584,7 @@ let check_cs_events com t = match t with let tmeth = (tfun [f.cf_type] com.basic.tvoid) in let process_event_method name = - let m = try PMap.find name fields with Not_found -> error ("Missing event method: " ^ name) f.cf_pos in + let m = try PMap.find name fields with Not_found -> typing_error ("Missing event method: " ^ name) f.cf_pos in (* check method signature *) begin @@ -603,17 +615,17 @@ let check_cs_events com t = match t with () (* Removes interfaces tagged with @:remove metadata *) -let check_remove_metadata ctx t = match t with +let check_remove_metadata t = match t with | TClassDecl c -> c.cl_implements <- List.filter (fun (c,_) -> not (Meta.has Meta.Remove c.cl_meta)) c.cl_implements; | _ -> () (* Checks for Void class fields *) -let check_void_field ctx t = match t with +let check_void_field t = match t with | TClassDecl c -> let check f = - match follow f.cf_type with TAbstract({a_path=[],"Void"},_) -> error "Fields of type Void are not allowed" f.cf_pos | _ -> (); + match follow f.cf_type with TAbstract({a_path=[],"Void"},_) -> typing_error "Fields of type Void are not allowed" f.cf_pos | _ -> (); in List.iter check c.cl_ordered_fields; List.iter check c.cl_ordered_statics; @@ -623,7 +635,7 @@ let check_void_field ctx t = match t with (* Interfaces have no 'super', but can extend many other interfaces. This makes the first extended (implemented) interface the super for efficiency reasons (you can get one for 'free') and leaves the remaining ones as 'implemented' *) -let promote_first_interface_to_super ctx t = match t with +let promote_first_interface_to_super t = match t with | TClassDecl c when (has_class_flag c CInterface) -> begin match c.cl_implements with | ({ cl_path = ["cpp";"rtti"],_ },_ ) :: _ -> () @@ -635,16 +647,17 @@ let promote_first_interface_to_super ctx t = match t with | _ -> () -let commit_features ctx t = +let commit_features com t = let m = (t_infos t).mt_module in Hashtbl.iter (fun k v -> - Common.add_feature ctx.com k; + Common.add_feature com k; ) m.m_extra.m_features let check_reserved_type_paths ctx t = let check path pos = - if List.mem path ctx.com.config.pf_reserved_type_paths then - ctx.com.warning ("Type path " ^ (s_type_path path) ^ " is reserved on this target") pos + if List.mem path ctx.com.config.pf_reserved_type_paths then begin + warning ctx WReservedTypePath ("Type path " ^ (s_type_path path) ^ " is reserved on this target") pos + end in match t with | TClassDecl c when not (has_class_flag c CExtern) -> check c.cl_path c.cl_pos @@ -653,18 +666,12 @@ let check_reserved_type_paths ctx t = (* PASS 3 end *) -let pp_counter = ref 1 - -let is_cached t = +let is_cached com t = let m = (t_infos t).mt_module.m_extra in - if m.m_processed = 0 then m.m_processed <- !pp_counter; - m.m_processed <> !pp_counter + m.m_processed <> 0 && m.m_processed < com.compilation_step let apply_filters_once ctx filters t = - if not (is_cached t) then run_expression_filters None ctx filters t - -let next_compilation() = - incr pp_counter + if not (is_cached ctx.com t) then run_expression_filters None ctx filters t let iter_expressions fl mt = match mt with @@ -706,10 +713,212 @@ module ForRemap = struct loop e end +let destruction tctx detail_times main locals = + let com = tctx.com in + let t = filter_timer detail_times ["type 2"] in + (* PASS 2: type filters pre-DCE *) + List.iter (fun t -> + remove_generic_base t; + remove_extern_fields com t; + (* check @:remove metadata before DCE so it is ignored there (issue #2923) *) + check_remove_metadata t; + ) com.types; + t(); + com.stage <- CDceStart; + let t = filter_timer detail_times ["dce"] in + (* DCE *) + let dce_mode = try Common.defined_value com Define.Dce with _ -> "no" in + let dce_mode = match dce_mode with + | "full" -> if Common.defined com Define.Interp then Dce.DceNo else DceFull + | "std" -> DceStd + | "no" -> DceNo + | _ -> failwith ("Unknown DCE mode " ^ dce_mode) + in + Dce.run com main dce_mode; + t(); + com.stage <- CDceDone; + (* PASS 3: type filters post-DCE *) + List.iter + (run_expression_filters + ~ignore_processed_status:true + (timer_label detail_times []) + tctx + (* This has to run after DCE, or otherwise its condition always holds. *) + ["insert_save_stacks",Exceptions.insert_save_stacks tctx] + ) + com.types; + let type_filters = [ + Exceptions.patch_constructors tctx; (* TODO: I don't believe this should load_instance anything at this point... *) + check_private_path tctx; + apply_native_paths; + add_rtti com; + (match com.platform with | Java | Cs -> (fun _ -> ()) | _ -> (fun mt -> add_field_inits tctx.curclass.cl_path locals com mt)); + (match com.platform with Hl -> (fun _ -> ()) | _ -> add_meta_field com); + check_void_field; + (match com.platform with | Cpp -> promote_first_interface_to_super | _ -> (fun _ -> ())); + commit_features com; + (if com.config.pf_reserved_type_paths <> [] then check_reserved_type_paths tctx else (fun _ -> ())); + ] in + let type_filters = match com.platform with + | Cs -> type_filters @ [ fun t -> InterfaceProps.run t ] + | _ -> type_filters + in + let t = filter_timer detail_times ["type 3"] in + List.iter (fun t -> + begin match t with + | TClassDecl c -> + tctx.curclass <- c + | _ -> + () + end; + List.iter (fun f -> f t) type_filters + ) com.types; + t(); + com.callbacks#run com.callbacks#get_after_filters; + com.stage <- CFilteringDone + +let update_cache_dependencies com t = + let visited_anons = ref [] in + let rec check_t m t = match t with + | TInst(c,tl) -> + add_dependency m c.cl_module; + List.iter (check_t m) tl; + | TEnum(en,tl) -> + add_dependency m en.e_module; + List.iter (check_t m) tl; + | TType(t,tl) -> + add_dependency m t.t_module; + List.iter (check_t m) tl; + | TAbstract(a,tl) -> + add_dependency m a.a_module; + List.iter (check_t m) tl; + | TFun(targs,tret) -> + List.iter (fun (_,_,t) -> check_t m t) targs; + check_t m tret; + | TAnon an -> + if not (List.memq an !visited_anons) then begin + visited_anons := an :: !visited_anons; + PMap.iter (fun _ cf -> check_t m cf.cf_type) an.a_fields + end + | TMono r -> + begin match r.tm_type with + | Some t -> + check_t m t + | _ -> + (* Bind any still open monomorph that's part of a signature to Dynamic now (issue #10653) *) + Monomorph.do_bind r t_dynamic; + end + | TLazy f -> + check_t m (lazy_type f) + | TDynamic None -> + () + | TDynamic (Some t) -> + check_t m t + in + let rec check_field m cf = + check_t m cf.cf_type; + List.iter (check_field m) cf.cf_overloads + in + match t with + | TClassDecl c -> + List.iter (check_field c.cl_module) c.cl_ordered_statics; + List.iter (check_field c.cl_module) c.cl_ordered_fields; + (match c.cl_constructor with None -> () | Some cf -> check_field c.cl_module cf); + | _ -> + () + +(* Saves a class state so it can be restored later, e.g. after DCE or native path rewrite *) +let save_class_state ctx t = + (* Update m_processed here. This means that nothing should add a dependency afterwards because + then the module is immediately considered uncached again *) + (t_infos t).mt_module.m_extra.m_processed <- ctx.com.compilation_step; + match t with + | TClassDecl c -> + let vars = ref [] in + let rec save_vars e = + let add v = vars := (v, v.v_type) :: !vars in + match e.eexpr with + | TFunction fn -> + List.iter (fun (v, _) -> add v) fn.tf_args; + save_vars fn.tf_expr + | TVar (v, e) -> + add v; + Option.may save_vars e + | _ -> + iter save_vars e + in + let mk_field_restore f = + Option.may save_vars f.cf_expr; + let rec mk_overload_restore f = + add_class_field_flag f CfPostProcessed; + f.cf_name,f.cf_kind,f.cf_expr,f.cf_type,f.cf_meta,f.cf_params + in + ( f,mk_overload_restore f, List.map (fun f -> f,mk_overload_restore f) f.cf_overloads ) + in + let restore_field (f,res,overloads) = + let restore_field (f,(name,kind,expr,t,meta,params)) = + f.cf_name <- name; f.cf_kind <- kind; f.cf_expr <- expr; f.cf_type <- t; f.cf_meta <- meta; f.cf_params <- params; + f + in + let f = restore_field (f,res) in + f.cf_overloads <- List.map restore_field overloads; + f + in + let mk_pmap lst = + List.fold_left (fun pmap f -> PMap.add f.cf_name f pmap) PMap.empty lst + in + + let meta = c.cl_meta and path = c.cl_path and ext = (has_class_flag c CExtern) in + let sup = c.cl_super and impl = c.cl_implements in + let csr = Option.map (mk_field_restore) c.cl_constructor in + let ofr = List.map (mk_field_restore) c.cl_ordered_fields in + let osr = List.map (mk_field_restore) c.cl_ordered_statics in + let init = c.cl_init in + Option.may save_vars init; + c.cl_restore <- (fun() -> + c.cl_super <- sup; + c.cl_implements <- impl; + c.cl_meta <- meta; + if ext then add_class_flag c CExtern else remove_class_flag c CExtern; + c.cl_path <- path; + c.cl_init <- init; + c.cl_ordered_fields <- List.map restore_field ofr; + c.cl_ordered_statics <- List.map restore_field osr; + c.cl_fields <- mk_pmap c.cl_ordered_fields; + c.cl_statics <- mk_pmap c.cl_ordered_statics; + c.cl_constructor <- Option.map restore_field csr; + c.cl_descendants <- []; + List.iter (fun (v, t) -> v.v_type <- t) !vars; + ) + | TEnumDecl en -> + let path = en.e_path in + en.e_restore <- (fun () -> + let rec loop acc = function + | [] -> + en.e_path <- path; + | (Meta.RealPath,[Ast.EConst (Ast.String(path,_)),_],_) :: l -> + en.e_path <- Ast.parse_path path; + en.e_meta <- (List.rev acc) @ l; + | x :: l -> loop (x::acc) l + in + loop [] en.e_meta + ) + | TTypeDecl td -> + let path = td.t_path in + td.t_restore <- (fun () -> + td.t_path <- path + ); + | TAbstractDecl a -> + let path = a.a_path in + a.a_restore <- (fun () -> + a.a_path <- path; + a.a_meta <- List.filter (fun (m,_,_) -> m <> Meta.ValueUsed) a.a_meta + ) + let run com tctx main = let detail_times = Common.defined com DefineList.FilterTimes in let new_types = List.filter (fun t -> - let cached = is_cached t in + let cached = is_cached com t in begin match t with | TClassDecl cls -> List.iter (fun (iface,_) -> add_descendant iface cls) cls.cl_implements; @@ -720,9 +929,8 @@ let run com tctx main = (* Save cf_expr_unoptimized early: We want to inline with the original expression on the next compilation. *) if not cached then begin - let field cf = match cf.cf_expr with - | Some {eexpr = TFunction tf} -> cf.cf_expr_unoptimized <- Some tf - | _ -> () + let field cf = + cf.cf_expr_unoptimized <- cf.cf_expr in List.iter field cls.cl_ordered_fields; List.iter field cls.cl_ordered_statics; @@ -732,6 +940,15 @@ let run com tctx main = end; not cached ) com.types in + (* IMPORTANT: + There may be types in new_types which have already been post-processed, but then had their m_processed flag unset + because they received an additional dependency. This could happen in cases such as @:generic methods in #10635. + It is important that all filters from here up to save_class_state only process fields which do not have the + CfPostProcessed flag set. + + This is mostly covered by run_expression_filters already, but any new additions which don't utilize that have to + be aware of this. + *) NullSafety.run com new_types; (* PASS 1: general expression filters *) let filters = [ @@ -741,8 +958,9 @@ let run com tctx main = ] in List.iter (run_expression_filters (timer_label detail_times ["expr 0"]) tctx filters) new_types; let filters = [ + "local_statics",LocalStatic.run tctx; "fix_return_dynamic_from_void_function",fix_return_dynamic_from_void_function tctx true; - "check_local_vars_init",check_local_vars_init tctx.com; + "check_local_vars_init",check_local_vars_init tctx; "check_abstract_as_value",check_abstract_as_value; "Tre",if defined com Define.AnalyzerOptimize then Tre.run tctx else (fun e -> e); "reduce_expression",Optimizer.reduce_expression tctx; @@ -780,7 +998,7 @@ let run com tctx main = List.iter (fun f -> List.iter f new_types) filters; t(); com.stage <- CAnalyzerStart; - if com.platform <> Cross then Analyzer.Run.run_on_types tctx new_types; + if com.platform <> Cross then Analyzer.Run.run_on_types com new_types; com.stage <- CAnalyzerDone; let locals = RenameVars.init com in let filters = [ @@ -788,71 +1006,22 @@ let run com tctx main = "add_final_return",if com.config.pf_add_final_return then add_final_return else (fun e -> e); "RenameVars",(match com.platform with | Eval -> (fun e -> e) - | _ -> RenameVars.run tctx locals); + | _ -> (fun e -> RenameVars.run tctx.curclass.cl_path locals e)); "mark_switch_break_loops",mark_switch_break_loops; ] in List.iter (run_expression_filters (timer_label detail_times ["expr 2"]) tctx filters) new_types; - next_compilation(); let t = filter_timer detail_times ["callbacks"] in - List.iter (fun f -> f()) (List.rev com.callbacks#get_before_save); (* macros onGenerate etc. *) + com.callbacks#run com.callbacks#get_before_save; (* macros onGenerate etc. *) t(); com.stage <- CSaveStart; let t = filter_timer detail_times ["save state"] in - List.iter (save_class_state tctx) new_types; + List.iter (fun mt -> + update_cache_dependencies com mt; + save_class_state tctx mt + ) new_types; t(); com.stage <- CSaveDone; let t = filter_timer detail_times ["callbacks"] in - List.iter (fun f -> f()) (List.rev com.callbacks#get_after_save); (* macros onGenerate etc. *) - t(); - let t = filter_timer detail_times ["type 2"] in - (* PASS 2: type filters pre-DCE *) - List.iter (fun t -> - remove_generic_base tctx t; - remove_extern_fields tctx t; - Codegen.update_cache_dependencies t; - (* check @:remove metadata before DCE so it is ignored there (issue #2923) *) - check_remove_metadata tctx t; - ) com.types; - t(); - com.stage <- CDceStart; - let t = filter_timer detail_times ["dce"] in - (* DCE *) - let dce_mode = try Common.defined_value com Define.Dce with _ -> "no" in - let dce_mode = match dce_mode with - | "full" -> if Common.defined com Define.Interp then Dce.DceNo else DceFull - | "std" -> DceStd - | "no" -> DceNo - | _ -> failwith ("Unknown DCE mode " ^ dce_mode) - in - Dce.run com main dce_mode; + com.callbacks#run com.callbacks#get_after_save; (* macros onGenerate etc. *) t(); - com.stage <- CDceDone; - (* PASS 3: type filters post-DCE *) - List.iter - (run_expression_filters - (timer_label detail_times []) - tctx - ["insert_save_stacks",Exceptions.insert_save_stacks tctx] - ) - new_types; - let type_filters = [ - Exceptions.patch_constructors; - check_private_path; - apply_native_paths; - add_rtti; - (match com.platform with | Java | Cs -> (fun _ _ -> ()) | _ -> add_field_inits locals); - (match com.platform with Hl -> (fun _ _ -> ()) | _ -> add_meta_field); - check_void_field; - (match com.platform with | Cpp -> promote_first_interface_to_super | _ -> (fun _ _ -> ()) ); - commit_features; - (if com.config.pf_reserved_type_paths <> [] then check_reserved_type_paths else (fun _ _ -> ())); - ] in - let type_filters = match com.platform with - | Cs -> type_filters @ [ fun _ t -> InterfaceProps.run t ] - | _ -> type_filters - in - let t = filter_timer detail_times ["type 3"] in - List.iter (fun t -> List.iter (fun f -> f tctx t) type_filters) com.types; - t(); - List.iter (fun f -> f()) (List.rev com.callbacks#get_after_filters); - com.stage <- CFilteringDone + destruction tctx detail_times main locals \ No newline at end of file diff --git a/src/filters/filtersCommon.ml b/src/filters/filtersCommon.ml index c29bdb20..0ebe3f58 100644 --- a/src/filters/filtersCommon.ml +++ b/src/filters/filtersCommon.ml @@ -26,7 +26,7 @@ let rec is_removable_class c = (match c.cl_super with | Some (c,_) -> is_removable_class c | _ -> false) || - List.exists (fun (_,t) -> match follow t with + List.exists (fun tp -> match follow tp.ttp_type with | TInst(c,_) -> has_ctor_constraint c || Meta.has Meta.Const c.cl_meta | _ -> @@ -48,7 +48,7 @@ let is_overridden cls field = in List.exists (fun d -> loop_inheritance d) cls.cl_descendants -let run_expression_filters time_details ctx filters t = +let run_expression_filters ?(ignore_processed_status=false) time_details ctx filters t = let run e = List.fold_left (fun e (filter_name,f) -> @@ -66,12 +66,15 @@ let run_expression_filters time_details ctx filters t = | TClassDecl c when is_removable_class c -> () | TClassDecl c -> ctx.curclass <- c; + ctx.m <- TypeloadModule.make_curmod ctx c.cl_module; let rec process_field f = - ctx.curfield <- f; - (match f.cf_expr with - | Some e when not (is_removable_field ctx f) -> - f.cf_expr <- Some (rec_stack_loop AbstractCast.cast_stack f run e); - | _ -> ()); + if ignore_processed_status || not (has_class_field_flag f CfPostProcessed) then begin + ctx.curfield <- f; + (match f.cf_expr with + | Some e when not (is_removable_field ctx.com f) -> + f.cf_expr <- Some (rec_stack_loop AbstractCast.cast_stack f run e); + | _ -> ()); + end; List.iter process_field f.cf_overloads in List.iter process_field c.cl_ordered_fields; diff --git a/src/filters/renameVars.ml b/src/filters/renameVars.ml index db03f085..3296d079 100644 --- a/src/filters/renameVars.ml +++ b/src/filters/renameVars.ml @@ -280,10 +280,14 @@ let trailing_numbers = Str.regexp "[0-9]+$" Rename `v` if needed *) let maybe_rename_var rc reserved (v,overlaps) = + let commit name = + v.v_meta <- (Meta.RealPath,[EConst (String(v.v_name,SDoubleQuotes)),null_pos],null_pos) :: v.v_meta; + v.v_name <- name + in (* chop escape char for all local variables generated *) if is_gen_local v then begin let name = String.sub v.v_name 1 (String.length v.v_name - 1) in - v.v_name <- "_g" ^ (Str.replace_first trailing_numbers "" name) + commit ("_g" ^ (Str.replace_first trailing_numbers "" name)) end; let name = ref v.v_name in let count = ref 0 in @@ -295,7 +299,7 @@ let maybe_rename_var rc reserved (v,overlaps) = incr count; name := v.v_name ^ (string_of_int !count); done; - v.v_name <- !name; + commit !name; if rc.rc_no_shadowing || (has_var_flag v VCaptured && rc.rc_hoisting) then reserve reserved v.v_name (** @@ -311,7 +315,7 @@ let rec rename_vars rc scope = (** Rename local variables in `e` expression if needed. *) -let run ctx ri e = +let run cl_path ri e = (try let rc = { rc_scope = ri.ri_scope; @@ -322,7 +326,7 @@ let run ctx ri e = rc_reserved = ri.ri_reserved; } in if ri.ri_reserve_current_top_level_symbol then begin - match ctx.curclass.cl_path with + match cl_path with | s :: _,_ | [],s -> reserve_ctx rc s end; let scope = create_scope None in diff --git a/src/generators/gencpp.ml b/src/generators/gencpp.ml index 6aa55144..331e6eda 100644 --- a/src/generators/gencpp.ml +++ b/src/generators/gencpp.ml @@ -28,6 +28,8 @@ open Globals *) let follow = Abstract.follow_with_abstracts +let replace_float_separators s = Texpr.replace_separators s "" + (* Code for generating source files. It manages creating diretories, indents, blocks and only modifying files @@ -416,6 +418,20 @@ let join_class_path_remap path separator = | x -> x ;; +let make_path_absolute path pos = + try + if (String.sub path 0 2) = "./" then begin + let base = if (Filename.is_relative pos.pfile) then + Filename.concat (Sys.getcwd()) pos.pfile + else + pos.pfile + in + Path.normalize_path (Filename.concat (Filename.dirname base) (String.sub path 2 ((String.length path) -2))) + end else + path + with Invalid_argument _ -> path +;; + let get_meta_string meta key = let rec loop = function | [] -> "" @@ -425,28 +441,27 @@ let get_meta_string meta key = loop meta ;; - - let get_meta_string_path meta key = let rec loop = function | [] -> "" | (k,[Ast.EConst (Ast.String(name,_)),_], pos) :: _ when k=key-> - (try - if (String.sub name 0 2) = "./" then begin - let base = if (Filename.is_relative pos.pfile) then - Filename.concat (Sys.getcwd()) pos.pfile - else - pos.pfile - in - Path.normalize_path (Filename.concat (Filename.dirname base) (String.sub name 2 ((String.length name) -2) )) - end else - name - with Invalid_argument _ -> name) + make_path_absolute name pos | _ :: l -> loop l in loop meta ;; +let get_all_meta_string_path meta_list key = + let extract_path pos expr = + match expr with + | (Ast.EConst (Ast.String(name, _)), _) -> make_path_absolute name pos + | _ -> "" in + let extract_meta meta = + match meta with + | (k, exprs, pos) when k = key -> Some (extract_path pos (List.hd exprs)) + | _ -> None in + ExtList.List.filter_map extract_meta meta_list +;; let get_meta_string_full_filename meta key = let rec loop = function @@ -695,10 +710,25 @@ let is_extern_class class_def = | _ -> false ); ;; +let is_extern_enum enum_def = + (enum_def.e_extern) || (has_meta_key enum_def.e_meta Meta.Extern) +;; + let is_native_class class_def = ((is_extern_class class_def) || (is_native_gen_class class_def)) && (not (is_internal_class class_def.cl_path)) ;; +let cpp_enum_path_of enum = + (* + let rename = get_meta_string enum.e_meta Meta.Native in + if rename <> "" then + rename + else + *) + let globalNamespace = if (get_meta_string enum.e_meta Meta.Native)<>"" then "" else "::" in + globalNamespace ^ (join_class_path_remap enum.e_path "::") +;; + (* Get a string to represent a type. The "suffix" will be nothing or "_obj", depending if we want the name of the pointer class or the pointee (_obj class *) @@ -753,7 +783,10 @@ let rec class_string klass suffix params remap = (join_class_path_remap klass.cl_path "::") ^ " *" (* Normal class *) | _ when is_native_class klass -> - join_class_path_remap klass.cl_path "::" + let class_params = match params with + | [] -> "" + | _ -> "<" ^ (String.concat "," (List.map type_string params)) ^ ">" in + (join_class_path_remap klass.cl_path "::") ^ class_params | _ -> let globalNamespace = if (get_meta_string klass.cl_meta Meta.Native)<>"" then "" else "::" in globalNamespace ^ (join_class_path_remap klass.cl_path "::") ^ suffix @@ -776,7 +809,7 @@ and type_string_suff suffix haxe_type remap = | TAbstract ({ a_path = [],"Bool" },_) -> "Dynamic" ^ suffix | t when type_has_meta_key t Meta.NotNull -> "Dynamic" ^ suffix | _ -> type_string_suff suffix t remap) - | TEnum (enum,params) -> "::" ^ (join_class_path_remap enum.e_path "::") ^ suffix + | TEnum (enum,_) -> (cpp_enum_path_of enum) ^ suffix | TInst (klass,params) -> (class_string klass suffix params remap) | TType (type_def,params) -> (match type_def.t_path with @@ -804,7 +837,7 @@ and type_string_suff suffix haxe_type remap = | _ -> die "" __LOC__) | ["cpp"] , "Function" -> "::cpp::Function< " ^ (cpp_function_signature_params params ) ^ " >" - | _ -> type_string_suff suffix (apply_params type_def.t_params params type_def.t_type) remap + | _ -> type_string_suff suffix (apply_typedef type_def params) remap ) | TFun (args,haxe_type) -> "Dynamic" ^ suffix | TAnon a -> "Dynamic" @@ -1366,7 +1399,7 @@ type tcpp = | TCppNativePointer of tclass | TCppVariant | TCppCode of tcpp - | TCppInst of tclass + | TCppInst of tclass * tcpp list | TCppInterface of tclass | TCppProtocol of tclass | TCppClass @@ -1634,10 +1667,10 @@ and tcpp_to_string_suffix suffix tcpp = match tcpp with name else "::hx::Native< " ^ name ^ "* >"; - | TCppInst klass -> - (cpp_class_path_of klass) ^ (if is_native_class klass then "" else suffix) + | TCppInst (klass, p) -> + (cpp_class_path_of klass p) ^ (if is_native_class klass then "" else suffix) | TCppInterface klass when suffix="_obj" -> - (cpp_class_path_of klass) ^ suffix + (cpp_class_path_of klass []) ^ suffix | TCppInterface _ -> "::Dynamic" | TCppClass -> "::hx::Class" ^ suffix; | TCppGlobal -> "::Dynamic"; @@ -1663,16 +1696,21 @@ and tcpp_objc_block_struct argTypes retType = and tcpp_to_string tcpp = tcpp_to_string_suffix "" tcpp -and cpp_class_path_of klass = - let globalNamespace = if (get_meta_string klass.cl_meta Meta.Native)<>"" then " " else " ::" in - globalNamespace ^ (join_class_path_remap klass.cl_path "::") +and cpp_class_path_of klass params = + match (get_meta_string klass.cl_meta Meta.Native)<>"" with + | true -> + let typeParams = match params with + | [] -> "" + | _ -> "<" ^ String.concat "," (List.map tcpp_to_string params) ^ ">" in + (" " ^ (join_class_path_remap klass.cl_path "::") ^ typeParams) + | false -> " ::" ^ (join_class_path_remap klass.cl_path "::") ;; let cpp_const_type cval = match cval with | TInt i -> CppInt(i) , TCppScalar("int") | TBool b -> CppBool(b) , TCppScalar("bool") - | TFloat f -> CppFloat(f) , TCppScalar("Float") + | TFloat f -> CppFloat(replace_float_separators f) , TCppScalar("Float") | TString s -> CppString(s) , TCppString | _ -> (* TNull, TThis & TSuper should already be handled *) CppNull, TCppNull @@ -1688,7 +1726,7 @@ let is_cpp_scalar cpp_type = let is_cpp_array_implementer cppType = match cppType with - | TCppInst (klass) + | TCppInst (klass, _) | TCppInterface (klass) -> (match klass.cl_array_access with | Some _ -> true @@ -1707,7 +1745,7 @@ let rec const_int_of expr = let rec const_float_of expr = match expr.eexpr with | TConst TInt x -> Printf.sprintf "%ld" x - | TConst TFloat x -> x + | TConst TFloat x -> (replace_float_separators x) | TConst TBool x -> if x then "1" else "0" | TParenthesis e -> const_float_of e | _ -> raise Not_found @@ -1726,11 +1764,18 @@ let rec cpp_is_struct_access t = match t with | TCppFunction _ -> true | TCppStruct _-> false - | TCppInst (class_def) -> (has_meta_key class_def.cl_meta Meta.StructAccess) + | TCppInst (class_def, _) -> (has_meta_key class_def.cl_meta Meta.StructAccess) | TCppReference (r) -> cpp_is_struct_access r | _ -> false ;; +let rec cpp_is_native_array_access t = + match t with + | TCppStruct s -> cpp_is_native_array_access s + | TCppReference s -> cpp_is_native_array_access s + | TCppInst ({ cl_array_access = Some _ } as klass, _) when is_extern_class klass && has_meta_key klass.cl_meta Meta.NativeArrayAccess -> true + | _ -> false +;; let cpp_is_dynamic_type = function | TCppDynamic | TCppObject | TCppVariant | TCppWrapped _ | TCppGlobal | TCppNull @@ -1773,7 +1818,7 @@ let rec cpp_type_of stack ctx haxe_type = | TType (type_def,params) -> cpp_type_from_path stack ctx type_def.t_path params (fun () -> - cpp_type_of stack ctx (apply_params type_def.t_params params type_def.t_type) ) + cpp_type_of stack ctx (apply_typedef type_def params) ) | TFun _ -> TCppObject | TAnon _ -> TCppObject @@ -1930,9 +1975,11 @@ let rec cpp_type_of stack ctx haxe_type = else if (has_class_flag klass CInterface) then TCppInterface(klass) else if (has_class_flag klass CExtern) && (not (is_internal_class klass.cl_path) ) then - TCppInst(klass) + let tcpp_params = List.map (cpp_type_of stack ctx) params in + TCppInst(klass, tcpp_params) else - TCppInst(klass) + let tcpp_params = List.map (cpp_type_of stack ctx) params in + TCppInst(klass, tcpp_params) ) let cpp_type_of ctx = cpp_type_of [] ctx @@ -1965,18 +2012,6 @@ let is_cpp_objc_type cpptype = match cpptype with ;; -let cpp_enum_path_of enum = - (* - let rename = get_meta_string enum.e_meta Meta.Native in - if rename <> "" then - rename - else - *) - let globalNamespace = if (get_meta_string enum.e_meta Meta.Native)<>"" then "" else "::" in - globalNamespace ^ (join_class_path_remap enum.e_path "::") -;; - - (* let rec cpp_object_name = function @@ -2055,6 +2090,7 @@ let cpp_variant_type_of t = match t with | TCppScalar "Int" | TCppScalar "bool" | TCppScalar "Float" -> t + | TCppScalar "::cpp::Int64" -> TCppScalar("Int64") | TCppScalar "double" | TCppScalar "float" -> TCppScalar("Float") | TCppScalar _ -> TCppScalar("int") @@ -2071,11 +2107,9 @@ let cpp_cast_variant_type_of t = match t with | _ -> cpp_variant_type_of t; ;; -let cpp_base_type_of t = +let enum_getter_type t = match cpp_variant_type_of t with - | TCppDynamic -> "Object" | TCppString -> "String" - | TCppVoidStar -> "Pointer" | TCppScalar "int" -> "Int" | TCppScalar "bool" -> "Bool" | TCppScalar x -> x @@ -2206,8 +2240,8 @@ let cpp_no_debug_synbol ctx var = (ctx.ctx_debug_level<=1) || (match var.v_kind with VUser _ -> false | _ -> true) || match cpp_type_of ctx var.v_type with | TCppStar _ | TCppReference _ -> true - | TCppInst (class_def) when (has_meta_key class_def.cl_meta Meta.StructAccess) -> true - | TCppInst (class_def) when (has_meta_key class_def.cl_meta Meta.Unreflective) -> true + | TCppInst (class_def, _) when (has_meta_key class_def.cl_meta Meta.StructAccess) -> true + | TCppInst (class_def, _) when (has_meta_key class_def.cl_meta Meta.Unreflective) -> true | _-> let name = cpp_var_debug_name_of var in (String.length name) >4 && (String.sub name 0 4) = "_hx_" @@ -2225,7 +2259,7 @@ let cpp_debug_var_visible ctx var = let only_stack_access ctx haxe_type = let tcpp = cpp_type_of ctx haxe_type in match tcpp with - | TCppInst(klass) -> has_meta_key klass.cl_meta Meta.StackOnly + | TCppInst(klass, _) -> has_meta_key klass.cl_meta Meta.StackOnly | _ -> false; ;; @@ -2247,9 +2281,10 @@ let is_array_splice_call obj member = let is_map_get_call obj member = member.cf_name="get" && (match obj.cpptype with - | TCppInst({cl_path=(["haxe";"ds"],"IntMap")}) -> true - | TCppInst({cl_path=(["haxe";"ds"],"StringMap")}) -> true - | TCppInst({cl_path=(["haxe";"ds"],"ObjectMap")}) -> true + | TCppInst({cl_path=(["cpp"],"Int64Map")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"IntMap")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"StringMap")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"ObjectMap")}, _) -> true | _ -> false ) ;; @@ -2257,9 +2292,10 @@ let is_map_get_call obj member = let is_map_set_call obj member = member.cf_name="set" && (match obj.cpptype with - | TCppInst({cl_path=(["haxe";"ds"],"IntMap")}) -> true - | TCppInst({cl_path=(["haxe";"ds"],"StringMap")}) -> true - | TCppInst({cl_path=(["haxe";"ds"],"ObjectMap")}) -> true + | TCppInst({cl_path=(["cpp"],"Int64Map")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"IntMap")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"StringMap")}, _) -> true + | TCppInst({cl_path=(["haxe";"ds"],"ObjectMap")}, _) -> true | _ -> false ) ;; @@ -2278,6 +2314,8 @@ let cpp_can_static_cast funcType inferredType = | TCppReference(_) | TCppStar(_) | TCppStruct(_) -> false | _ -> (match inferredType with + | TCppInst (cls, _) when is_extern_class cls -> false + | TCppEnum e when is_extern_enum e -> false | TCppInst _ | TCppClass | TCppEnum _ @@ -2334,7 +2372,7 @@ let cpp_enum_name_of field = let is_object_element ctx member_type = match member_type with - | TCppInst x + | TCppInst (x, _) | TCppInterface x -> not (is_extern_class x) | TCppDynamic @@ -2741,6 +2779,7 @@ let retype_expression ctx request_type function_args function_type expression_tr let fname, cppType = match return_type with | TCppVoid | TCppScalar("bool") -> (if forCppia then "getBool" else "get_bool"), return_type | TCppScalar("int") -> (if forCppia then "getInt" else "get_int"), return_type + | TCppScalar("::cpp::Int64") -> (if forCppia then "getInt64" else "get_int64"), return_type | TCppScalar("Float") -> (if forCppia then "getFloat" else "get_float"), return_type | TCppString -> (if forCppia then "getString" else "get_string"), return_type | _ -> "get", TCppDynamic @@ -2760,6 +2799,7 @@ let retype_expression ctx request_type function_args function_type expression_tr let fname = match retypedArgs with | [_;{cpptype=TCppScalar("bool")}] -> "setBool" | [_;{cpptype=TCppScalar("int")}] -> "setInt" + | [_;{cpptype=TCppScalar("::cpp::Int64")}] -> "setInt64" | [_;{cpptype=TCppScalar("Float")}] -> "setFloat" | [_;{cpptype=TCppString}] -> "setString" | _ -> "set" @@ -2780,6 +2820,17 @@ let retype_expression ctx request_type function_args function_type expression_tr *) (* Other functions ... *) + | CppFunction( FuncInstance(_, InstStruct, {cf_type=TFun(arg_types,_)}) as func, return_type) -> + (* For struct access classes use the types of the arguments instead of the function argument types *) + (* In the case of generic extern classes a TFun arg type could be `MyClass.T` instead of the real type *) + let map_args func_arg passed_arg = + let (name, opt, _) = func_arg in + name, opt, passed_arg.etype in + let real_types = List.map2 map_args arg_types args in + let arg_types = List.map (fun (_,opt,t) -> cpp_tfun_arg_type_of ctx opt t) real_types in + let retypedArgs = retype_function_args args arg_types in + CppCall(func,retypedArgs), return_type + | CppFunction( FuncInstance(_,_,{cf_type=TFun(arg_types,_)} ) as func, returnType ) | CppFunction( FuncStatic(_,_,{cf_type=TFun(arg_types,_)} ) as func, returnType ) | CppFunction( FuncThis({cf_type=TFun(arg_types,_)},_ ) as func, returnType ) -> @@ -2841,7 +2892,7 @@ let retype_expression ctx request_type function_args function_type expression_tr let arg_types, _ = cpp_function_type_of_args_ret ctx constructor_type in let retypedArgs = retype_function_args args arg_types in let created_type = cpp_type_of expr.etype in - gc_stack := !gc_stack || (match created_type with | TCppInst(t) -> not (is_native_class t) | _ -> false ); + gc_stack := !gc_stack || (match created_type with | TCppInst(t, _) -> not (is_native_class t) | _ -> false ); CppCall( FuncNew(created_type), retypedArgs), created_type | TFunction func -> @@ -2883,26 +2934,32 @@ let retype_expression ctx request_type function_args function_type expression_tr CppClosure(result), TCppDynamic | TArray (e1,e2) -> - let retypedObj = retype TCppDynamic e1 in - let retypedIdx = retype (TCppScalar("int")) e2 in - let arrayExpr, elemType = (match retypedObj.cpptype with - | TCppScalarArray scalar -> - CppArray( ArrayTyped(retypedObj,retypedIdx,scalar) ), scalar - | TCppPointer (_,elem) -> - CppArray( ArrayPointer(retypedObj, retypedIdx) ), elem - | TCppRawPointer (_,elem) -> - CppArray( ArrayRawPointer(retypedObj, retypedIdx) ), elem - | TCppObjectArray TCppDynamic -> - CppArray( ArrayObject(retypedObj,retypedIdx,TCppDynamic) ), TCppDynamic - | TCppObjectArray elem -> - CppArray( ArrayObject(retypedObj,retypedIdx,elem) ), elem - | TCppInst({cl_array_access = Some _ } as klass) -> - CppArray( ArrayImplements(klass, retypedObj,retypedIdx) ), cpp_type_of expr.etype - | TCppDynamicArray -> - CppArray( ArrayVirtual(retypedObj, retypedIdx) ), TCppDynamic - | _ -> - CppArray( ArrayDynamic(retypedObj, retypedIdx) ), TCppDynamic - ) in + let arrayExpr, elemType = match cpp_is_native_array_access (cpp_type_of e1.etype) with + | true -> + let retypedObj = retype TCppUnchanged e1 in + let retypedIdx = retype (TCppScalar("int")) e2 in + CppArray( ArrayRawPointer(retypedObj, retypedIdx) ), cpp_type_of expr.etype + | false -> + let retypedObj = retype TCppDynamic e1 in + let retypedIdx = retype (TCppScalar("int")) e2 in + (match retypedObj.cpptype with + | TCppScalarArray scalar -> + CppArray( ArrayTyped(retypedObj,retypedIdx,scalar) ), scalar + | TCppPointer (_,elem) -> + CppArray( ArrayPointer(retypedObj, retypedIdx) ), elem + | TCppRawPointer (_,elem) -> + CppArray( ArrayRawPointer(retypedObj, retypedIdx) ), elem + | TCppObjectArray TCppDynamic -> + CppArray( ArrayObject(retypedObj,retypedIdx,TCppDynamic) ), TCppDynamic + | TCppObjectArray elem -> + CppArray( ArrayObject(retypedObj,retypedIdx,elem) ), elem + | TCppInst({cl_array_access = Some _ } as klass, _) -> + CppArray( ArrayImplements(klass, retypedObj,retypedIdx) ), cpp_type_of expr.etype + | TCppDynamicArray -> + CppArray( ArrayVirtual(retypedObj, retypedIdx) ), TCppDynamic + | _ -> + CppArray( ArrayDynamic(retypedObj, retypedIdx) ), TCppDynamic) + in let returnType = cpp_type_of expr.etype in if cpp_can_static_cast elemType returnType then CppCastStatic(mk_cppexpr arrayExpr returnType, returnType), returnType @@ -2910,7 +2967,12 @@ let retype_expression ctx request_type function_args function_type expression_tr arrayExpr, elemType | TTypeExpr module_type -> - let path = t_path module_type in + (* If we try and use the coreType / runtimeValue cpp.Int64 abstract with Class then we get a class decl of the abstract *) + (* as that abstract has functions in its declaration *) + (* Intercept it and replace it with the path of the actual int64 type so the generated cpp is correct *) + let path = match module_type with + | TClassDecl ({ cl_path = ["cpp";"_Int64"],"Int64_Impl_" }) -> ["cpp"],"Int64" + | _ -> t_path module_type in CppClassOf(path, is_native_gen_module module_type), TCppClass | TBinop (op,left,right) -> @@ -3175,10 +3237,10 @@ let retype_expression ctx request_type function_args function_type expression_tr end else if (cppExpr.cpptype=TCppVariant || cppExpr.cpptype=TCppDynamic || cppExpr.cpptype==TCppObject) then begin match return_type with | TCppUnchanged -> cppExpr - | TCppInst(t) when (has_meta_key t.cl_meta Meta.StructAccess) -> - let structType = TCppStruct( TCppInst(t) ) in + | TCppInst(t, _) when (has_meta_key t.cl_meta Meta.StructAccess) -> + let structType = TCppStruct( TCppInst(t, []) ) in let structCast = mk_cppexpr (CppCast(cppExpr,structType)) structType in - mk_cppexpr (CppCast(structCast,(TCppInst t))) (TCppInst t) + mk_cppexpr (CppCast(structCast,(TCppInst (t, [])))) (TCppInst (t, [])) | TCppObjectArray _ | TCppScalarArray _ @@ -3266,7 +3328,7 @@ let retype_expression ctx request_type function_args function_type expression_tr | TCppStar(t,const), TCppStruct _ -> mk_cppexpr (CppDereference(cppExpr)) return_type - | TCppInst(t), TCppStar _ when (is_native_class t) && (match cppExpr.cppexpr with + | TCppInst(t, _), TCppStar _ when (is_native_class t) && (match cppExpr.cppexpr with | CppCall(FuncNew(_), _) -> true | _ -> false) -> mk_cppexpr (CppNewNative(cppExpr)) return_type @@ -3283,8 +3345,8 @@ let retype_expression ctx request_type function_args function_type expression_tr | t, TCppProtocol protocol -> mk_cppexpr (CppCastProtocol(cppExpr,protocol)) return_type - | TCppInst(t), TCppDynamic when (has_meta_key t.cl_meta Meta.StructAccess) -> - let structType = TCppStruct( TCppInst(t) ) in + | TCppInst(t, _), TCppDynamic when (has_meta_key t.cl_meta Meta.StructAccess) -> + let structType = TCppStruct( TCppInst(t, []) ) in let structCast = mk_cppexpr (CppCast(cppExpr,structType)) structType in mk_cppexpr (CppCast(structCast,TCppDynamic)) TCppDynamic @@ -3333,7 +3395,7 @@ let string_of_path path = let default_value_string ctx value = match value.eexpr with | TConst (TInt i) -> Printf.sprintf "%ld" i - | TConst (TFloat float_as_string) -> "((Float)" ^ float_as_string ^ ")" + | TConst (TFloat float_as_string) -> "((Float)" ^ (replace_float_separators float_as_string) ^ ")" | TConst (TString s) -> strq ctx s | TConst (TBool b) -> (if b then "true" else "false") | TConst TNull -> "null()" @@ -3580,8 +3642,8 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args function_ expr.cpppos); out " ]" - | CppCall(FuncNew( TCppInst klass), args) when can_quick_alloc klass -> - out ((cpp_class_path_of klass) ^ "_obj::__alloc( HX_CTX "); + | CppCall(FuncNew( TCppInst (klass, p)), args) when can_quick_alloc klass -> + out ((cpp_class_path_of klass p) ^ "_obj::__alloc( HX_CTX "); List.iter (fun arg -> out ","; gen arg ) args; out (")") @@ -3635,14 +3697,14 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args function_ | FuncEnumConstruct(enum,field) -> out ((string_of_path enum.e_path) ^ "::" ^ (cpp_enum_name_of field)); - | FuncSuperConstruct(TCppInst klass) when is_native_class klass -> + | FuncSuperConstruct(TCppInst (klass, _)) when is_native_class klass -> doCall := false; | FuncSuperConstruct _ -> out ((if not ctx.ctx_real_this_ptr then "__this->" else "") ^ "super::__construct") - | FuncSuper(_,TCppInst(klass),field) when is_native_class klass -> - out ((cpp_class_path_of klass) ^ "::" ^ (cpp_member_name_of field)); + | FuncSuper(_,TCppInst(klass, p),field) when is_native_class klass -> + out ((cpp_class_path_of klass p) ^ "::" ^ (cpp_member_name_of field)); | FuncSuper(this,_,field) -> out ( (if this==ThisReal then "this->" else "__->") ^ "super::" ^ (cpp_member_name_of field) ) @@ -3653,10 +3715,10 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args function_ | TCppDynamicArray -> "::cpp::VirtualArray_obj::__new" | TCppObjectArray _ -> "::Array_obj< ::Dynamic>::__new" | TCppScalarArray(value) -> "::Array_obj< " ^ (tcpp_to_string value) ^ " >::__new" - | TCppObjC klass -> (cpp_class_path_of klass) ^ "_obj::__new" - | TCppNativePointer klass -> "new " ^ (cpp_class_path_of klass); - | TCppInst klass when is_native_class klass -> cpp_class_path_of klass - | TCppInst klass -> (cpp_class_path_of klass) ^ "_obj::__new" + | TCppObjC klass -> (cpp_class_path_of klass []) ^ "_obj::__new" + | TCppNativePointer klass -> "new " ^ (cpp_class_path_of klass []); + | TCppInst (klass, p) when is_native_class klass -> cpp_class_path_of klass p + | TCppInst (klass, p) -> (cpp_class_path_of klass p) ^ "_obj::__new" | TCppClass -> "::hx::Class_obj::__new"; | TCppFunction _ -> tcpp_to_string newType | _ -> abort ("Unknown 'new' target " ^ (tcpp_to_string newType)) expr.cpppos @@ -3920,7 +3982,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args function_ | CppEnumParameter(obj,field,index) -> let valueType = cpp_type_of ctx (get_nth_type field index) in - let baseType = cpp_base_type_of valueType in + let baseType = enum_getter_type valueType in gen obj; if cpp_is_dynamic_type obj.cpptype then out ".StaticCast< ::hx::EnumBase >()"; @@ -4218,6 +4280,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args function_ | OpInterval -> "..." | OpArrow -> "->" | OpIn -> " in " + | OpNullCoal -> "??" | OpAssign | OpAssignOp _ -> abort "Unprocessed OpAssign" pos and gen_closure closure = @@ -4457,7 +4520,7 @@ let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface let needsWrapper t = match t with | TCppStar _ -> true - | TCppInst(t) -> has_meta_key t.cl_meta Meta.StructAccess + | TCppInst(t, _) -> has_meta_key t.cl_meta Meta.StructAccess | _ -> false in let orig_debug = ctx.ctx_debug_level in @@ -4503,7 +4566,7 @@ let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface match return_type with | TCppStar _ -> output "return (cpp::Pointer) " - | TCppInst(t) when has_meta_key t.cl_meta Meta.StructAccess -> + | TCppInst(t, _) when has_meta_key t.cl_meta Meta.StructAccess -> output ("return (cpp::Struct< " ^ (tcpp_to_string return_type) ^ " >) "); | _ -> output "return "; end; @@ -4519,7 +4582,7 @@ let gen_field ctx class_def class_name ptr_name dot_name is_static is_interface (match arg with | TCppStar (t,const) -> output ("(cpp::" ^ (if const then "Const" else "") ^"Pointer<" ^ (tcpp_to_string t)^" >) ") - | TCppInst(t) when has_meta_key t.cl_meta Meta.StructAccess -> + | TCppInst(t, _) when has_meta_key t.cl_meta Meta.StructAccess -> output ("(cpp::Struct< " ^ (tcpp_to_string arg) ^ " >) "); | _ -> () ); output ("a" ^ (string_of_int i)); @@ -4713,7 +4776,7 @@ let gen_member_def ctx class_def is_static is_interface field = output (if return_type="Void" then "void" else return_type ); output (" " ^ remap_name ^ "(" ); output (ctx_arg_list ctx tl "" ); - output ") = 0;\n"; + output (") " ^ (if return_type="void" then "{}" else "{ return 0; }" ) ^ "\n"); if doDynamic then output (" ::Dynamic " ^ remap_name ^ "_dyn();\n" ); | _ when has_decl -> @@ -4766,9 +4829,9 @@ let path_of_string path = let find_referenced_types_flags ctx obj field_name super_deps constructor_deps header_only for_depends include_super_args = let types = ref PMap.empty in if for_depends then begin - let include_file = get_meta_string_path (t_infos obj).mt_meta Meta.Depend in - if (include_file<>"") then - types := (PMap.add ( path_of_string include_file ) true !types); + let include_files = get_all_meta_string_path (t_infos obj).mt_meta Meta.Depend in + let include_adder = fun inc -> types := (PMap.add ( path_of_string inc ) true !types) in + List.iter include_adder include_files; end; let rec add_type_flag isNative in_path = if ( not (PMap.mem in_path !types)) then begin @@ -4782,9 +4845,9 @@ let find_referenced_types_flags ctx obj field_name super_deps constructor_deps h in let add_extern_type decl = let tinfo = t_infos decl in - let include_file = get_meta_string_path tinfo.mt_meta (if for_depends then Meta.Depend else Meta.Include) in - if (include_file<>"") then - add_type ( path_of_string include_file ) + let include_files = get_all_meta_string_path tinfo.mt_meta (if for_depends then Meta.Depend else Meta.Include) in + if List.length include_files > 0 then + List.iter (fun inc -> add_type(path_of_string inc)) include_files else if (not for_depends) && (has_meta_key tinfo.mt_meta Meta.Include) then add_type tinfo.mt_path in @@ -4792,10 +4855,13 @@ let find_referenced_types_flags ctx obj field_name super_deps constructor_deps h let add_extern_class klass = add_extern_type (TClassDecl klass) in + let add_extern_enum enum = + add_extern_type (TEnumDecl enum) + in let add_native_gen_class klass = - let include_file = get_meta_string_path klass.cl_meta (if for_depends then Meta.Depend else Meta.Include) in - if (include_file<>"") then - add_type ( path_of_string include_file ) + let include_files = get_all_meta_string_path klass.cl_meta (if for_depends then Meta.Depend else Meta.Include) in + if List.length include_files > 0 then + List.iter (fun inc -> add_type ( path_of_string inc )) include_files else if for_depends then add_type klass.cl_path else begin @@ -4814,7 +4880,10 @@ let find_referenced_types_flags ctx obj field_name super_deps constructor_deps h visited := in_type :: !visited; begin match follow in_type with | TMono r -> (match r.tm_type with None -> () | Some t -> visit_type t) - | TEnum (enum,params) -> add_type enum.e_path + | TEnum (enum,_) -> + (match is_extern_enum enum with + | true -> add_extern_enum enum + | false -> add_type enum.e_path) (* If a class has a template parameter, then we treat it as dynamic - except for the Array, Class, FastIterator or Pointer classes, for which we do a fully typed object *) | TInst (klass,params) -> @@ -4823,7 +4892,9 @@ let find_referenced_types_flags ctx obj field_name super_deps constructor_deps h | (["cpp"],"Pointer") | (["cpp"],"ConstPointer") | (["cpp"],"Function") | (["cpp"],"RawPointer") | (["cpp"],"RawConstPointer") -> List.iter visit_type params | _ when is_native_gen_class klass -> add_native_gen_class klass - | _ when is_extern_class klass -> add_extern_class klass + | _ when is_extern_class klass -> + add_extern_class klass; + List.iter visit_type params; | _ -> (match klass.cl_kind with KTypeParameter _ -> () | _ -> add_type klass.cl_path); ) | TAbstract (a,params) when is_scalar_abstract a -> @@ -4843,6 +4914,7 @@ let find_referenced_types_flags ctx obj field_name super_deps constructor_deps h | TTypeExpr type_def -> ( match type_def with | TClassDecl class_def when is_native_gen_class class_def -> add_native_gen_class class_def | TClassDecl class_def when is_extern_class class_def -> add_extern_class class_def + | TEnumDecl enum_def when is_extern_enum enum_def -> add_extern_enum enum_def | _ -> add_type (t_path type_def) ) @@ -5830,7 +5902,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta in (match find_super_args [function_def.tf_expr.eexpr] with | Some args -> - out ("\n:" ^ (cpp_class_path_of klass) ^ "("); + out ("\n:" ^ (cpp_class_path_of klass []) ^ "("); let sep = ref "" in List.iter (fun arg -> out !sep; sep := ","; @@ -5884,9 +5956,9 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta cpp_file#write_h "\n"; output_cpp ( get_class_code class_def Meta.CppFileCode ); - let inc = get_meta_string_path class_def.cl_meta Meta.CppInclude in - if (inc<>"") then - output_cpp ("#include \"" ^ inc ^ "\"\n"); + let includes = get_all_meta_string_path class_def.cl_meta Meta.CppInclude in + let printer = fun inc -> output_cpp ("#include \"" ^ inc ^ "\"\n") in + List.iter printer includes; gen_open_namespace output_cpp class_path; output_cpp "\n"; @@ -6138,7 +6210,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta let toCommon t f value = t ^ "( " ^ ( match cpp_type_of ctx f.cf_type with - | TCppInst(t) as inst when (has_meta_key t.cl_meta Meta.StructAccess) + | TCppInst(t, _) as inst when (has_meta_key t.cl_meta Meta.StructAccess) -> "cpp::Struct< " ^ (tcpp_to_string inst) ^ " >( " ^ value ^ " )" | TCppStar(t,_) -> "cpp::Pointer( " ^ value ^ " )" | _ -> value @@ -6186,7 +6258,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta let castable f = match cpp_type_of ctx f.cf_type with - | TCppInst(t) as inst when (has_meta_key t.cl_meta Meta.StructAccess) + | TCppInst(t, _) as inst when (has_meta_key t.cl_meta Meta.StructAccess) -> "cpp::Struct< " ^ (tcpp_to_string inst) ^ " > " | TCppStar(t,_) -> "cpp::Pointer< " ^ ( tcpp_to_string t ) ^ " >" | _ -> ctx_type_string ctx f.cf_type @@ -6503,7 +6575,7 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta let class_name_text = join_class_path class_path "." in (* Initialise static in boot function ... *) - if (not (has_class_flag class_def CInterface) && not nativeGen) && not (has_class_flag class_def CAbstract) then begin + if (not (has_class_flag class_def CInterface) && not nativeGen) then begin (* Remap the specialised "extern" classes back to the generic names *) output_cpp ("::hx::Class " ^ class_name ^ "::__mClass;\n\n"); if (scriptable) then begin @@ -6527,13 +6599,17 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta in output_cpp ("void " ^ class_name ^ "::__register()\n{\n"); - output_cpp ("\t" ^ class_name ^ " _hx_dummy;\n"); - output_cpp ("\t" ^ class_name ^ "::_hx_vtable = *(void **)&_hx_dummy;\n"); + if (not (has_class_flag class_def CAbstract)) then begin + output_cpp ("\t" ^ class_name ^ " _hx_dummy;\n"); + output_cpp ("\t" ^ class_name ^ "::_hx_vtable = *(void **)&_hx_dummy;\n"); + end; output_cpp ("\t::hx::Static(__mClass) = new ::hx::Class_obj();\n"); output_cpp ("\t__mClass->mName = " ^ (strq class_name_text) ^ ";\n"); output_cpp ("\t__mClass->mSuper = &super::__SGetClass();\n"); - output_cpp ("\t__mClass->mConstructEmpty = &__CreateEmpty;\n"); - output_cpp ("\t__mClass->mConstructArgs = &__Create;\n"); + if (not (has_class_flag class_def CAbstract)) then begin + output_cpp ("\t__mClass->mConstructEmpty = &__CreateEmpty;\n"); + output_cpp ("\t__mClass->mConstructArgs = &__Create;\n"); + end; output_cpp ("\t__mClass->mGetStaticField = &" ^ ( if (has_get_static_field class_def) then class_name ^ "::__GetStatic;\n" else "::hx::Class_obj::GetNoStaticField;\n" )); output_cpp ("\t__mClass->mSetStaticField = &" ^ ( @@ -6635,15 +6711,21 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta (match class_def.cl_super with | Some super -> let klass = fst super in - let include_file = get_meta_string_path klass.cl_meta Meta.Include in - h_file#add_include (if include_file="" then klass.cl_path else path_of_string include_file) + let include_files = get_all_meta_string_path klass.cl_meta Meta.Include in + if List.length include_files > 0 then + List.iter (fun inc -> h_file#add_include (path_of_string inc)) include_files + else + h_file#add_include klass.cl_path | _ -> () ); (* And any interfaces ... *) List.iter (fun imp-> let interface = fst imp in - let include_file = get_meta_string_path interface.cl_meta Meta.Include in - h_file#add_include (if include_file="" then interface.cl_path else path_of_string include_file) ) + let include_files = get_all_meta_string_path interface.cl_meta Meta.Include in + if List.length include_files > 0 then + List.iter (fun inc -> h_file#add_include (path_of_string inc)) include_files + else + h_file#add_include interface.cl_path) (real_interfaces class_def.cl_implements); (* Only need to forward-declare classes that are mentioned in the header file @@ -6653,9 +6735,9 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta output_h "\n"; output_h ( get_class_code class_def Meta.HeaderCode ); - let inc = get_meta_string_path class_def.cl_meta Meta.HeaderInclude in - if (inc<>"") then - output_h ("#include \"" ^ inc ^ "\"\n"); + let includes = get_all_meta_string_path class_def.cl_meta Meta.HeaderInclude in + let printer = fun inc -> output_h ("#include \"" ^ inc ^ "\"\n") in + List.iter printer includes; gen_open_namespace output_h class_path; output_h "\n\n"; @@ -6710,9 +6792,11 @@ let generate_class_files baseCtx super_deps constructor_deps class_def inScripta output_h ("\t\tstatic " ^ptr_name^ " __alloc(::hx::Ctx *_hx_ctx" ^ (if constructor_type_args="" then "" else "," ^constructor_type_args) ^");\n"); end; - output_h ("\t\tstatic void * _hx_vtable;\n"); - output_h ("\t\tstatic Dynamic __CreateEmpty();\n"); - output_h ("\t\tstatic Dynamic __Create(::hx::DynamicArray inArgs);\n"); + if (not (has_class_flag class_def CAbstract)) then begin + output_h ("\t\tstatic void * _hx_vtable;\n"); + output_h ("\t\tstatic Dynamic __CreateEmpty();\n"); + output_h ("\t\tstatic Dynamic __Create(::hx::DynamicArray inArgs);\n"); + end; if (List.length dynamic_functions > 0) then output_h ("\t\tstatic void __alloc_dynamic_functions(::hx::Ctx *_hx_alloc," ^ class_name ^ " *_hx_obj);\n"); if (scriptable) then @@ -6934,18 +7018,20 @@ let write_build_data common_ctx filename classes main_deps boot_deps build_extra output_string buildfile "\n"; output_string buildfile build_extra; if (Common.defined common_ctx Define.HxcppSmartStings) then - output_string buildfile ("\n"); + output_string buildfile ("\n"); output_string buildfile "\n"; close_out buildfile;; let write_build_options common_ctx filename defines = let writer = cached_source_writer common_ctx filename in + let write_define name value = writer#write (Printf.sprintf "%s=%s\n" name value) in PMap.iter ( fun name value -> match name with | "true" | "sys" | "dce" | "cpp" | "debug" -> () - | _ -> writer#write (name ^ "="^(escape_command value)^ "\n" ) ) defines; - let cmd = Unix.open_process_in "haxelib path hxcpp" in - writer#write ("hxcpp=" ^ (Pervasives.input_line cmd)); - Pervasives.ignore (Unix.close_process_in cmd); + | _ -> write_define name (escape_command value)) defines; + let pin,pid = Process_helper.open_process_args_in_pid "haxelib" [|"haxelib"; "path"; "hxcpp"|] in + set_binary_mode_in pin false; + write_define "hxcpp" (Pervasives.input_line pin); + Pervasives.ignore (Process_helper.close_process_in_pid (pin,pid)); writer#close;; let create_member_types common_ctx = @@ -7086,7 +7172,7 @@ let rec script_cpptype_string cppType = match cppType with | TCppProtocol _ -> "cpp.ObjC.Protocol" | TCppNativePointer klass -> "cpp.Pointer." ^ (join_class_path klass.cl_path ".") | TCppInterface klass -> (join_class_path klass.cl_path ".") - | TCppInst klass -> (join_class_path klass.cl_path ".") + | TCppInst (klass, _) -> (join_class_path klass.cl_path ".") | TCppClass -> "Class" | TCppGlobal -> "?global"; | TCppNull -> "null"; @@ -7295,6 +7381,7 @@ let cppia_op_info = function | IaBinOp OpInterval -> ("...", 121) | IaBinOp OpArrow -> ("=>", 122) | IaBinOp OpIn -> (" in ", 123) + | IaBinOp OpNullCoal -> ("??", 124) | IaBinOp OpAssignOp OpAdd -> ("+=", 201) | IaBinOp OpAssignOp OpMult -> ("*=", 202) | IaBinOp OpAssignOp OpDiv -> ("/=", 203) @@ -7312,6 +7399,7 @@ let cppia_op_info = function | IaBinOp OpAssignOp OpMod -> ("%=", 220) | IaBinOp OpAssignOp OpIn + | IaBinOp OpAssignOp OpNullCoal | IaBinOp OpAssignOp OpInterval | IaBinOp OpAssignOp OpAssign | IaBinOp OpAssignOp OpEq @@ -7433,7 +7521,7 @@ class script_writer ctx filename asciiOut = end method constText c = match c with | TInt i -> (this#op IaConstInt) ^ (Printf.sprintf "%ld " i) - | TFloat f -> (this#op IaConstFloat) ^ (this#stringText f) + | TFloat f -> (this#op IaConstFloat) ^ (this#stringText (replace_float_separators f)) | TString s -> (this#op IaConstString) ^ (this#stringText s) | TBool true -> (this#op IaConstTrue) | TBool false -> (this#op IaConstFalse) @@ -7982,11 +8070,11 @@ class script_writer ctx filename asciiOut = | FuncEnumConstruct(enum,field) -> this#write ((this#op IaCreateEnum) ^ (this#enumText enum) ^ " " ^ (this#stringText field.ef_name) ^ argN ^ (this#commentOf field.ef_name) ^ "\n"); - | FuncSuperConstruct(TCppInst klass) when (is_native_gen_class klass) && (is_native_class klass) -> + | FuncSuperConstruct(TCppInst (klass, _)) when (is_native_gen_class klass) && (is_native_class klass) -> abort "Unsupported super for native class constructor" expression.cpppos; | FuncSuperConstruct childType -> this#write ((this#op IaCallSuperNew) ^ (this#astType childType) ^ " " ^ argN ^ "\n"); - | FuncSuper(_,TCppInst(klass),_) when (is_native_gen_class klass) && (is_native_class klass) -> + | FuncSuper(_,TCppInst(klass, _),_) when (is_native_gen_class klass) && (is_native_class klass) -> abort "Unsupported super for native class method" expression.cpppos; | FuncSuper(_,objType,field) -> this#write ( (this#op IaCallSuper) ^ (this#astType objType) ^ " " ^ (this#stringText field.cf_name) ^ @@ -8354,6 +8442,7 @@ let generate_script_enum common_ctx script enum_def meta = let generate_cppia ctx = let common_ctx = ctx.ctx_common in let debug = ctx.ctx_debug_level in + Path.mkdir_from_path common_ctx.file; let script = new script_writer ctx common_ctx.file common_ctx.debug in ignore (script#stringId ""); ignore (script#typeId ""); @@ -8553,21 +8642,20 @@ let generate_source ctx = | _ -> "output" in write_build_data common_ctx (common_ctx.file ^ "/Build.xml") !exe_classes !main_deps (!boot_enums@ !boot_classes) !build_xml !extern_src output_name; - let cmd_defines = ref "" in - PMap.iter ( fun name value -> match name with - | "true" | "sys" | "dce" | "cpp" | "debug" -> () - | _ -> cmd_defines := !cmd_defines ^ " -D" ^ name ^ "=\"" ^ (escape_command value) ^ "\"" ) common_ctx.defines.Define.values; write_build_options common_ctx (common_ctx.file ^ "/Options.txt") common_ctx.defines.Define.values; if ( not (Common.defined common_ctx Define.NoCompilation) ) then begin let t = Timer.timer ["generate";"cpp";"native compilation"] in let old_dir = Sys.getcwd() in Sys.chdir common_ctx.file; - let cmd = ref "haxelib run hxcpp Build.xml haxe" in - if (common_ctx.debug) then cmd := !cmd ^ " -Ddebug"; - cmd := !cmd ^ !cmd_defines; - cmd := List.fold_left (fun cmd path -> cmd ^ " -I\"" ^ (escape_command path) ^ "\"" ) !cmd common_ctx.class_path; - common_ctx.print (!cmd ^ "\n"); - if common_ctx.run_command !cmd <> 0 then failwith "Build failed"; + let cmd = ref ["run"; "hxcpp"; "Build.xml"; "haxe"] in + if (common_ctx.debug) then cmd := !cmd @ ["-Ddebug"]; + PMap.iter ( fun name value -> match name with + | "true" | "sys" | "dce" | "cpp" | "debug" -> (); + | _ -> cmd := !cmd @ [Printf.sprintf "-D%s=\"%s\"" name (escape_command value)]; + ) common_ctx.defines.values; + List.iter (fun path -> cmd := !cmd @ [Printf.sprintf "-I%s" (escape_command path)]) common_ctx.class_path; + common_ctx.print ("haxelib " ^ (String.concat " " !cmd) ^ "\n"); + if common_ctx.run_command_args "haxelib" !cmd <> 0 then failwith "Build failed"; Sys.chdir old_dir; t() end @@ -8583,5 +8671,3 @@ let generate common_ctx = generate_source ctx end ;; - - diff --git a/src/generators/gencs.ml b/src/generators/gencs.ml index bfbcb578..563db242 100644 --- a/src/generators/gencs.ml +++ b/src/generators/gencs.ml @@ -110,7 +110,7 @@ let rec is_null t = match t with | TInst( { cl_path = (["haxe"; "lang"], "Null") }, _ ) | TAbstract( { a_path = ([], "Null") }, _ ) -> true - | TType( t, tl ) -> is_null (apply_params t.t_params tl t.t_type) + | TType( t, tl ) -> is_null (apply_typedef t tl) | TMono r -> (match r.tm_type with | Some t -> is_null t @@ -158,7 +158,7 @@ let get_overloads_for_optional_args gen cl cf is_static = | [],Method (MethNormal | MethDynamic | MethInline) -> (match cf.cf_expr, follow cf.cf_type with | Some ({ eexpr = TFunction fn } as method_expr), TFun (args, return_type) -> - let type_params = List.map snd cl.cl_params in + let type_params = extract_param_types cl.cl_params in let rec collect_overloads tf_args_rev args_rev default_values_rev = match tf_args_rev, args_rev with | (_, Some default_value) :: rest_tf_args_rev, _ :: rest_args_rev -> @@ -1369,6 +1369,7 @@ let generate con = | _ -> () *) | TFloat s -> + let s = Texpr.replace_separators s "" in let len = String.length s in let rec loop i prev_c = if i >= len then begin @@ -1466,7 +1467,7 @@ let generate con = ) | TParenthesis e -> write w "("; expr_s w e; write w ")" - | TMeta ((Meta.LoopLabel,[(EConst(Int n),_)],_), e) -> + | TMeta ((Meta.LoopLabel,[(EConst(Int (n, _)),_)],_), e) -> (match e.eexpr with | TFor _ | TWhile _ -> expr_s w e; @@ -1889,7 +1890,7 @@ let generate con = let rec gen_fpart_attrib w = function | EConst( Ident i ), _ -> write w i - | EField( ef, f ), _ -> + | EField( ef, f, _ ), _ -> gen_fpart_attrib w ef; write w "."; write w f @@ -1899,14 +1900,14 @@ let generate con = let rec gen_spart w = function | EConst c, p -> (match c with - | Int s | Float s | Ident s -> + | Int (s, _) | Float (s, _) | Ident s -> write w s | String(s,_) -> write w "\""; write w (escape s); write w "\"" | _ -> gen.gcon.error "Invalid expression inside @:meta metadata" p) - | EField( ef, f ), _ -> + | EField( ef, f, _ ), _ -> gen_spart w ef; write w "."; write w f @@ -2013,12 +2014,12 @@ let generate con = let combination_error c1 c2 = gen.gcon.error ("The " ^ (get_constraint c1) ^ " constraint cannot be combined with the " ^ (get_constraint c2) ^ " constraint.") cl.cl_pos in - let params = sprintf "<%s>" (String.concat ", " (List.map (fun (_, tcl) -> get_param_name tcl) cl_params)) in + let params = sprintf "<%s>" (String.concat ", " (List.map (fun tp -> get_param_name tp.ttp_type) cl_params)) in let params_extends = if hxgen || not (Meta.has (Meta.NativeGen) cl.cl_meta) then [""] else - List.fold_left (fun acc (name, t) -> + List.fold_left (fun acc {ttp_name=name;ttp_type=t} -> match run_follow gen t with | TInst({cl_kind = KTypeParameter constraints}, _) when constraints <> [] -> (* base class should come before interface constraints *) @@ -2327,7 +2328,7 @@ let generate con = let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in let visibility, is_virtual = if is_explicit_iface then "",false else if visibility = "private" then "private",false else visibility, is_virtual in let v_n = if is_static then "static" else if is_override && not is_interface then "override" else if is_virtual then "virtual" else "" in - let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in + let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, extract_param_types cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in let ret_type, args = match follow cf_type with | TFun (strbtl, t) -> (t, strbtl) | _ -> die "" __LOC__ in gen_nocompletion w cf.cf_meta; @@ -2435,7 +2436,7 @@ let generate con = let args,ret = get_fun cf.cf_type in match args with | [_,_,idx] -> pairs := PMap.add (t_s idx) ( t_s ret, Some cf, None ) !pairs - | _ -> gen.gcon.warning "The __get function must have exactly one argument (the index)" cf.cf_pos + | _ -> gen.gwarning WGenerator "The __get function must have exactly one argument (the index)" cf.cf_pos ) (get :: get.cf_overloads) with | Not_found -> ()); (try @@ -2446,12 +2447,12 @@ let generate con = | [_,_,idx; _,_,v] -> (try let vt, g, _ = PMap.find (t_s idx) !pairs in let tvt = t_s v in - if vt <> tvt then gen.gcon.warning "The __get function of same index has a different type from this __set function" cf.cf_pos; + if vt <> tvt then gen.gwarning WGenerator "The __get function of same index has a different type from this __set function" cf.cf_pos; pairs := PMap.add (t_s idx) (vt, g, Some cf) !pairs with | Not_found -> pairs := PMap.add (t_s idx) (t_s v, None, Some cf) !pairs) | _ -> - gen.gcon.warning "The __set function must have exactly two arguments (index, value)" cf.cf_pos + gen.gwarning WGenerator "The __set function must have exactly two arguments (index, value)" cf.cf_pos ) (set :: set.cf_overloads) with | Not_found -> ()); PMap.iter (fun idx (v, get, set) -> @@ -2568,7 +2569,7 @@ let generate con = let this = if static then make_static_this cl f.cf_pos else - { eexpr = TConst TThis; etype = TInst(cl,List.map snd cl.cl_params); epos = f.cf_pos } + { eexpr = TConst TThis; etype = TInst(cl,extract_param_types cl.cl_params); epos = f.cf_pos } in print w "public %s%s %s" (if static then "static " else "") (t_s f.cf_type) (Dotnet.netname_to_hx f.cf_name); begin_block w; @@ -2633,7 +2634,7 @@ let generate con = else "object" :: (loop (pred i) acc) in - let tparams = loop (match m with [(EConst(Int s),_)] -> int_of_string s | _ -> die "" __LOC__) [] in + let tparams = loop (match m with [(EConst(Int (s, _)),_)] -> int_of_string s | _ -> die "" __LOC__) [] in cl.cl_meta <- (Meta.Meta, [ EConst(String("global::haxe.lang.GenericInterface(typeof(global::" ^ module_s (TClassDecl cl) ^ "<" ^ String.concat ", " tparams ^ ">))",SDoubleQuotes) ), cl.cl_pos ], cl.cl_pos) :: cl.cl_meta @@ -2755,11 +2756,11 @@ let generate con = let events, nonprops = !events, !nonprops in - let t = TInst(cl, List.map snd cl.cl_params) in + let t = TInst(cl, extract_param_types cl.cl_params) in let find_prop name = try List.assoc name !props with | Not_found -> match field_access gen t name with - | FClassField (_,_,decl,v,_,t,_) when is_extern_prop (TInst(cl,List.map snd cl.cl_params)) name -> + | FClassField (_,_,decl,v,_,t,_) when is_extern_prop (TInst(cl,extract_param_types cl.cl_params)) name -> let ret = ref (v,t,None,None) in props := (name, ret) :: !props; ret @@ -3133,7 +3134,7 @@ let generate con = add_cast_handler gen; if not erase_generics then - RealTypeParams.configure gen (fun e t -> gen.gcon.warning ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject"))) + RealTypeParams.configure gen (fun e t -> gen.gwarning WGenerator ("Cannot cast to " ^ (debug_type t)) e.epos; mk_cast t e) ifaces (get_cl (get_type gen (["haxe";"lang"], "IGenericObject"))) else RealTypeParams.RealTypeParamsModf.configure gen (RealTypeParams.RealTypeParamsModf.set_only_hxgeneric gen); @@ -3463,11 +3464,11 @@ let generate con = let net_lib = List.find (function net_lib -> is_some (net_lib#lookup (["haxe";"lang"], "FieldLookup"))) gen.gcon.native_libs.net_libs in let name = net_lib#get_name in if not (Common.defined gen.gcon Define.DllImport) then begin - gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly. Please define `-D dll_import` to handle Haxe-generated dll import correctly") null_pos; + gen.gwarning WGenerator ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly. Please define `-D dll_import` to handle Haxe-generated dll import correctly") null_pos; raise Not_found end; if not (List.exists (function net_lib -> net_lib#get_name = name) haxe_libs) then - gen.gcon.warning ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly, however it wasn't compiled with `-dce no`. Recompilation with `-dce no` is recommended") null_pos; + gen.gwarning WGenerator ("The -net-lib with path " ^ name ^ " contains a Haxe-generated assembly, however it wasn't compiled with `-dce no`. Recompilation with `-dce no` is recommended") null_pos; (* it has; in this case, we need to add the used fields on each __init__ *) add_class_flag flookup_cl CExtern; let hashs_by_path = Hashtbl.create !nhash in @@ -3547,17 +3548,18 @@ let generate con = if ( not (Common.defined gen.gcon Define.NoCompilation) ) then begin let old_dir = Sys.getcwd() in Sys.chdir gen.gcon.file; - let cmd = "haxelib run hxcs hxcs_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in - let cmd = + let cmd = "haxelib" in + let args = ["run"; "hxcs"; "hxcs_build.txt"; "--haxe-version"; (string_of_int gen.gcon.version); "--feature-level"; "1"] in + let args = match gen.gentry_point with | Some (name,_,_) -> let name = if gen.gcon.debug then name ^ "-Debug" else name in - cmd ^ " --out " ^ gen.gcon.file ^ "/bin/" ^ name + args@["--out"; gen.gcon.file ^ "/bin/" ^ name] | _ -> - cmd + args in - print_endline cmd; - if gen.gcon.run_command cmd <> 0 then failwith "Build failed"; + print_endline (cmd ^ " " ^ (String.concat " " args)); + if gen.gcon.run_command_args cmd args <> 0 then failwith "Build failed"; Sys.chdir old_dir; end diff --git a/src/generators/genhl.ml b/src/generators/genhl.ml index ab842b23..e8ffa87c 100644 --- a/src/generators/genhl.ml +++ b/src/generators/genhl.ml @@ -150,7 +150,7 @@ let is_extern_field f = let is_array_class name = match name with - | "hl.types.ArrayDyn" | "hl.types.ArrayBytes_Int" | "hl.types.ArrayBytes_Float" | "hl.types.ArrayObj" | "hl.types.ArrayBytes_F32" | "hl.types.ArrayBytes_hl_UI16" -> true + | "hl.types.ArrayDyn" | "hl.types.ArrayBytes_Int" | "hl.types.ArrayBytes_Float" | "hl.types.ArrayObj" | "hl.types.ArrayBytes_hl_F32" | "hl.types.ArrayBytes_hl_UI16" -> true | _ -> false let is_array_type t = @@ -355,7 +355,7 @@ let make_debug ctx arr = let fake_tnull = {null_abstract with a_path = [],"Null"; - a_params = ["T",t_dynamic]; + a_params = [{ttp_name = "T"; ttp_type = t_dynamic; ttp_default = None}]; } let get_rec_cache ctx t none_callback not_found_callback = @@ -380,7 +380,7 @@ let rec to_type ?tref ctx t = let t = get_rec_cache ctx t (fun() -> abort "Unsupported recursive type" td.t_pos) - (fun tref -> to_type ~tref ctx (apply_params td.t_params tl td.t_type)) + (fun tref -> to_type ~tref ctx (apply_typedef td tl)) in (match td.t_path with | ["haxe";"macro"], name -> Hashtbl.replace ctx.macro_typedefs name t; t @@ -395,7 +395,7 @@ let rec to_type ?tref ctx t = | TAnon a when (match !(a.a_status) with Statics _ | EnumStatics _ -> true | _ -> false) -> (match !(a.a_status) with | Statics c -> - class_type ctx c (List.map snd c.cl_params) true + class_type ctx c (extract_param_types c.cl_params) true | EnumStatics e -> enum_class ctx e | _ -> die "" __LOC__) @@ -644,7 +644,13 @@ and class_type ?(tref=None) ctx c pl statics = | Method MethDynamic when has_class_field_flag f CfOverride -> Some (try fst (get_index f.cf_name p) with Not_found -> die "" __LOC__) | _ -> - let fid = add_field f.cf_name (fun() -> to_type ctx f.cf_type) in + let fid = add_field f.cf_name (fun() -> + let t = to_type ctx f.cf_type in + if has_meta (Meta.Custom ":packed") f.cf_meta then begin + (match t with HStruct _ -> () | _ -> abort "Packed field should be struct" f.cf_pos); + HPacked t + end else t + ) in Some fid ) in match f.cf_kind, fid with @@ -761,7 +767,7 @@ and alloc_global ctx name t = and class_global ?(resolve=true) ctx c = let static = c != ctx.base_class in let c = if resolve && is_array_type (HObj { null_proto with pname = s_type_path c.cl_path }) then ctx.array_impl.abase else c in - let c = resolve_class ctx c (List.map snd c.cl_params) static in + let c = resolve_class ctx c (extract_param_types c.cl_params) static in let t = class_type ctx c [] static in alloc_global ctx ("$" ^ s_type_path c.cl_path) t, t @@ -2484,7 +2490,7 @@ and eval_expr ctx e = free ctx r; binop r r b; r)) - | OpInterval | OpArrow | OpIn -> + | OpInterval | OpArrow | OpIn | OpNullCoal -> die "" __LOC__) | TUnop (Not,_,v) -> let tmp = alloc_tmp ctx HBool in @@ -2507,7 +2513,7 @@ and eval_expr ctx e = | HUI8 -> 0xFFl | HUI16 -> 0xFFFFl | HI32 -> 0xFFFFFFFFl - | _ -> abort (tstr t) e.epos + | _ -> abort ("Unsupported " ^ tstr t) e.epos ) in hold ctx r; let r2 = alloc_tmp ctx t in @@ -2646,8 +2652,8 @@ and eval_expr ctx e = ctx.m.mloop_trys <- oldtrys; alloc_tmp ctx HVoid | TCast ({ eexpr = TCast (v,None) },None) when not (is_number (to_type ctx e.etype)) -> - (* coalesce double casts into a single runtime check - temp fix for Map accesses *) - eval_expr ctx { e with eexpr = TCast(v,None) } + (* coalesce double casts into a single runtime check - temp fix for Map accesses *) + eval_expr ctx { e with eexpr = TCast(v,None) } | TCast (v,None) -> let t = to_type ctx e.etype in let rv = eval_expr ctx v in @@ -3318,7 +3324,7 @@ let generate_static ctx c f = add_native lib name | (Meta.HlNative,[(EConst(String(lib,_)),_)] ,_ ) :: _ -> add_native lib f.cf_name - | (Meta.HlNative,[(EConst(Float(ver)),_)] ,_ ) :: _ -> + | (Meta.HlNative,[(EConst(Float(ver,_)),_)] ,_ ) :: _ -> let cur_ver = (try Common.defined_value ctx.com Define.HlVer with Not_found -> "") in if cur_ver < ver then let gen_content() = @@ -3346,7 +3352,7 @@ let rec generate_member ctx c f = | Method m -> let gen_content = if f.cf_name <> "new" then None else Some (fun() -> - let o = (match class_type ctx c (List.map snd c.cl_params) false with + let o = (match class_type ctx c (extract_param_types c.cl_params) false with | HObj o | HStruct o -> o | _ -> die "" __LOC__ ) in @@ -3388,8 +3394,8 @@ let rec generate_member ctx c f = if f.cf_name = "toString" && not (has_class_field_flag f CfOverride) && not (PMap.mem "__string" c.cl_fields) && is_to_string f.cf_type then begin let p = f.cf_pos in (* function __string() return this.toString().bytes *) - let ethis = mk (TConst TThis) (TInst (c,List.map snd c.cl_params)) p in - let tstr = mk (TCall (mk (TField (ethis,FInstance(c,List.map snd c.cl_params,f))) f.cf_type p,[])) ctx.com.basic.tstring p in + let ethis = mk (TConst TThis) (TInst (c,extract_param_types c.cl_params)) p in + let tstr = mk (TCall (mk (TField (ethis,FInstance(c,extract_param_types c.cl_params,f))) f.cf_type p,[])) ctx.com.basic.tstring p in let cstr, cf_bytes = (try (match ctx.com.basic.tstring with TInst(c,_) -> c, PMap.find "bytes" c.cl_fields | _ -> die "" __LOC__) with Not_found -> die "" __LOC__) in let estr = mk (TReturn (Some (mk (TField (tstr,FInstance (cstr,[],cf_bytes))) cf_bytes.cf_type p))) ctx.com.basic.tvoid p in ignore(make_fun ctx (s_type_path c.cl_path,"__string") (alloc_fun_path ctx c.cl_path "__string") { tf_expr = estr; tf_args = []; tf_type = cf_bytes.cf_type; } (Some c) None) @@ -3437,7 +3443,9 @@ let generate_static_init ctx types main = let gen_content() = - op ctx (OCall0 (alloc_tmp ctx HVoid, alloc_fun_path ctx ([],"Type") "init")); + let is_init = alloc_tmp ctx HBool in + op ctx (OCall0 (is_init, alloc_fun_path ctx ([],"Type") "init")); + hold ctx is_init; (* init class values *) List.iter (fun t -> @@ -3448,7 +3456,7 @@ let generate_static_init ctx types main = let g, ct = class_global ~resolve:false ctx c in let ctype = if c == ctx.array_impl.abase then ctx.array_impl.aall else c in - let t = class_type ctx ctype (List.map snd ctype.cl_params) false in + let t = class_type ctx ctype (extract_param_types ctype.cl_params) false in let index name = match ct with @@ -3609,6 +3617,11 @@ let generate_static_init ctx types main = () ) types; + + let j = jump ctx (fun d -> OJTrue (is_init,d)) in + op ctx (ORet (alloc_tmp ctx HVoid)); + j(); + free ctx is_init; in (* init class statics *) let init_exprs = ref [] in @@ -3676,7 +3689,7 @@ let write_code ch code debug = let write_index = write_index_gen byte in let rec write_type t = - write_index (try PMap.find t htypes with Not_found -> die "" __LOC__) + write_index (try PMap.find t htypes with Not_found -> die (tstr t) __LOC__) in let write_op op = @@ -3829,7 +3842,7 @@ let write_code ch code debug = write_index p.pid; (match p.psuper with | None -> write_index (-1) - | Some t -> write_type (HObj t)); + | Some tsup -> write_type (match t with HObj _ -> HObj tsup | _ -> HStruct tsup)); (match p.pclassglobal with | None -> write_index 0 | Some g -> write_index (g + 1)); @@ -3871,6 +3884,9 @@ let write_code ch code debug = | HNull t -> byte 19; write_type t + | HPacked t -> + byte 22; + write_type t ) all_types; let write_debug_infos debug = @@ -3969,7 +3985,7 @@ let create_context com is_macro dump = let ctx = { com = com; is_macro = is_macro; - optimize = not (Common.raw_defined com "hl-no-opt"); + optimize = not (Common.raw_defined com "hl_no_opt"); dump_out = if dump then Some (IO.output_channel (open_out_bin "dump/hlopt.txt")) else None; m = method_context 0 HVoid null_capture false; cints = new_lookup(); @@ -4089,7 +4105,7 @@ let prev_sign = ref "" and prev_data = ref "" let generate com = let dump = Common.defined com Define.Dump in - let hl_check = Common.raw_defined com "hl-check" in + let hl_check = Common.raw_defined com "hl_check" in let sign = make_context_sign com in if sign = !prev_sign && not dump && not hl_check then begin @@ -4109,7 +4125,7 @@ let generate com = Hlcode.dump (fun s -> output_string ch (s ^ "\n")) code; close_out ch; end; - (*if Common.raw_defined com "hl-dump-spec" then begin + (*if Common.raw_defined com "hl_dump_spec" then begin let ch = open_out_bin "dump/hlspec.txt" in let write s = output_string ch (s ^ "\n") in Array.iter (fun f -> @@ -4132,16 +4148,22 @@ let generate com = "\"" ^ Buffer.contents b ^ "\"" in - if file_extension com.file = "c" then begin + if Path.file_extension com.file = "c" then begin let gnames = Array.create (Array.length code.globals) "" in PMap.iter (fun n i -> gnames.(i) <- n) ctx.cglobals.map; + if not (Common.defined com Define.SourceHeader) then begin + let version_major = com.version / 1000 in + let version_minor = (com.version mod 1000) / 100 in + let version_revision = (com.version mod 100) in + Common.define_value com Define.SourceHeader (Printf.sprintf "Generated by HLC %d.%d.%d (HL v%d)" version_major version_minor version_revision code.version); + end; Hl2c.write_c com com.file code gnames; let t = Timer.timer ["nativecompile";"hl"] in - if not (Common.defined com Define.NoCompilation) && com.run_command ("haxelib run hashlink build " ^ escape_command com.file) <> 0 then failwith "Build failed"; + if not (Common.defined com Define.NoCompilation) && com.run_command_args "haxelib" ["run";"hashlink";"build";escape_command com.file] <> 0 then failwith "Build failed"; t(); end else begin let ch = IO.output_string() in - write_code ch code (not (Common.raw_defined com "hl-no-debug")); + write_code ch code (not (Common.raw_defined com "hl_no_debug")); let str = IO.close_out ch in let ch = open_out_bin com.file in output_string ch str; @@ -4152,7 +4174,7 @@ let generate com = Hlopt.clean_cache(); t(); if Common.raw_defined com "run" then begin - if com.run_command ("haxelib run hashlink run " ^ escape_command com.file) <> 0 then failwith "Failed to run HL"; + if com.run_command_args "haxelib" ["run";"hashlink";"run";escape_command com.file] <> 0 then failwith "Failed to run HL"; end; if Common.defined com Define.Interp then try diff --git a/src/generators/genhxold.ml b/src/generators/genhxold.ml index 482e658b..df7667fb 100644 --- a/src/generators/genhxold.ml +++ b/src/generators/genhxold.ml @@ -104,8 +104,10 @@ let generate_type com t = "{" ^ String.concat ", " fields ^ "}" | TLazy f -> stype (lazy_type f) - | TDynamic t2 -> - if t == t2 then "Dynamic" else "Dynamic<" ^ stype t2 ^ ">" + | TDynamic None -> + "Dynamic" + | TDynamic (Some t2) -> + "Dynamic<" ^ stype t2 ^ ">" | TFun ([],ret) -> "() -> " ^ ftype ret | TFun (args,ret) -> @@ -180,14 +182,14 @@ let generate_type com t = | [] -> Ident "null" | (Meta.DefParam,[(EConst (String(p,_)),_);(EConst v,_)],_) :: _ when p = a -> (match v with - | Float "1.#QNAN" -> Float "0./*NaN*/" - | Float "4294967295." -> Int "0xFFFFFFFF" - | Int "16777215" -> Int "0xFFFFFF" - | Float x -> + | Float ("1.#QNAN", _) -> Float ("0./*NaN*/", None) + | Float ("4294967295.", _) -> Int ("0xFFFFFFFF", None) + | Int ("16777215", _) -> Int ("0xFFFFFF", None) + | Float (x, _) -> (try let f = float_of_string x in let s = string_of_int (int_of_float f) in - if s ^ "." = x then Int s else v + if s ^ "." = x then Int (s, None) else v with _ -> v) | _ -> v) @@ -198,7 +200,7 @@ let generate_type com t = | _ -> die "" __LOC__ ) in - let tparams = (match f.cf_params with [] -> "" | l -> "<" ^ String.concat "," (List.map fst l) ^ ">") in + let tparams = (match f.cf_params with [] -> "" | l -> "<" ^ String.concat "," (List.map extract_param_name l) ^ ">") in p "function %s%s(%s) : %s" name tparams (String.concat ", " (List.map sparam params)) (stype ret); ); p ";\n"; @@ -208,7 +210,7 @@ let generate_type com t = | TClassDecl c -> print_meta c.cl_meta; let finalmod = if (has_class_flag c CFinal) then "final " else "" in - p "extern %s%s %s" finalmod (if (has_class_flag c CInterface) then "interface" else "class") (stype (TInst (c,List.map snd c.cl_params))); + p "extern %s%s %s" finalmod (if (has_class_flag c CInterface) then "interface" else "class") (stype (TInst (c,extract_param_types c.cl_params))); let ext = (match c.cl_super with | None -> [] | Some (c,pl) -> [" extends " ^ stype (TInst (c,pl))] @@ -249,7 +251,7 @@ let generate_type com t = p "}\n"; | TEnumDecl e -> print_meta e.e_meta; - p "extern enum %s {\n" (stype (TEnum(e,List.map snd e.e_params))); + p "extern enum %s {\n" (stype (TEnum(e,extract_param_types e.e_params))); List.iter (fun n -> let c = PMap.find n e.e_constrs in p "\t%s" c.ef_name; @@ -261,7 +263,7 @@ let generate_type com t = p "}\n" | TTypeDecl t -> print_meta t.t_meta; - p "typedef %s = " (stype (TType (t,List.map snd t.t_params))); + p "typedef %s = " (stype (TType (t,extract_param_types t.t_params))); p "%s" (stype t.t_type); p "\n"; | TAbstractDecl a -> @@ -270,7 +272,7 @@ let generate_type com t = p "extern "; let is_enum = a.a_enum in if is_enum then p "enum "; - p "abstract %s" (stype (TAbstract (a,List.map snd a.a_params))); + p "abstract %s" (stype (TAbstract (a,extract_param_types a.a_params))); if not (Meta.has Meta.CoreType a.a_meta) then p "(%s)" (stype a.a_this); p " {\n"; Option.may (fun c -> diff --git a/src/generators/genjava.ml b/src/generators/genjava.ml index 0b3b0d23..c19edc3a 100644 --- a/src/generators/genjava.ml +++ b/src/generators/genjava.ml @@ -1084,6 +1084,10 @@ let generate con = Some t | TAbstract (({ a_path = [],"Null" } as tab),[t2]) -> Some (TAbstract(tab,[gen.gfollow#run_f t2])) + | TType ({ t_path = ["haxe";"_Rest"],"NativeRest" } as td, [t2]) -> + Some (gen.gfollow#run_f (follow (TType (td,[get_boxed gen t2])))) + | TAbstract ({ a_path = ["haxe"],"Rest" } as a, [t2]) -> + Some (gen.gfollow#run_f ( Abstract.get_underlying_type a [get_boxed gen t2]) ) | TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) -> Some (gen.gfollow#run_f ( Abstract.get_underlying_type a pl) ) | TAbstract( { a_path = ([], "EnumValue") }, _ ) @@ -1138,11 +1142,8 @@ let generate con = | TAbstract( { a_path = (["java"], "Int64") }, [] ) | TAbstract( { a_path = (["haxe"], "Int64") }, [] ) -> TInst(cl_long, []) - | _ -> (match follow t with - | TInst( { cl_kind = KTypeParameter _ }, []) -> - t_dynamic - | _ -> real_type t - ) + | _ -> + real_type t ) | TAbstract (a, pl) when not (Meta.has Meta.CoreType a.a_meta) -> real_type (Abstract.get_underlying_type a pl) @@ -1520,7 +1521,7 @@ let generate con = | TTypeExpr mt -> write w (md_s e.epos mt) | TParenthesis e -> write w "("; expr_s w e; write w ")" - | TMeta ((Meta.LoopLabel,[(EConst(Int n),_)],_), e) -> + | TMeta ((Meta.LoopLabel,[(EConst(Int (n, _)),_)],_), e) -> (match e.eexpr with | TFor _ | TWhile _ -> print w "label%s:" n; @@ -1530,6 +1531,11 @@ let generate con = | _ -> die "" __LOC__) | TMeta (_,e) -> expr_s w e + | TCall ({ eexpr = TField(_, FStatic({ cl_path = (["haxe";"_Rest"],"Rest_Impl_") }, { cf_name = "createNative" })) }, el) -> + (match follow e.etype with + | TInst (cls, params) -> + expr_s w { e with eexpr = TNew(cls,params,el) } + | _ -> die ~p:e.epos "Unexpected return type of haxe.Rest.createNative" __LOC__) | TCall ({ eexpr = TIdent "__array__" }, el) | TCall ({ eexpr = TField(_, FStatic({ cl_path = (["java"],"NativeArray") }, { cf_name = "make" })) }, el) | TArrayDecl el when t_has_type_param e.etype -> @@ -1815,7 +1821,7 @@ let generate con = let rec gen_fpart_attrib w = function | EConst( Ident i ), _ -> write w i - | EField( ef, f ), _ -> + | EField( ef, f, _ ), _ -> gen_fpart_attrib w ef; write w "."; write w f @@ -1825,14 +1831,14 @@ let generate con = let rec gen_spart w = function | EConst c, p -> (match c with - | Int s | Float s | Ident s -> + | Int (s, _) | Float (s, _) | Ident s -> write w s | String(s,_) -> write w "\""; write w (escape s); write w "\"" | _ -> gen.gcon.error "Invalid expression inside @:meta metadata" p) - | EField( ef, f ), _ -> + | EField( ef, f, _ ), _ -> gen_spart w ef; write w "."; write w f @@ -1900,8 +1906,8 @@ let generate con = | [] -> ("","") | _ -> - let params = sprintf "<%s>" (String.concat ", " (List.map (fun (_, tcl) -> match follow tcl with | TInst(cl, _) -> snd cl.cl_path | _ -> die "" __LOC__) cl_params)) in - let params_extends = List.fold_left (fun acc (name, t) -> + let params = sprintf "<%s>" (String.concat ", " (List.map (fun tp -> match follow tp.ttp_type with | TInst(cl, _) -> snd cl.cl_path | _ -> die "" __LOC__) cl_params)) in + let params_extends = List.fold_left (fun acc {ttp_name=name;ttp_type=t} -> match run_follow gen t with | TInst (cl, p) -> (match cl.cl_implements with @@ -1992,9 +1998,9 @@ let generate con = let modifiers = if is_abstract then "abstract" :: modifiers else modifiers in let visibility, is_virtual = if is_explicit_iface then "",false else visibility, is_virtual in let v_n = if is_static then "static" else if is_override && not is_interface then "" else if not is_virtual then "final" else "" in - let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, List.map snd cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in + let cf_type = if is_override && not is_overload && not (has_class_field_flag cf CfOverload) then match field_access gen (TInst(cl, extract_param_types cl.cl_params)) cf.cf_name with | FClassField(_,_,_,_,_,actual_t,_) -> actual_t | _ -> die "" __LOC__ else cf.cf_type in - let params = List.map snd cl.cl_params in + let params = extract_param_types cl.cl_params in let ret_type, args, has_rest_args = match follow cf_type, follow cf.cf_type with | TFun (strbtl, t), TFun(rargs, _) -> let ret_type = apply_params cl.cl_params params (real_type t) @@ -2084,6 +2090,11 @@ let generate con = newline w in + let gen_class_field w ?(is_overload=false) is_static cl is_final cf = + (* This should probably be handled somewhere earlier in the unholy gencommon machinery, but whatever *) + if not (has_class_field_flag cf CfExtern) then gen_class_field w ~is_overload is_static cl is_final cf + in + let gen_class w cl = let cf_filters = [ handle_throws ] in List.iter (fun f -> List.iter (f gen) cl.cl_ordered_fields) cf_filters; @@ -2693,17 +2704,18 @@ let generate con = if ( not (Common.defined gen.gcon Define.NoCompilation) ) then begin let old_dir = Sys.getcwd() in Sys.chdir gen.gcon.file; - let cmd = "haxelib run hxjava hxjava_build.txt --haxe-version " ^ (string_of_int gen.gcon.version) ^ " --feature-level 1" in - let cmd = + let cmd = "haxelib" in + let args = ["run";"hxjava";"hxjava_build.txt";"--haxe-version";(string_of_int gen.gcon.version);"--feature-level";"1"] in + let args = match gen.gentry_point with | Some (name,_,_) -> let name = if gen.gcon.debug then name ^ "-Debug" else name in - cmd ^ " --out " ^ gen.gcon.file ^ "/" ^ name + args @ ["--out";gen.gcon.file ^ "/" ^ name] | _ -> - cmd + args in - print_endline cmd; - if gen.gcon.run_command cmd <> 0 then failwith "Build failed"; + print_endline (cmd ^ " " ^ (String.concat " " args)); + if gen.gcon.run_command_args cmd args <> 0 then failwith "Build failed"; Sys.chdir old_dir; end diff --git a/src/generators/genjs.ml b/src/generators/genjs.ml index c999e525..08199621 100644 --- a/src/generators/genjs.ml +++ b/src/generators/genjs.ml @@ -716,7 +716,7 @@ and gen_expr ctx e = spr ctx "("; gen_value ctx e; spr ctx ")"; - | TMeta ((Meta.LoopLabel,[(EConst(Int n),_)],_), e) -> + | TMeta ((Meta.LoopLabel,[(EConst(Int (n, _)),_)],_), e) -> (match e.eexpr with | TWhile _ | TFor _ -> print ctx "_hx_loop%s: " n; @@ -1332,15 +1332,17 @@ let generate_class_es3 ctx c = let p = s_path ctx cl_path in let dotp = dot_path cl_path in - let added_to_hxClasses = ctx.has_resolveClass && not is_abstract_impl in - if ctx.js_modern || not added_to_hxClasses then + (* Do not add to $hxClasses on same line as declaration to make sure not to trip js debugger *) + (* when it tries to get a string representation of a class. Will be added below *) + if ctx.com.debug || ctx.js_modern || not added_to_hxClasses then print ctx "%s = " p else print ctx "%s = $hxClasses[\"%s\"] = " p dotp; - process_expose c.cl_meta (fun () -> dotp) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s)); + if not ctx.com.debug then + process_expose c.cl_meta (fun () -> dotp) (fun s -> print ctx "$hx_exports%s = " (path_to_brackets s)); if is_abstract_impl then begin (* abstract implementations only contain static members and don't need to have constructor functions *) @@ -1354,11 +1356,17 @@ let generate_class_es3 ctx c = newline ctx; - if ctx.js_modern && added_to_hxClasses then begin + if (ctx.js_modern || ctx.com.debug) && added_to_hxClasses then begin print ctx "$hxClasses[\"%s\"] = %s" dotp p; newline ctx; end; + if ctx.com.debug then + process_expose c.cl_meta (fun () -> dotp) (fun s -> begin + print ctx "$hx_exports%s = %s" (path_to_brackets s) p; + newline ctx; + end); + if not is_abstract_impl then begin generate_class___name__ ctx cl_path; generate_class___isInterface__ ctx c; @@ -2082,7 +2090,7 @@ let generate com = newline ctx; end; if has_feature ctx "use.$arrayPush" then begin - print ctx "function $arrayPush(x) { this.push(x); }"; + print ctx "function $arrayPush(x) { return this.push(x); }"; newline ctx end; if has_feature ctx "$global.$haxeUID" then begin diff --git a/src/generators/genjvm.ml b/src/generators/genjvm.ml index 22d9a947..59958186 100644 --- a/src/generators/genjvm.ml +++ b/src/generators/genjvm.ml @@ -40,20 +40,30 @@ open Genshared let is_really_int t = not (is_nullable t) && ExtType.is_int (follow t) +(* Due to @:native, the usual String path doesn't match *) +let is_string t = match t with +| TInst({cl_path=([],"String") | (["java";"lang"],"String")},_) -> true +| _ -> false + let get_construction_mode c cf = if Meta.has Meta.HxGen cf.cf_meta then ConstructInitPlusNew else ConstructInit +class virtual jvm_output = object(self) + method virtual add_entry : string -> string -> unit + method virtual close : unit +end + (* Haxe *) type generation_context = { com : Common.context; - jar : Zip.out_file; + out : jvm_output; t_runtime_exception : Type.t; entry_point : (tclass * texpr) option; t_exception : Type.t; t_throwable : Type.t; - mutable anon_identification : jsignature tanon_identification; + anon_identification : jsignature tanon_identification; mutable preprocessor : jsignature preprocessor; default_export_config : export_config; typed_functions : JvmFunctions.typed_functions; @@ -62,13 +72,12 @@ type generation_context = { detail_times : bool; mutable timer : Timer.timer; mutable typedef_interfaces : jsignature typedef_interfaces; - mutable current_field_info : field_generation_info option; jar_compression_level : int; dynamic_level : int; } type ret = - | RValue of jsignature option + | RValue of jsignature option * string option | RVoid | RReturn @@ -99,6 +108,36 @@ let run_timed gctx detail name f = sub#run_finally f (fun () -> gctx.timer <- old) end +class jar_output + (jar_path : string) + (compression_level : int) += object(self) + inherit jvm_output + val jar = Zip.open_out jar_path + + method add_entry (content : string) (name : string) = + Zip.add_entry ~level:compression_level content jar name + + method close = + Zip.close_out jar +end + +class file_output + (base_path : string) + = object(self) + inherit jvm_output + + method add_entry (content : string) (name : string) = + let path = base_path ^ name in + Path.mkdir_from_path path; + let ch = open_out_bin path in + output_string ch content; + close_out ch + + method close = + () +end + open NativeSignatures let rec jsignature_of_type gctx stack t = @@ -129,7 +168,6 @@ let rec jsignature_of_type gctx stack t = | [t] -> TArray(jsignature_of_type t,None) | _ -> die "" __LOC__ end - | ["haxe"],"Rest" -> TArray(object_sig,None) | [],"Dynamic" -> object_sig | [],("Class" | "Enum") -> @@ -157,7 +195,7 @@ let rec jsignature_of_type gctx stack t = TObject((["haxe";"root"],"Array"),[TType(WNone,t)]) | TInst({cl_path = (["java"],"NativeArray")},[t]) -> TArray(jsignature_of_type t,None) - | TInst({cl_kind = KTypeParameter [t]},_) -> jsignature_of_type t + | TInst({cl_kind = KTypeParameter [t]},_) when t != t_dynamic -> jsignature_of_type t | TInst({cl_kind = KTypeParameter _; cl_path = (_,name)},_) -> TTypeParameter name | TInst({cl_path = ["_Class"],"Class_Impl_"},_) -> java_class_sig | TInst({cl_path = ["_Enum"],"Enum_Impl_"},_) -> java_class_sig @@ -174,7 +212,7 @@ let rec jsignature_of_type gctx stack t = | TType(td,tl) -> begin match gctx.typedef_interfaces#get_interface_class td.t_path with | Some c -> TObject(c.cl_path,[]) - | None -> jsignature_of_type (apply_params td.t_params tl td.t_type) + | None -> jsignature_of_type (apply_typedef td tl) end | TLazy f -> jsignature_of_type (lazy_type f) @@ -204,32 +242,38 @@ let convert_fields gctx pfm = l module AnnotationHandler = struct - let generate_annotations builder meta = + let convert_annotations meta = let parse_path e = - let sl = try string_list_of_expr_path_raise e with Exit -> Error.error "Field expression expected" (pos e) in + let sl = try string_list_of_expr_path_raise e with Exit -> Error.typing_error "Field expression expected" (pos e) in let path = match sl with | s :: sl -> List.rev sl,s - | _ -> Error.error "Field expression expected" (pos e) + | _ -> Error.typing_error "Field expression expected" (pos e) in path in let rec parse_value e = match fst e with - | EConst (Int s) -> AInt (Int32.of_string s) - | EConst (Float s) -> ADouble (float_of_string s) + | EConst (Int (s, _)) -> AInt (Int32.of_string s) + | EConst (Float (s, _)) -> ADouble (float_of_string s) | EConst (String(s,_)) -> AString s | EConst (Ident "true") -> ABool true | EConst (Ident "false") -> ABool false | EArrayDecl el -> AArray (List.map parse_value el) - | EField(e1,s) -> + | EField(e1,s,_) -> let path = parse_path e1 in AEnum(object_path_sig path,s) - | _ -> Error.error "Expected value expression" (pos e) - in - let parse_value_pair e = match fst e with + | ECall(e1, el) -> + let path = parse_path e1 in + let _,name = ExtString.String.replace (snd path) "." "$" in + let path = (fst path, name) in + let values = List.map parse_value_pair el in + AAnnotation(TObject(path, []),values) + + | _ -> Error.typing_error "Expected value expression" (pos e) + and parse_value_pair e = match fst e with | EBinop(OpAssign,(EConst(Ident s),_),e1) -> s,parse_value e1 | _ -> - Error.error "Assignment expression expected" (pos e) + Error.typing_error "Assignment expression expected" (pos e) in let parse_expr e = match fst e with | ECall(e1,el) -> @@ -239,19 +283,24 @@ module AnnotationHandler = struct let values = List.map parse_value_pair el in path,values | _ -> - Error.error "Call expression expected" (pos e) + Error.typing_error "Call expression expected" (pos e) in - List.iter (fun (m,el,_) -> match m,el with + ExtList.List.filter_map (fun (m,el,_) -> match m,el with | Meta.Meta,[e] -> let path,annotation = parse_expr e in let path = match path with | [],name -> ["haxe";"root"],name | _ -> path in - builder#add_annotation path annotation; + Some(path,annotation) | _ -> - () + None ) meta + + let generate_annotations builder meta = + List.iter (fun (path,annotation) -> + builder#add_annotation path annotation + ) (convert_annotations meta) end let enum_ctor_sig = @@ -295,7 +344,7 @@ let write_class gctx path jc = let t = Timer.timer ["jvm";"write"] in let ch = IO.output_bytes() in JvmWriter.write_jvm_class ch jc; - Zip.add_entry ~level:gctx.jar_compression_level (Bytes.unsafe_to_string (IO.close_out ch)) gctx.jar path; + gctx.out#add_entry (Bytes.unsafe_to_string (IO.close_out ch)) path; t() let is_const_int_pattern (el,_) = @@ -371,7 +420,7 @@ let create_field_closure gctx jc path_this jm name jsig = | _ -> die "" __LOC__ in - let jm_invoke = wf#generate_invoke args ret in + let jm_invoke = wf#generate_invoke args ret [] in let vars = List.map (fun (name,jsig) -> jm_invoke#add_local name jsig VarArgument ) args in @@ -417,11 +466,17 @@ let create_field_closure gctx jc path_this jm name jsig f = [jsig_this] ) -let rvalue_any = RValue None -let rvalue_sig jsig = RValue (Some jsig) -let rvalue_type gctx t = RValue (Some (jsignature_of_type gctx t)) - -class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return_type : jsignature option) = object(self) +let rvalue_any = RValue(None,None) +let rvalue_sig jsig = RValue (Some jsig,None) +let rvalue_type gctx t name = RValue (Some (jsignature_of_type gctx t),name) + +class texpr_to_jvm + (gctx : generation_context) + (field_info : field_generation_info option) + (jc : JvmClass.builder) + (jm : JvmMethod.builder) + (return_type : jsignature option) += object(self) val com = gctx.com val code = jm#get_code val pool : JvmConstantPool.constant_pool = jc#get_pool @@ -491,7 +546,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return jm#cast vt method cast_expect ret t = match ret with - | RValue (Some jsig) -> jm#cast jsig + | RValue (Some jsig,_) -> jm#cast jsig | _ -> self#cast t method make_static_closure_field (name : string) (jc_closure : JvmClass.builder) = @@ -500,16 +555,24 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return jm_init#construct ConstructInit jc_closure#get_this_path (fun () -> []); jm_init#putstatic jc_closure#get_this_path jf_closure#get_name jf_closure#get_jsig; - method tfunction e tf = + method tfunction ret e tf = let outside,accesses_this = Texpr.collect_captured_vars e in let env = List.map (fun v -> v.v_id,(v.v_name,self#vtype v.v_type) ) outside in let env = if accesses_this then ((0,("this",jc#get_jsig)) :: env) else env in let context = List.map snd env in - let wf = new JvmFunctions.typed_function gctx.typed_functions FuncLocal jc jm context in + let name = match ret with + | RValue(_,Some s) -> Some s + | _ -> None + in + let wf = new JvmFunctions.typed_function gctx.typed_functions (FuncLocal name) jc jm context in let jc_closure = wf#get_class in ignore(wf#generate_constructor (env <> [])); + let filter = match ret with + | RValue (Some (TObject(path,_)),_) -> [path] + | _ -> [] + in let args,ret = let args = List.map (fun (v,eo) -> (* TODO: Can we do this differently? *) @@ -518,8 +581,8 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return ) tf.tf_args in args,(return_of_type gctx tf.tf_type) in - let jm_invoke = wf#generate_invoke args ret in - let handler = new texpr_to_jvm gctx jc_closure jm_invoke ret in + let jm_invoke = wf#generate_invoke args ret filter in + let handler = new texpr_to_jvm gctx field_info jc_closure jm_invoke ret in handler#set_env env; let args = List.map (fun (v,eo) -> handler#add_local v VarArgument,v,eo @@ -595,7 +658,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return let wf = new JvmFunctions.typed_function gctx.typed_functions (FuncStatic(path,name)) jc jm [] in let jc_closure = wf#get_class in ignore(wf#generate_constructor false); - let jm_invoke = wf#generate_invoke args ret in + let jm_invoke = wf#generate_invoke args ret [] in let vars = List.map (fun (name,jsig) -> jm_invoke#add_local name jsig VarArgument ) args in @@ -945,7 +1008,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return method do_compare op = match code#get_stack#get_stack_items 2 with - | [TInt | TByte | TChar | TBool;TInt | TByte | TChar | TBool] -> + | [TInt | TByte | TChar | TShort | TBool;TInt | TByte | TChar | TShort | TBool] -> let op = flip_cmp_op op in CmpSpecial (code#if_icmp op) | [TObject((["java";"lang"],"String"),[]);TObject((["java";"lang"],"String"),[])] -> @@ -1290,7 +1353,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return self#boolop (self#binop_compare op e1 e2) | OpAssign,_ -> let f () = - self#texpr (rvalue_type gctx e1.etype) e2; + self#texpr (rvalue_type gctx e1.etype None) e2; self#cast e1.etype; in self#read_write ret AKNone e1 f @@ -1335,6 +1398,9 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return | TDouble -> code#dconst 1.; if op = Increment then code#dadd else code#dsub + | TFloat -> + code#fconst 1.; + if op = Increment then code#fadd else code#fsub | TByte | TShort | TInt -> code#iconst Int32.one; if op = Increment then code#iadd else code#isub; @@ -1351,6 +1417,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return begin match jsig with | TLong -> code#lneg; | TDouble -> code#dneg; + | TFloat -> code#fneg; | TByte | TShort | TInt -> code#ineg; | _ -> jm#invokestatic haxe_jvm_path "opNeg" (method_sig [object_sig] (Some object_sig)) end; @@ -1361,7 +1428,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return (fun () -> code#bconst false) (fun () -> code#bconst true) | Spread, _ -> - self#texpr (rvalue_type gctx e.etype) e + self#texpr (rvalue_type gctx e.etype None) e | NegBits,_ -> let jsig = jsignature_of_type gctx (follow e.etype) in self#texpr rvalue_any e; @@ -1396,7 +1463,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return | TUnop (Spread,_,e) -> self#texpr (rvalue_sig jsig) e | _ -> - self#new_native_array (get_boxed_type (jsignature_of_type gctx t1)) (e :: el) + self#new_native_array (jsignature_of_type gctx t1) (e :: el) ); List.rev (jsig :: acc) | _ -> @@ -1413,7 +1480,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return (match Type.follow t with | TAbstract({a_path = ["haxe"],"Rest"},[t1]) -> let jsig = jsignature_of_type gctx t in - self#new_native_array (get_boxed_type (jsignature_of_type gctx t1)) []; + self#new_native_array (jsignature_of_type gctx t1) []; List.rev (jsig :: acc) | _ -> List.rev acc) | _,[] -> List.rev acc @@ -1449,11 +1516,11 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return self#expect_reference_type; let path = match jsignature_of_type gctx (type_of_module_type mt) with | TObject(path,_) -> path - | _ -> Error.error "Class expected" pe + | _ -> Error.typing_error "Class expected" pe in code#instanceof path; Some TBool - | _ -> Error.error "Type expression expected" e1.epos + | _ -> Error.typing_error "Type expression expected" e1.epos end; | TField(_,FStatic({cl_path = (["java";"lang"],"Math")},{cf_name = ("isNaN" | "isFinite") as name})) -> begin match el with @@ -1512,7 +1579,19 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return self#new_native_array jsig el; Some (array_sig jsig) | _ -> - Error.error (Printf.sprintf "Bad __array__ type: %s" (s_type (print_context()) tr)) e1.epos; + Error.typing_error (Printf.sprintf "Bad __array__ type: %s" (s_type (print_context()) tr)) e1.epos; + end + | TField(_,FStatic({cl_path = (["haxe"],"EnumTools")}, {cf_name = "values"})) -> + begin match el with + | [e1] -> + let jsig_ret = array_sig (object_path_sig object_path) in + let meth = gctx.typed_functions#register_signature [] (Some jsig_ret) in + let jsig_meth = (method_sig meth.dargs meth.dret) in + self#read (fun () -> jm#cast jsig_meth) e1 (FDynamic "values"); + jm#invokevirtual haxe_function_path meth.name jsig_meth; + Some jsig_ret + | _ -> + die "" __LOC__ end | TField(e1,FStatic(c,({cf_kind = Method (MethNormal | MethInline)} as cf))) -> let tl,tr = self#call_arguments cf.cf_type el in @@ -1580,12 +1659,12 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return invoke e1.etype end | TConst TSuper -> - let c,cf = match gctx.current_field_info with + let c,cf = match field_info with | Some ({super_call_fields = hd :: tl} as info) -> info.super_call_fields <- tl; hd | _ -> - Error.error "Something went wrong" e1.epos + Error.typing_error "Something went wrong" e1.epos in let kind = get_construction_mode c cf in begin match kind with @@ -1651,7 +1730,16 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return end | _ -> self#texpr rvalue_any e1; - invoke e1.etype; + let t = match e1.eexpr with + | TLocal ({v_extra = Some {v_params = l}} as v) when l <> [] -> + (* If we call a local variable with type parameters, we want to use the general (unapplied) + type, which is the variable type itself. This is necessary for some special situations + like the Rest case in issue #10906. *) + v.v_type + | _ -> + e1.etype + in + invoke t; in match need_val ret,tro with | false,Some _ -> code#pop @@ -1729,13 +1817,13 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return method const ret t ct = match ct with | Type.TInt i32 -> begin match ret with - | RValue (Some (TLong | TObject((["java";"lang"],"Long"),_))) -> code#lconst (Int64.of_int32 i32) - | RValue (Some (TDouble | TObject((["java";"lang"],"Double"),_))) -> code#dconst (Int32.to_float i32) + | RValue (Some (TLong | TObject((["java";"lang"],"Long"),_)),_) -> code#lconst (Int64.of_int32 i32) + | RValue (Some (TDouble | TObject((["java";"lang"],"Double"),_)),_) -> code#dconst (Int32.to_float i32) | _ -> code#iconst i32 end | TFloat f -> begin match ret with - | RValue (Some (TFloat | TObject((["java";"lang"],"Float"),_))) -> code#fconst (float_of_string f) + | RValue (Some (TFloat | TObject((["java";"lang"],"Float"),_)),_) -> code#fconst (float_of_string f) | _ -> code#dconst (float_of_string f) end | TBool true -> code#bconst true @@ -1767,7 +1855,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return code#set_line (Lexer.get_error_line e.epos); match e.eexpr with | TVar(v,Some e1) -> - self#texpr (rvalue_type gctx v.v_type) e1; + self#texpr (rvalue_type gctx v.v_type (Some v.v_name)) e1; self#cast v.v_type; let _,_,store = self#add_local v VarWillInit in store() @@ -1787,12 +1875,12 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return | Not | Neg | NegBits when not (need_val ret) -> self#texpr ret e1 | _ -> self#unop ret op flag e1 end - | TBinop(OpAdd,e1,e2) when ExtType.is_string (follow e.etype) -> + | TBinop(OpAdd,e1,e2) when is_string (follow e.etype) -> let string_builder_path = (["java";"lang"],"StringBuilder") in let string_builder_sig = object_path_sig string_builder_path in jm#construct ConstructInit string_builder_path (fun () -> []); let rec loop e = match e.eexpr with - | TBinop(OpAdd,e1,e2) when ExtType.is_string (follow e.etype) -> + | TBinop(OpAdd,e1,e2) when is_string (follow e.etype) -> loop e1; loop e2; | _ -> @@ -1842,18 +1930,22 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return ) | TSwitch(e1,cases,def) -> self#switch ret e1 cases def - | TWhile(e1,e2,flag) -> (* TODO: do-while *) + | TWhile(e1,e2,flag) -> block_exits <- ExitLoop :: block_exits; let is_true_loop = match (Texpr.skip e1).eexpr with TConst (TBool true) -> true | _ -> false in let continue_label = jm#spawn_label "continue" in let break_label = jm#spawn_label "break" in let body_label = jm#spawn_label "body" in + let restore = jm#start_branch in + if flag = DoWhile then begin + body_label#goto; + restore(); + end; let old_continue = continue in continue <- Some continue_label; let old_break = break in break <- Some break_label; continue_label#here; - let restore = jm#start_branch in if not is_true_loop then self#condition false e1 body_label break_label; let pop_scope = jm#push_scope in body_label#here; @@ -1903,7 +1995,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return ) | TNew(c,tl,el) -> begin match OverloadResolution.maybe_resolve_constructor_overload c tl el with - | None -> Error.error "Could not find overload" e.epos + | None -> Error.typing_error "Could not find overload" e.epos | Some (c',cf,_) -> let f () = let tl,_ = self#call_arguments cf.cf_type el in @@ -1916,12 +2008,14 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return jm#return; | TReturn (Some e1) -> self#texpr rvalue_any e1; - let jsig = Option.get return_type in - jm#cast jsig; - self#emit_block_exits false; - jm#return; + if not (jm#is_terminated) then begin + let jsig = Option.get return_type in + jm#cast jsig; + self#emit_block_exits false; + jm#return; + end | TFunction tf -> - self#tfunction e tf + self#tfunction ret e tf | TArrayDecl el when not (need_val ret) -> List.iter (self#texpr ret) el | TArrayDecl el -> @@ -2004,56 +2098,59 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return self#cast e.etype; | TThrow e1 -> self#texpr rvalue_any e1; - if not (Exceptions.is_haxe_exception e1.etype) && not (does_unify e1.etype gctx.t_runtime_exception) then begin - let exc = new haxe_exception gctx e1.etype in - if not (List.exists (fun exc' -> exc#is_assignable_to exc') caught_exceptions) then - jm#add_thrown_exception exc#get_native_path; - end; - code#athrow; - jm#set_terminated true + (* There could be something like `throw throw`, so we should only throw if we aren't terminated (issue #10363) *) + if not (jm#is_terminated) then begin + if not (Exceptions.is_haxe_exception e1.etype) && not (does_unify e1.etype gctx.t_runtime_exception) then begin + let exc = new haxe_exception gctx e1.etype in + if not (List.exists (fun exc' -> exc#is_assignable_to exc') caught_exceptions) then + jm#add_thrown_exception exc#get_native_path; + end; + code#athrow; + jm#set_terminated true + end | TObjectDecl fl -> - let td = gctx.anon_identification#identify true e.etype in - begin match follow e.etype,td with - (* The guard is here because in the case of quoted fields like `"a-b"`, the field is not part of the - type. In this case we have to do full dynamic construction. *) - | TAnon an,Some pfm when List.for_all (fun ((name,_,_),_) -> PMap.mem name an.a_fields) fl -> - let fl' = convert_fields gctx pfm in + let had_invalid_field_name = ref false in + (* We cannot rely on e.etype because it might have optional field shit, so we need to build a concrete type from the fields... *) + let fields = List.fold_left (fun acc ((name,_,_),e) -> + let cf = mk_field name e.etype e.epos e.epos in + if not (Lexer.is_valid_identifier name) then had_invalid_field_name := true; + PMap.add name cf acc + ) PMap.empty fl in + let t = mk_anon ~fields (ref Closed) in + let td = gctx.anon_identification#identify true t in + begin match td with + | Some pfm when not !had_invalid_field_name -> + let lut = Hashtbl.create 0 in jm#construct ConstructInit pfm.pfm_path (fun () -> - (* We have to respect declaration order, so let's temp var where necessary *) - let rec loop fl fl' ok acc = match fl,fl' with - | ((name,_,_),e) :: fl,(name',jsig) :: fl' -> - if ok && name = name' then begin + (* Step 1: Expressions in order with temp vars *) + let rec loop fl = match fl with + | ((name,_,_),e) :: fl -> + let jsig = self#vtype e.etype in + let load = match (Texpr.skip e).eexpr with + | TConst _ | TTypeExpr _ | TFunction _ -> + (fun () -> self#texpr rvalue_any e) + | _ -> + let _,load,save = jm#add_local (Printf.sprintf "_hx_tmp_%s" name) jsig VarWillInit in self#texpr rvalue_any e; - jm#cast jsig; - loop fl fl' ok acc - end else begin - let load = match (Texpr.skip e).eexpr with - | TConst _ | TTypeExpr _ | TFunction _ -> - (fun () -> self#texpr rvalue_any e) - | _ -> - let _,load,save = jm#add_local (Printf.sprintf "_hx_tmp_%s" name) (self#vtype e.etype) VarWillInit in - self#texpr rvalue_any e; - save(); - load - in - loop fl fl' false ((name,load) :: acc) - end - | [],[] -> - acc - | (_,e) :: fl,[] -> - self#texpr RVoid e; - loop fl fl' ok acc - | [],(_,jsig) :: fl' -> - jm#load_default_value jsig; - loop [] fl' ok acc + save(); + load + in + Hashtbl.add lut name load; + loop fl; + | [] -> + () in - let vars = loop fl fl' true [] in - let vars = List.sort (fun (name1,_) (name2,_) -> compare name1 name2) vars in - List.iter (fun (name,load) -> + loop fl; + (* Step 2: Fields in order of constructor arguments *) + let order = List.sort (fun ((name1,_,_),_) ((name2,_,_),_) -> compare name1 name2) fl in + List.map (fun ((name,_,_),_) -> + let load = Hashtbl.find lut name in load(); - if List.mem_assoc name fl' then jm#cast (List.assoc name fl') - ) vars; - List.map snd fl'; + let cf = PMap.find name pfm.pfm_fields in + let jsig = self#vtype cf.cf_type in + jm#cast jsig; + jsig + ) order; ) | _ -> jm#construct ConstructInit haxe_dynamic_object_path (fun () -> []); @@ -2066,7 +2163,7 @@ class texpr_to_jvm gctx (jc : JvmClass.builder) (jm : JvmMethod.builder) (return ) fl; end | TIdent _ -> - Error.error (s_expr_ast false "" (s_type (print_context())) e) e.epos; + Error.typing_error (s_expr_ast false "" (s_type (print_context())) e) e.epos; (* api *) @@ -2238,7 +2335,7 @@ class tclass_to_jvm gctx c = object(self) | None -> () end else begin - let _,_,cf_super = raw_class_field (fun cf -> cf.cf_type) c_sup (List.map snd c_sup.cl_params) cf.cf_name in + let _,_,cf_super = raw_class_field (fun cf -> cf.cf_type) c_sup (extract_param_types c_sup.cl_params) cf.cf_name in compare_fields cf cf_super end in @@ -2279,7 +2376,7 @@ class tclass_to_jvm gctx c = object(self) let _,load,_ = jm_empty_ctor#add_local "_" haxe_empty_constructor_sig VarArgument in jm_empty_ctor#load_this; if c.cl_constructor = None then begin - let handler = new texpr_to_jvm gctx jc jm_empty_ctor None in + let handler = new texpr_to_jvm gctx None jc jm_empty_ctor None in DynArray.iter (fun e -> handler#texpr RVoid e; ) field_inits; @@ -2294,7 +2391,7 @@ class tclass_to_jvm gctx c = object(self) jm_empty_ctor#call_super_ctor ConstructInit jsig_empty end; if c.cl_constructor = None then begin - let handler = new texpr_to_jvm gctx jc jm_empty_ctor None in + let handler = new texpr_to_jvm gctx None jc jm_empty_ctor None in DynArray.iter (fun e -> handler#texpr RVoid e; ) delayed_field_inits; @@ -2307,7 +2404,7 @@ class tclass_to_jvm gctx c = object(self) PMap.iter (fun _ (c,cf) -> let cmode = get_construction_mode c cf in let jm = jc#spawn_method (if cmode = ConstructInit then "" else "new") (jsignature_of_type gctx cf.cf_type) [MPublic] in - let handler = new texpr_to_jvm gctx jc jm None in + let handler = new texpr_to_jvm gctx None jc jm None in jm#load_this; DynArray.iter (fun e -> handler#texpr RVoid e; @@ -2326,16 +2423,22 @@ class tclass_to_jvm gctx c = object(self) with Not_found -> () - method generate_expr gctx jc jm e is_method scmode mtype = + method generate_expr gctx field_info jc jm e scmode mtype = let e,args,tr = match e.eexpr with - | TFunction tf when is_method -> + | TFunction tf -> tf.tf_expr,tf.tf_args,(return_of_type gctx tf.tf_type) | _ -> e,[],None in - let handler = new texpr_to_jvm gctx jc jm tr in + let handler = new texpr_to_jvm gctx field_info jc jm tr in List.iter (fun (v,_) -> - ignore(handler#add_local v VarArgument); + let slot,_,_ = handler#add_local v VarArgument in + let annot = AnnotationHandler.convert_annotations v.v_meta in + match annot with + | [] -> + () + | _ -> + jm#add_argument_annotation slot annot; ) args; jm#finalize_arguments; begin match mtype with @@ -2362,7 +2465,6 @@ class tclass_to_jvm gctx c = object(self) handler#texpr RReturn e method generate_method gctx jc c mtype cf = - gctx.current_field_info <- gctx.preprocessor#get_field_info cf.cf_meta; let jsig = jsignature_of_type gctx cf.cf_type in let flags = if Meta.has Meta.Private cf.cf_meta then [MPrivate] else if Meta.has Meta.Protected cf.cf_meta then [MProtected] else [MPublic] in let flags = if (has_class_flag c CInterface) then MAbstract :: flags else flags in @@ -2386,14 +2488,15 @@ class tclass_to_jvm gctx c = object(self) begin match cf.cf_expr with | None -> () | Some e -> - self#generate_expr gctx jc jm e true scmode mtype; + let field_info = gctx.preprocessor#get_field_info cf.cf_meta in + self#generate_expr gctx field_info jc jm e scmode mtype; end; begin match cf.cf_params with | [] when c.cl_params = [] -> () | _ -> - let stl = String.concat "" (List.map (fun (n,_) -> - Printf.sprintf "%s:Ljava/lang/Object;" n + let stl = String.concat "" (List.map (fun tp -> + Printf.sprintf "%s:Ljava/lang/Object;" tp.ttp_name ) cf.cf_params) in let ssig = generate_method_signature true (jsignature_of_type gctx cf.cf_type) in let s = if cf.cf_params = [] then ssig else Printf.sprintf "<%s>%s" stl ssig in @@ -2407,6 +2510,7 @@ class tclass_to_jvm gctx c = object(self) let flags = if Meta.has Meta.Private cf.cf_meta then [FdPrivate] else if Meta.has Meta.Protected cf.cf_meta then [FdProtected] else [FdPublic] in let flags = if mtype = MStatic then FdStatic :: flags else flags in let flags = if Meta.has Meta.JvmSynthetic cf.cf_meta then FdSynthetic :: flags else flags in + let flags = if Meta.has Meta.Volatile cf.cf_meta then FdVolatile :: flags else flags in let jm = jc#spawn_field cf.cf_name jsig flags in let default e = let p = null_pos in @@ -2427,7 +2531,7 @@ class tclass_to_jvm gctx c = object(self) default e; end; | Some e when mtype <> MStatic -> - let tl = List.map snd c.cl_params in + let tl = extract_param_types c.cl_params in let ethis = mk (TConst TThis) (TInst(c,tl)) null_pos in let efield = mk (TField(ethis,FInstance(c,tl,cf))) cf.cf_type null_pos in let eop = mk (TBinop(OpAssign,efield,e)) cf.cf_type null_pos in @@ -2448,9 +2552,17 @@ class tclass_to_jvm gctx c = object(self) | _ -> default e; end; - let ssig = generate_signature true (jsignature_of_type gctx cf.cf_type) in - let offset = jc#get_pool#add_string ssig in - jm#add_attribute (AttributeSignature offset) + let jsig = jsignature_of_type gctx cf.cf_type in + (* see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4 *) + begin match jsig with + | TObject _ | TArray _ | TTypeParameter _ -> + let ssig = generate_signature true jsig in + let offset = jc#get_pool#add_string ssig in + jm#add_attribute (AttributeSignature offset); + | _ -> + () + end; + AnnotationHandler.generate_annotations (jm :> JvmBuilder.base_builder) cf.cf_meta; method generate_main e = let jsig = method_sig [array_sig string_sig] None in @@ -2461,14 +2573,14 @@ class tclass_to_jvm gctx c = object(self) jm#putstatic (["haxe";"root"],"Sys") "_args" (TArray(string_sig,None)) end; jm#invokestatic (["haxe"; "java"], "Init") "init" (method_sig [] None); - self#generate_expr gctx jc jm e true SCNone MStatic; + self#generate_expr gctx None jc jm e SCNone MStatic; if not jm#is_terminated then jm#return method private generate_fields = let field mtype cf = match cf.cf_kind with | Method (MethNormal | MethInline) -> List.iter (fun cf -> - self#generate_method gctx jc c mtype cf + if not (has_class_field_flag cf CfExtern) then self#generate_method gctx jc c mtype cf ) (cf :: List.filter (fun cf -> has_class_field_flag cf CfOverload) cf.cf_overloads) | _ -> if not (has_class_flag c CInterface) && is_physical_field cf then self#generate_field gctx jc c mtype cf @@ -2489,13 +2601,13 @@ class tclass_to_jvm gctx c = object(self) () | Some e -> let jm = jc#get_static_init_method in - let handler = new texpr_to_jvm gctx jc jm None in + let handler = new texpr_to_jvm gctx None jc jm None in handler#texpr RReturn (mk_block e); end method private generate_signature = - jc#set_type_parameters (List.map (fun (n,t) -> - let jsigs = match follow t with + jc#set_type_parameters (List.map (fun tp -> + let jsigs = match follow tp.ttp_type with | TInst({cl_kind = KTypeParameter tl},_) -> List.map (fun t -> get_boxed_type (jsignature_of_type gctx t) @@ -2503,7 +2615,7 @@ class tclass_to_jvm gctx c = object(self) | _ -> [] in - (n,jsigs) + (tp.ttp_name,jsigs) ) c.cl_params); match c.cl_super with | Some(c,tl) -> jc#set_super_parameters (List.map (jtype_argument_of_type gctx []) tl) @@ -2541,7 +2653,7 @@ let generate_class gctx c = let generate_enum_equals gctx (jc_ctor : JvmClass.builder) = let jm_equals,load = generate_equals_function jc_ctor (haxe_enum_sig object_sig) in let code = jm_equals#get_code in - let jm_equals_handler = new texpr_to_jvm gctx jc_ctor jm_equals (Some TBool) in + let jm_equals_handler = new texpr_to_jvm gctx None jc_ctor jm_equals (Some TBool) in let is_haxe_enum jsig = match jsig with | TObject(path,_) -> Hashtbl.mem gctx.enum_paths path @@ -2695,7 +2807,7 @@ let generate_enum gctx en = () | Some e -> ignore(jc_enum#spawn_field "__meta__" object_sig [FdStatic;FdPublic]); - let handler = new texpr_to_jvm gctx jc_enum jm_clinit None in + let handler = new texpr_to_jvm gctx None jc_enum jm_clinit None in handler#texpr rvalue_any e; jm_clinit#putstatic jc_enum#get_this_path "__meta__" object_sig end; @@ -2806,6 +2918,29 @@ module Preprocessor = struct end else if fst mt.mt_path = [] then mt.mt_path <- make_root mt.mt_path + let check_single_method_interface gctx c = + let rec loop m l = match l with + | [] -> + m + | cf :: l -> + if not (has_class_field_flag cf CfDefault) then begin match m with + | None -> + loop (Some cf) l + | Some _ -> + None + end else + loop m l + in + match loop None c.cl_ordered_fields with + | None -> + () + | Some cf -> + match jsignature_of_type gctx cf.cf_type with + | TMethod(args,ret) -> + JvmFunctions.JavaFunctionalInterfaces.add args ret c.cl_path cf.cf_name (List.map extract_param_name (c.cl_params @ cf.cf_params)); + | _ -> + () + let preprocess gctx = let rec has_runtime_meta = function | (Meta.Custom s,_,_) :: _ when String.length s > 0 && s.[0] <> ':' -> @@ -2835,6 +2970,7 @@ module Preprocessor = struct match mt with | TClassDecl c -> if not (has_class_flag c CInterface) then gctx.preprocessor#preprocess_class c + else check_single_method_interface gctx c; | _ -> () ) gctx.com.types; (* find typedef-interface implementations *) @@ -2852,31 +2988,38 @@ let generate jvm_flag com = | Some (jarname,cl,expr) -> jarname, Some (cl,expr) | None -> "jar",None in - let jar_dir,jar_path = if jvm_flag then begin + let compression_level = try + int_of_string (Define.defined_value com.defines Define.JvmCompressionLevel) + with _ -> + 6 + in + if compression_level < 0 || compression_level > 9 then failwith "Invalid value for -D jvm.compression-level: Must be >=0 and <= 9"; + let create_jar path = + new jar_output path compression_level + in + let out_dir,out = if jvm_flag then begin match path.file_name with | Some _ -> begin match path.directory with | None -> - "./","./" ^ com.file + "./",create_jar ("./" ^ com.file) | Some dir -> mkdir_from_path dir; - add_trailing_slash dir,com.file + add_trailing_slash dir,create_jar com.file end - | None -> - failwith "Please specify an output file name" + | None -> match path.directory with + | Some dir -> + let dir = add_trailing_slash dir in + dir,new file_output dir + | None -> + failwith "Please specify an output file name" end else begin let jar_name = if com.debug then jar_name ^ "-Debug" else jar_name in let jar_dir = add_trailing_slash com.file in let jar_path = Printf.sprintf "%s%s.jar" jar_dir jar_name in - jar_dir,jar_path + jar_dir,create_jar jar_path end in let anon_identification = new tanon_identification haxe_dynamic_object_path in - let compression_level = try - int_of_string (Define.defined_value com.defines Define.JvmCompressionLevel) - with _ -> - 6 - in - if compression_level < 0 || compression_level > 9 then failwith "Invalid value for -D jvm.compression-level: Must be >=0 and <= 9"; let dynamic_level = try int_of_string (Define.defined_value com.defines Define.JvmDynamicLevel) with _ -> @@ -2885,7 +3028,7 @@ let generate jvm_flag com = if dynamic_level < 0 || dynamic_level > 2 then failwith "Invalid value for -D jvm.dynamic-level: Must be >=0 and <= 2"; let gctx = { com = com; - jar = Zip.open_out jar_path; + out = out; t_runtime_exception = TInst(resolve_class com (["java";"lang"],"RuntimeException"),[]); entry_point = entry_point; t_exception = TInst(resolve_class com (["java";"lang"],"Exception"),[]); @@ -2896,23 +3039,21 @@ let generate jvm_flag com = typed_functions = new JvmFunctions.typed_functions; closure_paths = Hashtbl.create 0; enum_paths = Hashtbl.create 0; - current_field_info = None; default_export_config = { export_debug = true; }; - detail_times = Common.Define.raw_defined com.defines "jvm-times"; + detail_times = Common.raw_defined com "jvm_times"; timer = new Timer.timer ["generate";"java"]; jar_compression_level = compression_level; dynamic_level = dynamic_level; } in - gctx.anon_identification <- anon_identification; gctx.preprocessor <- new preprocessor com.basic (jsignature_of_type gctx); gctx.typedef_interfaces <- new typedef_interfaces gctx.preprocessor#get_infos anon_identification; gctx.typedef_interfaces#add_interface_rewrite (["haxe";"root"],"Iterator") (["java";"util"],"Iterator") true; let class_paths = ExtList.List.filter_map (fun java_lib -> if java_lib#has_flag NativeLibraries.FlagIsStd || java_lib#has_flag FlagIsExtern then None else begin - let dir = Printf.sprintf "%slib/" jar_dir in + let dir = Printf.sprintf "%slib/" out_dir in Path.mkdir_from_path dir; let name = FilePath.name_and_extension (FilePath.parse java_lib#get_file_path) in let ch_in = open_in_bin java_lib#get_file_path in @@ -2926,7 +3067,7 @@ let generate jvm_flag com = ) com.native_libs.java_libs in Hashtbl.iter (fun name v -> let filename = Codegen.escape_res_name name true in - Zip.add_entry ~level:gctx.jar_compression_level v gctx.jar filename; + gctx.out#add_entry v filename; ) com.resources; let generate_real_types () = List.iter (generate_module_type gctx) com.types; @@ -2947,6 +3088,5 @@ let generate jvm_flag com = (match class_paths with [] -> "" | _ -> "\nClass-Path: " ^ (String.concat " " class_paths)) ^ "\n\n" in - Zip.add_entry ~level:gctx.jar_compression_level manifest_content gctx.jar "META-INF/MANIFEST.MF"; - - Zip.close_out gctx.jar + gctx.out#add_entry manifest_content "META-INF/MANIFEST.MF"; + gctx.out#close; diff --git a/src/generators/genlua.ml b/src/generators/genlua.ml index 866b15e2..89071b72 100644 --- a/src/generators/genlua.ml +++ b/src/generators/genlua.ml @@ -56,6 +56,7 @@ type object_store = { mutable os_fields : object_store list; } +let replace_float_separators s = Texpr.replace_separators s "" let debug_expression expression = " --[[ " ^ Type.s_expr_kind expression ^ " --]] " @@ -76,7 +77,7 @@ let get_exposed ctx path meta = try (match args with | [ EConst (String(s,_)), _ ] -> [s] | [] -> [path] - | _ -> error "Invalid @:expose parameters" pos) + | _ -> typing_error "Invalid @:expose parameters" pos) with Not_found -> [] let dot_path = Globals.s_type_path @@ -158,7 +159,7 @@ let println ctx = newline ctx end) -let unsupported p = error "This expression cannot be compiled to Lua" p +let unsupported p = typing_error "This expression cannot be compiled to Lua" p let basename path = try @@ -221,8 +222,15 @@ let this ctx = match ctx.in_value with None -> "self" | Some _ -> "self" let is_dot_access e cf = match follow(e.etype), cf with - | TInst (c,_), FInstance(_,_,icf) when (Meta.has Meta.LuaDotMethod c.cl_meta || Meta.has Meta.LuaDotMethod icf.cf_meta)-> - true; + | TInst (c, _), FInstance(_, _, icf) -> (match icf.cf_kind with + | Var _ -> + true + | Method _ when Meta.has Meta.LuaDotMethod c.cl_meta -> + true + | Method _ when Meta.has Meta.LuaDotMethod icf.cf_meta -> + true + | Method _ -> + false) | _ -> false @@ -324,7 +332,7 @@ let rec extract_expr e = match e.eexpr with let gen_constant ctx p = function | TInt i -> print ctx "%ld" i - | TFloat s -> spr ctx s + | TFloat s -> spr ctx (replace_float_separators s) | TString s -> begin add_feature ctx "use.string"; print ctx "\"%s\"" (s_escape_lua s) @@ -344,7 +352,7 @@ let rec is_function_type t = and gen_argument ?(reflect=false) ctx e = begin match e.eexpr with - | TField (x,((FInstance (_,_,f)| FAnon(f) | FClosure(_,f)))) when (is_function_type e.etype) -> + | TField (x, ((FInstance (_, _, f) | FAnon(f) | FClosure(_,f)) as i)) when ((is_function_type e.etype) && (not(is_dot_access x i))) -> ( if reflect then ( add_feature ctx "use._hx_funcToField"; @@ -376,7 +384,7 @@ and gen_call ctx e el = (match e.eexpr , el with | TConst TSuper , params -> (match ctx.current.cl_super with - | None -> error "Missing api.setCurrentClass" e.epos + | None -> typing_error "Missing api.setCurrentClass" e.epos | Some (c,_) -> print ctx "%s.super(%s" (ctx.type_accessor (TClassDecl c)) (this ctx); List.iter (fun p -> print ctx ","; gen_argument ctx p) params; @@ -384,7 +392,7 @@ and gen_call ctx e el = ); | TField ({ eexpr = TConst TSuper },f) , params -> (match ctx.current.cl_super with - | None -> error "Missing api.setCurrentClass" e.epos + | None -> typing_error "Missing api.setCurrentClass" e.epos | Some (c,_) -> let name = field_name f in print ctx "%s.prototype%s(%s" (ctx.type_accessor (TClassDecl c)) (field name) (this ctx); @@ -437,7 +445,7 @@ and gen_call ctx e el = if List.length(fields) > 0 then incr count; | { eexpr = TConst(TNull)} -> () | _ -> - error "__lua_table__ only accepts array or anonymous object arguments" e.epos; + typing_error "__lua_table__ only accepts array or anonymous object arguments" e.epos; )) el; spr ctx "})"; | TIdent "__lua__", [{ eexpr = TConst (TString code) }] -> @@ -557,20 +565,26 @@ and gen_cond ctx cond = gen_value ctx cond; ctx.iife_assign <- false -and gen_loop ctx label cond e = +and gen_loop ctx cond do_while e = let old_in_loop = ctx.in_loop in ctx.in_loop <- true; let old_handle_continue = ctx.handle_continue in let will_continue = has_continue e in ctx.handle_continue <- has_continue e; ctx.break_depth <- ctx.break_depth + 1; - if will_continue then begin + if will_continue then println ctx "local _hx_continue_%i = false;" ctx.break_depth; - end; + if do_while then + println ctx "local _hx_do_first_%i = true;" ctx.break_depth; let b = open_block ctx in - print ctx "%s " label; + print ctx "while "; gen_cond ctx cond; + if do_while then + print ctx " or _hx_do_first_%i" ctx.break_depth; print ctx " do "; + if do_while then + newline ctx; + println ctx "_hx_do_first_%i = false;" ctx.break_depth; if will_continue then print ctx "repeat "; gen_block_element ctx e; if will_continue then begin @@ -626,7 +640,7 @@ and check_multireturn_param ctx t pos = match t with TAbstract(_,p) | TInst(_,p) -> if List.exists ttype_multireturn p then - error "MultiReturns must not be type parameters" pos + typing_error "MultiReturns must not be type parameters" pos else () | _ -> @@ -716,6 +730,8 @@ and gen_expr ?(local=true) ctx e = begin spr ctx (id ^ "_" ^ (ident v.v_name) ^ "_" ^ (field_name f)); | _ -> Globals.die "" __LOC__); + | TField (_, (FStatic ({cl_path = [],""},_) as f)) -> + spr ctx (ident (field_name f)) | TField (x,f) -> gen_value ctx x; let name = field_name f in @@ -944,11 +960,9 @@ and gen_expr ?(local=true) ctx e = begin gen_value ctx e; spr ctx (Ast.s_unop op) | TWhile (cond,e,Ast.NormalWhile) -> - gen_loop ctx "while" cond e + gen_loop ctx cond false e; | TWhile (cond,e,Ast.DoWhile) -> - gen_block_element ctx e; - newline ctx; - gen_loop ctx "while" cond e + gen_loop ctx cond true e; | TObjectDecl [] -> spr ctx "_hx_e()"; ctx.separator <- true @@ -1182,6 +1196,9 @@ and gen_value ctx e = gen_expr ctx e | TMeta (_,e1) -> gen_value ctx e1 + | TCall ({eexpr = (TField (e, (FInstance _ as s)))}, el) when (is_string_expr e) -> + spr ctx ("String.prototype." ^ (field_name s)); + gen_paren_arguments ctx (e :: el) | TCall (e,el) -> gen_call ctx e el | TReturn _ @@ -1262,10 +1279,14 @@ and gen_value ctx e = and gen_tbinop ctx op e1 e2 = (match op, e1.eexpr, e2.eexpr with - | Ast.OpAssign, TField(e3, FInstance _), TFunction f -> + | Ast.OpAssign, TField(e3, (FInstance _ as ci)), TFunction f -> gen_expr ctx e1; spr ctx " = " ; - print ctx "function(%s) " (String.concat "," ("self" :: List.map ident (List.map arg_name f.tf_args))); + let fn_args = List.map ident (List.map arg_name f.tf_args) in + print ctx "function(%s) " (String.concat "," + (if is_dot_access e3 ci + then fn_args + else "self" :: fn_args)); let fblock = fun_block ctx f e1.epos in (match fblock.eexpr with | TBlock el -> @@ -1297,14 +1318,28 @@ and gen_tbinop ctx op e1 e2 = gen_value ctx e1; spr ctx " = "; gen_value ctx e3; - | TField(e3, (FInstance _| FClosure _ | FAnon _ ) ), TField(e4, (FClosure _| FStatic _ | FAnon _) ) -> + | TField(e3, (FClosure _ | FAnon _)), TField(e4, (FClosure _ | FStatic _ | FAnon _)) -> + gen_value ctx e1; + print ctx " %s " (Ast.s_binop op); + add_feature ctx "use._hx_funcToField"; + spr ctx "_hx_funcToField("; + gen_value ctx e2; + spr ctx ")"; + | TField(e3, (FInstance _ as lhs)), TField(e4, (FInstance _ as rhs)) when not (is_dot_access e3 lhs) && (is_dot_access e4 rhs) -> gen_value ctx e1; print ctx " %s " (Ast.s_binop op); add_feature ctx "use._hx_funcToField"; spr ctx "_hx_funcToField("; gen_value ctx e2; spr ctx ")"; - | TField(_, FInstance _ ), TLocal t when (is_function_type t.v_type) -> + | TField(e3, (FInstance _ as ci)), TField(e4, (FClosure _ | FStatic _ | FAnon _)) when not (is_dot_access e3 ci) -> + gen_value ctx e1; + print ctx " %s " (Ast.s_binop op); + add_feature ctx "use._hx_funcToField"; + spr ctx "_hx_funcToField("; + gen_value ctx e2; + spr ctx ")"; + | TField(e3, (FInstance _ as ci)), TLocal t when ((is_function_type t.v_type) && (not (is_dot_access e3 ci))) -> gen_value ctx e1; print ctx " %s " (Ast.s_binop op); add_feature ctx "use._hx_funcToField"; @@ -1441,7 +1476,7 @@ and gen_return ctx e eo = spr ctx "do return end" | Some e -> (match e.eexpr with - | TField (e2, ((FClosure (_, tcf) | FAnon tcf |FInstance (_,_,tcf)))) when (is_function_type tcf.cf_type) -> + | TField (e2, ((FAnon tcf | FInstance (_,_,tcf)) as t)) when ((is_function_type tcf.cf_type) && (not(is_dot_access e2 t)))-> (* See issue #6259 *) add_feature ctx "use._hx_bind"; spr ctx "do return "; @@ -1484,20 +1519,20 @@ let check_multireturn ctx c = match c with | _ when Meta.has Meta.MultiReturn c.cl_meta -> if not (has_class_flag c CExtern) then - error "MultiReturns must be externs" c.cl_pos + typing_error "MultiReturns must be externs" c.cl_pos else if List.length c.cl_ordered_statics > 0 then - error "MultiReturns must not contain static fields" c.cl_pos + typing_error "MultiReturns must not contain static fields" c.cl_pos else if (List.exists (fun cf -> match cf.cf_kind with Method _ -> true | _-> false) c.cl_ordered_fields) then - error "MultiReturns must not contain methods" c.cl_pos; + typing_error "MultiReturns must not contain methods" c.cl_pos; | {cl_super = Some(csup,_)} when Meta.has Meta.MultiReturn csup.cl_meta -> - error "Cannot extend a MultiReturn" c.cl_pos + typing_error "Cannot extend a MultiReturn" c.cl_pos | _ -> () let check_field_name c f = match f.cf_name with | "prototype" | "__proto__" | "constructor" -> - error ("The field name '" ^ f.cf_name ^ "' is not allowed in Lua") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos); + typing_error ("The field name '" ^ f.cf_name ^ "' is not allowed in Lua") (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos); | _ -> () (* convert a.b.c to ["a"]["b"]["c"] *) @@ -1585,7 +1620,7 @@ let generate_class ctx c = ctx.current <- c; ctx.id_counter <- 0; (match c.cl_path with - | [],"Function" -> error "This class redefines a native one" c.cl_pos + | [],"Function" -> typing_error "This class redefines a native one" c.cl_pos | _ -> ()); let p = s_path ctx c.cl_path in let hxClasses = has_feature ctx "Type.resolveClass" in @@ -1783,7 +1818,7 @@ let generate_require ctx path meta = | [(EConst(String(module_name,_)),_) ; (EConst(String(object_path,_)),_)] -> print ctx "%s = _G.require(\"%s\").%s" p module_name object_path | _ -> - error "Unsupported @:luaRequire format" mp); + typing_error "Unsupported @:luaRequire format" mp); newline ctx @@ -1798,7 +1833,7 @@ let generate_type ctx = function if p = "Std" && c.cl_ordered_statics = [] then () else if (not (has_class_flag c CExtern)) && Meta.has Meta.LuaDotMethod c.cl_meta then - error "LuaDotMethod is valid for externs only" c.cl_pos + typing_error "LuaDotMethod is valid for externs only" c.cl_pos else if not (has_class_flag c CExtern) then generate_class ctx c; check_multireturn ctx c; @@ -1850,7 +1885,7 @@ let alloc_ctx com = lua_jit = Common.defined com Define.LuaJit; lua_vanilla = Common.defined com Define.LuaVanilla; lua_ver = try - float_of_string (PMap.find "lua_ver" com.defines.Define.values) + float_of_string (Common.defined_value com Define.LuaVer) with | Not_found -> 5.2; } in ctx.type_accessor <- (fun t -> @@ -1911,7 +1946,7 @@ let transform_multireturn ctx = function e | TReturn Some(e2) -> if is_multireturn e2.etype then - error "You cannot return a multireturn type from a haxe function" e2.epos + typing_error "You cannot return a multireturn type from a haxe function" e2.epos else Type.map_expr loop e; (* @@ -2057,6 +2092,9 @@ let generate com = println ctx "_hx_array_mt.__index = Array.prototype"; newline ctx; + (* Functions to support auto-run of libuv loop *) + print_file (Common.find_file com "lua/_lua/_hx_luv.lua"); + let b = open_block ctx in (* Localize init variables inside a do-block *) println ctx "local _hx_static_init = function()"; @@ -2108,27 +2146,28 @@ let generate com = print_file (Common.find_file com "lua/_lua/_hx_dyn_add.lua"); end; + print_file (Common.find_file com "lua/_lua/_hx_handle_error.lua"); + println ctx "_hx_static_init();"; List.iter (generate_enumMeta_fields ctx) com.types; Option.may (fun e -> - spr ctx "_G.xpcall("; - (match e.eexpr with - | TCall(e2,[]) -> - gen_value ctx e2; - | _-> + spr ctx "local success, err = _G.xpcall("; + let luv_run = + (* Runs libuv loop if needed *) + mk_lua_code ctx.com.basic "_hx_luv.run()" [] ctx.com.basic.tvoid Globals.null_pos + in let fn = { tf_args = []; tf_type = com.basic.tvoid; - tf_expr = mk (TBlock [e]) com.basic.tvoid e.epos; + tf_expr = mk (TBlock [e;luv_run]) com.basic.tvoid e.epos; } in - gen_value ctx { e with eexpr = TFunction fn; etype = TFun ([],com.basic.tvoid) } - ); - spr ctx ", _hx_error)"; - newline ctx + gen_value ctx { e with eexpr = TFunction fn; etype = TFun ([],com.basic.tvoid) }; + println ctx ", _hx_handle_error)"; + println ctx "if not success then _G.error(err) end"; ) com.main; if anyExposed then @@ -2137,4 +2176,3 @@ let generate com = let ch = open_out_bin com.file in output_string ch (Buffer.contents ctx.buf); close_out ch - diff --git a/src/generators/genneko.ml b/src/generators/genneko.ml index cd085792..e8a07a98 100644 --- a/src/generators/genneko.ml +++ b/src/generators/genneko.ml @@ -172,7 +172,7 @@ let gen_constant ctx pe c = with _ -> if ctx.version < 2 then abort "This integer is too big to be compiled to a Neko 31-bit integer. Please use a Float instead" pe; (EConst (Int32 i),p)) - | TFloat f -> (EConst (Float f),p) + | TFloat f -> (EConst (Float (Texpr.replace_separators f "")),p) | TString s -> call p (field p (ident p "String") "new") [gen_big_string ctx p s] | TBool b -> (EConst (if b then True else False),p) | TNull -> null p @@ -662,21 +662,16 @@ let generate_libs_init = function | libs -> (* var @s = $loader.loadprim("std@sys_string",0)(); - var @env = $loader.loadprim("std@get_env",1); - var @b = if( @s == "Windows" ) - @env("HAXEPATH") + "\\lib\\" - else try $loader.loadprim("std@file_contents",1)(@env("HOME")+"/.haxelib") + "/" - catch e - if( @s == "Linux" ) - if( $loader(loadprim("std@sys_exists",1))("/usr/lib/haxe/lib") ) - "/usr/lib/haxe/lib" - else - "/usr/share/haxe/lib/" - else - "/usr/local/lib/haxe/lib/"; - if( try $loader.loadprim("std@sys_file_type",1)(".haxelib") == "dir" catch e false ) @b = $loader.loadprim("std@file_full_path",1)(".haxelib") + "/"; - if( $loader.loadprim("std@sys_is64",0)() ) @s = @s + 64; - @b = @b + "/" + if( $version() >= 240 ) + @s = @s + switch $loader.loadprim("std@sys_cpu_arch",0)() { + "arm64" => "Arm64" + "arm" => "Arm" + "x86_64" => "64" + default => "" + }; + else if( $loader.loadprim("std@sys_is64",0)() ) + @s = @s + 64; + @s = @s + "/"; *) let p = null_pos in let es = ident p "@s" in @@ -689,34 +684,25 @@ let generate_libs_init = function let boot = [ (EVars [ "@s",Some (call p (loadp "sys_string" 0) []); - "@env",Some (loadp "get_env" 1); - "@b", Some (EIf (op "==" es (str p "Windows"), - op "+" (call p (ident p "@env") [str p "HAXEPATH"]) (str p "\\lib\\"), - Some (ETry ( - op "+" (call p (loadp "file_contents" 1) [op "+" (call p (ident p "@env") [str p "HOME"]) (str p "/.haxelib")]) (str p "/"), - "e", - (EIf (op "==" es (str p "Linux"), - (EIf (call p (loadp "sys_exists" 1) [ str p "/usr/lib/haxe/lib" ], - str p "/usr/lib/haxe/lib/", - Some (str p "/usr/share/haxe/lib/")),p), - Some (str p "/usr/local/lib/haxe/lib/") - ),p) - ),p) - ),p); ],p); - (EIf ((ETry (op "==" (call p (loadp "sys_file_type" 1) [str p ".haxelib"]) (str p "dir"),"e",(EConst False,p)),p),op "=" (ident p "@b") (op "+" (call p (loadp "file_full_path" 1) [str p ".haxelib"]) (str p "/")), None),p); - (EIf (call p (loadp "sys_is64" 0) [],op "=" es (op "+" es (int p 64)),None),p); + (EIf (op ">=" (builtin p "version") (int p 240), + (op "=" es (op "+" es (ESwitch (call p (loadp "sys_cpu_arch" 0) [],[ + (str p "arm64", str p "Arm64"); + (str p "arm", str p "Arm"); + (str p "x86_64", str p "64"); + ], Some (str p "")),p))), + Some (EIf (call p (loadp "sys_is64" 0) [],op "=" es (op "+" es (int p 64)),None),p) + ),p); op "=" es (op "+" es (str p "/")); ] in let lpath = field p (builtin p "loader") "path" in boot @ List.map (fun dir -> - let full_path = dir.[0] = '/' || dir.[1] = ':' in let dstr = str p dir in (* // for each lib dir - $loader.path = $array($loader.path,@b+dir+@s); + $loader.path = $array(dir+@s,$loader.path); *) - op "=" lpath (call p (builtin p "array") [op "+" (if full_path then dstr else op "+" (ident p "@b") dstr) (ident p "@s"); lpath]) + op "=" lpath (call p (builtin p "array") [op "+" dstr (ident p "@s"); lpath]) ) libs let new_context com ver macros = @@ -787,7 +773,11 @@ let build ctx types = let generate com = Hashtbl.clear files; let ctx = new_context com (if Common.defined com Define.NekoV1 then 1 else 2) false in - let libs = (EBlock (generate_libs_init com.neko_libs) , { psource = "
"; pline = 1; }) in + let libs = (EBlock + (if Common.defined com Define.NekoNoHaxelibPaths then [] + else generate_libs_init com.neko_lib_paths), + { psource = "
"; pline = 1; } + ) in let el = build ctx com.types in let emain = (match com.main with None -> [] | Some e -> [gen_expr ctx e]) in let e = (EBlock ((header()) @ libs :: el @ emain), null_pos) in @@ -810,16 +800,16 @@ let generate com = in abort msg (loop 0) end; - let command cmd = try com.run_command cmd with _ -> -1 in + let command cmd args = try com.run_command_args cmd args with _ -> -1 in let neko_file = (try Filename.chop_extension com.file with _ -> com.file) ^ ".neko" in if source || use_nekoc then begin let ch = IO.output_channel (open_out_bin neko_file) in Binast.write ch e; IO.close_out ch; end; - if use_nekoc && command ("nekoc" ^ (if ctx.version > 1 then " -version " ^ string_of_int ctx.version else "") ^ " \"" ^ neko_file ^ "\"") <> 0 then failwith "Neko compilation failure"; + if use_nekoc && command "nekoc" (if ctx.version > 1 then ["-version"; (string_of_int ctx.version); neko_file] else [neko_file]) <> 0 then failwith "Neko compilation failure"; if source then begin - if command ("nekoc -p \"" ^ neko_file ^ "\"") <> 0 then failwith "Failed to print neko code"; + if command "nekoc" ["-p"; neko_file] <> 0 then failwith "Failed to print neko code"; Sys.remove neko_file; Sys.rename ((try Filename.chop_extension com.file with _ -> com.file) ^ "2.neko") neko_file; end diff --git a/src/generators/genphp7.ml b/src/generators/genphp7.ml index 3db34186..25715db8 100644 --- a/src/generators/genphp7.ml +++ b/src/generators/genphp7.ml @@ -1141,13 +1141,13 @@ let type_name_used_in_namespace ctx type_path as_name namespace = List.iter (fun ctx_type -> let wrapper = get_wrapper ctx_type in - Hashtbl.add ctx.pgc_namespaces_types_cache wrapper#get_namespace wrapper#get_name + Hashtbl.add ctx.pgc_namespaces_types_cache wrapper#get_namespace (StringHelper.uppercase wrapper#get_name) ) ctx.pgc_common.types; Hashtbl.find_all ctx.pgc_namespaces_types_cache namespace | types -> types in - List.mem as_name types + List.mem (StringHelper.uppercase as_name) types && (namespace, as_name) <> type_path (** @@ -2387,57 +2387,9 @@ class code_writer (ctx:php_generator_context) hx_type_path php_name = match fields with | [] -> self#write ("new " ^ (self#use hxanon_type_path) ^ "()") | _ -> - let inits,args_exprs,args_names = - List.fold_left (fun (inits,args_exprs,args_names) ((name,p,quotes), e) -> - let field,arg_name = - if quotes = NoQuotes then name,name - else "{" ^ (quote_string name) ^ "}", "_hx_" ^ (string_of_int (List.length args_exprs)) - in - (field,mk (TIdent ("$"^arg_name)) e.etype p) :: inits, e :: args_exprs, arg_name :: args_names - ) ([],[],[]) fields - in - let anon_name, declare_class = - let key = List.map (fun ((name,_,_),_) -> name) fields in - try - Hashtbl.find ctx.pgc_anons key, false - with Not_found -> - let name = "_HxAnon_" ^ self#get_name ^ (string_of_int (Hashtbl.length ctx.pgc_anons)) in - Hashtbl.add ctx.pgc_anons key name; - name, true - in - self#write ("new " ^ anon_name ^ "("); - write_args self#write self#write_expr (List.rev args_exprs); - self#write ")"; - if declare_class then begin - (* save writer's state *) - let original_buffer = buffer - and original_indentation = self#get_indentation in - let sm_pointer_before_body = get_sourcemap_pointer sourcemap in - (* generate a class for this anon *) - buffer <- ctx.pgc_bottom_buffer; - self#set_indentation 0; - self#write ("\nclass " ^ anon_name ^ " extends " ^ (self#use hxanon_type_path) ^ " {\n"); - self#indent_more; - self#write_with_indentation "function __construct("; - write_args self#write (fun name -> self#write ("$" ^ name)) (List.rev args_names); - self#write ") {\n"; - self#indent_more; - List.iter (fun (field,e) -> - self#write_with_indentation "$this->"; - self#write field; - self#write " = "; - self#write_expr e; - self#write ";\n"; - ) (List.rev inits); - self#indent_less; - self#write_line "}"; - self#indent_less; - self#write_with_indentation "}\n"; - (* restore writer's state *) - buffer <- original_buffer; - self#set_indentation original_indentation; - set_sourcemap_pointer sourcemap sm_pointer_before_body - end + self#write ("new " ^ (self#use hxanon_type_path) ^ "("); + self#write_assoc_array_decl fields; + self#write ")" (** Writes specified type to output buffer depending on type of expression. *) @@ -3687,17 +3639,23 @@ class class_builder ctx (cls:tclass) = (* Generate `__toString()` if not defined by user, but has `toString()` *) self#write_toString_if_required method private write_toString_if_required = - if PMap.exists "toString" cls.cl_fields then + try + let toString = PMap.find "toString" cls.cl_fields in if (not (has_class_flag cls CInterface)) && (not (PMap.exists "__toString" cls.cl_statics)) && (not (PMap.exists "__toString" cls.cl_fields)) then begin writer#write_empty_lines; writer#indent 1; writer#write_line "public function __toString() {"; writer#indent_more; - writer#write_line "return $this->toString();"; + let callee_str = match toString.cf_kind with + | Var _ -> "($this->toString)" + | Method _ -> "$this->toString" + in + writer#write_line ("return " ^ callee_str ^ "();"); writer#indent_less; writer#write_line "}" end + with Not_found -> () (** Check if this class requires constructor to be generated even if there is no user-defined one *) @@ -3870,27 +3828,36 @@ class class_builder ctx (cls:tclass) = let (args, return_type) = get_function_signature field in List.iter (fun (arg_name, _, _) -> writer#declared_local_var arg_name) args; self#write_doc (DocMethod (args, return_type, (gen_doc_text_opt field.cf_doc))) field.cf_meta; - writer#write_with_indentation ((get_visibility field.cf_meta) ^ " function " ^ (field_name field)); + let visibility_kwd = get_visibility field.cf_meta in + writer#write_with_indentation (visibility_kwd ^ " function " ^ (field_name field)); (match field.cf_expr with | None -> (* interface *) writer#write " ("; write_args writer#write (writer#write_arg true) (fix_tsignature_args args); writer#write ");\n"; | Some { eexpr = TFunction fn } -> (* normal class *) - writer#write " ("; - write_args writer#write writer#write_function_arg (fix_tfunc_args fn.tf_args); - writer#write ")\n"; + let write_args() = + writer#write " ("; + write_args writer#write writer#write_function_arg (fix_tfunc_args fn.tf_args); + writer#write ")\n" + in + write_args(); writer#write_line "{"; writer#indent_more; writer#write_indentation; - let field_access = "$this->" ^ (field_name field) - and default_value = "$this->__hx__default__" ^ (field_name field) in - writer#write ("if (" ^ field_access ^ " !== " ^ default_value ^ ") return call_user_func_array(" ^ field_access ^ ", func_get_args());\n"); - writer#write_fake_block fn.tf_expr; + let field_access = "$this->" ^ (field_name field) in + writer#write ("return call_user_func_array(" ^ field_access ^ ", func_get_args());\n"); writer#indent_less; writer#write_line "}"; (* Don't forget to create a field for default value *) - writer#write_statement ("protected $__hx__default__" ^ (field_name field)) + writer#write_indentation; + writer#write (visibility_kwd ^ " function __hx__default__" ^ (field_name field)); + write_args(); + writer#write_line "{"; + writer#indent_more; + writer#write_fake_block fn.tf_expr; + writer#indent_less; + writer#write_line "}" | _ -> fail field.cf_pos __LOC__ ); (** @@ -3907,14 +3874,9 @@ class class_builder ctx (cls:tclass) = *) method private write_instance_initialization = let init_dynamic_method field = - let field_name = field_name field in - let default_field = "$this->__hx__default__" ^ field_name in - writer#write_line ("if (!" ^ default_field ^ ") {"); - writer#indent_more; - writer#write_statement (default_field ^ " = new " ^ (writer#use hxclosure_type_path) ^ "($this, '" ^ field_name ^ "')"); - writer#write_statement ("if ($this->" ^ field_name ^ " === null) $this->" ^ field_name ^ " = " ^ default_field); - writer#indent_less; - writer#write_line "}" + let field_name = field_name field + and hx_closure = writer#use hxclosure_type_path in + writer#write_statement ("if ($this->" ^ field_name ^ " === null) $this->" ^ field_name ^ " = new " ^ hx_closure ^ "($this, '__hx__default__" ^ field_name ^ "')"); in List.iter (fun field -> diff --git a/src/generators/genpy.ml b/src/generators/genpy.ml index 7f58398c..334616ca 100644 --- a/src/generators/genpy.ml +++ b/src/generators/genpy.ml @@ -69,7 +69,7 @@ module KeywordHandler = struct let h = Hashtbl.create 0 in List.iter (fun s -> Hashtbl.add h s ()) [ "and"; "as"; "assert"; "async"; "await"; "break"; "class"; "continue"; "def"; "del"; "elif"; "else"; "except"; "exec"; "finally"; "for"; - "from"; "global"; "if"; "import"; "in"; "is"; "lambda"; "not"; "or"; "pass"; " raise"; "return"; "try"; "while"; + "from"; "global"; "if"; "import"; "in"; "is"; "lambda"; "not"; "or"; "pass"; "raise"; "return"; "try"; "while"; "with"; "yield"; "None"; "True"; "False"; ]; h @@ -1081,7 +1081,7 @@ module Printer = struct | OpShr -> ">>" | OpUShr -> ">>" | OpMod -> "%" - | OpInterval | OpArrow | OpIn | OpAssignOp _ -> die "" __LOC__ + | OpInterval | OpArrow | OpIn | OpNullCoal | OpAssignOp _ -> die "" __LOC__ let print_string s = Printf.sprintf "\"%s\"" (StringHelper.s_escape s) @@ -1342,16 +1342,7 @@ module Printer = struct | TParenthesis e1 -> Printf.sprintf "(%s)" (print_expr pctx e1) | TObjectDecl fl -> - let fl2 = ref fl in - begin match follow e.etype with - | TAnon an -> - PMap.iter (fun s cf -> - if not (Expr.field_mem_assoc s fl) then fl2 := ((s,null_pos,NoQuotes),null cf.cf_type cf.cf_pos) :: !fl2 - ) an.a_fields - | _ -> - () - end; - Printf.sprintf "_hx_AnonObject(%s)" (print_exprs_named pctx ", " !fl2) + Printf.sprintf "_hx_AnonObject(%s)" (print_exprs_named pctx ", " fl) | TArrayDecl el -> Printf.sprintf "[%s]" (print_exprs pctx ", " el) | TCall(e1,el) -> @@ -1501,9 +1492,9 @@ module Printer = struct let interpolate () = Codegen.interpolate_code pctx.pc_com code tl (Buffer.add_string buf) (fun e -> Buffer.add_string buf (print_expr pctx e)) ecode.epos in - let old = pctx.pc_com.error in - pctx.pc_com.error <- abort; - Std.finally (fun() -> pctx.pc_com.error <- old) interpolate (); + let old = pctx.pc_com.located_error in + pctx.pc_com.located_error <- abort_located; + Std.finally (fun() -> pctx.pc_com.located_error <- old) interpolate (); Buffer.contents buf | ("python_Syntax._pythonCode"), [e] -> print_expr pctx e @@ -1891,7 +1882,7 @@ module Generator = struct let py_metas = filter_py_metas cf.cf_meta in begin match cf.cf_expr with | Some ({eexpr = TFunction f} as ef) -> - let ethis = mk (TConst TThis) (TInst(c,List.map snd c.cl_params)) cf.cf_pos in + let ethis = mk (TConst TThis) (TInst(c,extract_param_types c.cl_params)) cf.cf_pos in let assigned_fields = ref [] in (* Collect all fields that are assigned to but panic out as soon as `this`, `super`, `return` or `throw` appears (regardless of control flow). *) diff --git a/src/generators/genshared.ml b/src/generators/genshared.ml index f2dd5b1e..6816a5c2 100644 --- a/src/generators/genshared.ml +++ b/src/generators/genshared.ml @@ -141,7 +141,7 @@ object(self) begin try Some (Hashtbl.find pfms td.t_path) with Not_found -> - self#identify accept_anons (apply_params td.t_params tl td.t_type) + self#identify accept_anons (apply_typedef td tl) end | TMono {tm_type = Some t} -> self#identify accept_anons t @@ -195,7 +195,7 @@ module Info = struct method get_class_info (c : tclass) = let rec loop ml = match ml with - | (Meta.Custom ":jvm.classInfo",[(EConst (Int s),_)],_) :: _ -> + | (Meta.Custom ":jvm.classInfo",[(EConst (Int (s, _)),_)],_) :: _ -> DynArray.get class_infos (int_of_string s) | _ :: ml -> loop ml @@ -206,7 +206,7 @@ module Info = struct implicit_ctors = PMap.empty; } in DynArray.add class_infos infos; - c.cl_meta <- (Meta.Custom ":jvm.classInfo",[(EConst (Int (string_of_int index)),null_pos)],null_pos) :: c.cl_meta; + c.cl_meta <- (Meta.Custom ":jvm.classInfo",[(EConst (Int (string_of_int index, None)),null_pos)],null_pos) :: c.cl_meta; infos in loop c.cl_meta @@ -241,7 +241,7 @@ object(self) method get_field_info (ml : metadata) = let rec loop ml = match ml with - | (Meta.Custom ":jvm.fieldInfo",[(EConst (Int s),_)],_) :: _ -> + | (Meta.Custom ":jvm.fieldInfo",[(EConst (Int (s, _)),_)],_) :: _ -> Some (DynArray.get field_infos (int_of_string s)) | _ :: ml -> loop ml @@ -278,7 +278,7 @@ object(self) in loop csup; (c,cf) - | None -> Error.error "Could not find overload constructor" e.epos + | None -> Error.typing_error "Could not find overload constructor" e.epos in let find_super_ctor el = let _,cf = find_super_ctor el in @@ -367,7 +367,7 @@ object(self) let info = self#preprocess_constructor_expr c cf e in let index = DynArray.length field_infos in DynArray.add field_infos info; - cf.cf_meta <- (Meta.Custom ":jvm.fieldInfo",[(EConst (Int (string_of_int index)),null_pos)],null_pos) :: cf.cf_meta; + cf.cf_meta <- (Meta.Custom ":jvm.fieldInfo",[(EConst (Int (string_of_int index, None)),null_pos)],null_pos) :: cf.cf_meta; if not (Meta.has Meta.HxGen cf.cf_meta) then begin let rec loop next c = if (has_class_flag c CExtern) then make_native cf @@ -413,7 +413,10 @@ class ['a] typedef_interfaces (infos : 'a info_context) (anon_identification : ' method private implements (c : tclass) (path_interface : path) = let info = infos#get_class_info c in - match info.typedef_implements with + List.exists (fun (c,_) -> + c.cl_path = path_interface + ) c.cl_implements + || match info.typedef_implements with | None -> false | Some l -> @@ -424,7 +427,7 @@ class ['a] typedef_interfaces (infos : 'a info_context) (anon_identification : ' | Some (c,_) -> self#implements_recursively c path | None -> false - method private make_interface_class (pfm : 'a path_field_mapping) = + method private make_interface_class (pfm : 'a path_field_mapping) (path : path) (is_extern : bool) = let path_inner = (fst pfm.pfm_path,snd pfm.pfm_path ^ "$Interface") in try Hashtbl.find interfaces path_inner @@ -436,7 +439,6 @@ class ['a] typedef_interfaces (infos : 'a info_context) (anon_identification : ' acc ) pfm.pfm_fields PMap.empty in if PMap.is_empty fields then raise (Unify_error [Unify_custom "no fields"]); - let path,is_extern = try Hashtbl.find interface_rewrites pfm.pfm_path with Not_found -> path_inner,false in let c = mk_class null_module path null_pos null_pos in add_class_flag c CInterface; c.cl_fields <- fields; @@ -450,14 +452,17 @@ class ['a] typedef_interfaces (infos : 'a info_context) (anon_identification : ' | Some(c,_) -> self#process_class c | None -> () end; - let tc = TInst(c,List.map snd c.cl_params) in + let tc = TInst(c,extract_param_types c.cl_params) in + (* TODO: this entire architecture looks slightly retarded because typedef_implements is only modified at the end of the + loop, which I think could cause items to be missed. *) let l = Hashtbl.fold (fun _ pfm acc -> let path = pfm.pfm_path in let path_inner = (fst path,snd path ^ "$Interface") in try + let path_inner,is_extern = try Hashtbl.find interface_rewrites pfm.pfm_path with Not_found -> path_inner,false in if self#implements_recursively c path_inner then raise (Unify_error [Unify_custom "already implemented"]); anon_identification#unify tc pfm; - let ci = self#make_interface_class pfm in + let ci = self#make_interface_class pfm path_inner is_extern in c.cl_implements <- (ci,[]) :: c.cl_implements; (* print_endline (Printf.sprintf "%s IMPLEMENTS %s" (s_type_path c.cl_path) (s_type_path path_inner)); *) (ci :: acc) diff --git a/src/generators/genswf.ml b/src/generators/genswf.ml index 3480f106..8aabc82a 100644 --- a/src/generators/genswf.ml +++ b/src/generators/genswf.ml @@ -84,7 +84,7 @@ let build_dependencies t = | TAnon a -> PMap.iter (fun _ f -> add_type_rec (t::l) f.cf_type) a.a_fields | TDynamic t2 -> - add_type_rec (t::l) t2; + add_type_rec (t::l) (match t2 with None -> t_dynamic | Some t2 -> t2); | TLazy f -> add_type_rec l (lazy_type f) | TMono r -> @@ -148,9 +148,9 @@ let build_dependencies t = (match c.cl_super with | None -> add_path ([],"Object") DKInherit; | Some x -> add_inherit x); - List.iter (fun (_,t) -> + List.iter (fun tp -> (* add type-parameters constraints dependencies *) - match follow t with + match follow tp.ttp_type with | TInst (c,_) -> List.iter add_inherit c.cl_implements | _ -> () ) c.cl_params; @@ -343,7 +343,7 @@ let build_swf9 com file swc = (match h.Png.png_color with | Png.ClTrueColor (Png.TBits8,Png.NoAlpha) -> if h.Png.png_width * h.Png.png_height * 4 > Sys.max_string_length then begin - com.warning "Flash will loose some color information for this file, add alpha channel to preserve it" p; + com.warning WGenerator [] "Flash will loose some color information for this file, add alpha channel to preserve it" p; raise Exit; end; let data = Extc.unzip (Png.data png) in diff --git a/src/generators/genswf9.ml b/src/generators/genswf9.ml index 299dbb23..9bbd0ae6 100644 --- a/src/generators/genswf9.ml +++ b/src/generators/genswf9.ml @@ -220,7 +220,7 @@ let rec follow_basic t = | TType ({ t_path = [],"UInt" },[]) -> t | TType (t,tl) -> - follow_basic (apply_params t.t_params tl t.t_type) + follow_basic (apply_typedef t tl) | TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) -> follow_basic (apply_params a.a_params pl a.a_this) | _ -> t @@ -1426,12 +1426,12 @@ let rec gen_expr_content ctx retval e = end | TIdent s -> abort ("Unbound variable " ^ s) e.epos -and args_as_array ctx mandatory_args spread_arg = +and args_as_array ctx mandatory_args spread_arg p = match mandatory_args with | [] -> spread_arg | _ -> - let p = punion_el (List.map (fun e -> ((),e.epos)) mandatory_args) in + let p = punion_el p mandatory_args in let array = mk (TArrayDecl mandatory_args) (ctx.com.basic.tarray t_dynamic) p in let concat = mk (TField (array,FDynamic "concat")) t_dynamic spread_arg.epos in mk (TCall (concat,[spread_arg])) (ctx.com.basic.tarray t_dynamic) (punion p spread_arg.epos) @@ -1444,7 +1444,7 @@ and gen_call ctx retval e el r = and t_array_dyn = ctx.com.basic.tarray t_dynamic in let t = TFun (["thisArg",false,t_dynamic; "argArray",false,t_array_dyn],r) in let apply = mk (TField (e,FDynamic "apply")) t e.epos in - gen_call ctx retval apply [null; args_as_array ctx (List.rev el_rev) rest] r + gen_call ctx retval apply [null; args_as_array ctx (List.rev el_rev) rest e.epos] r (* normal call without `...rest` *) | _ -> match e.eexpr , el with @@ -1829,7 +1829,7 @@ and gen_binop ctx retval op e1 e2 t p = gen_op A3OLt | OpLte -> gen_op A3OLte - | OpInterval | OpArrow | OpIn -> + | OpInterval | OpArrow | OpIn | OpNullCoal -> die "" __LOC__ and gen_expr ctx retval e = @@ -2253,7 +2253,7 @@ let mk_instance_getter_func c tl accessor_cl accessor_tl accessor_cf prop_cf = } let maybe_gen_instance_getter ctx c f acc alloc_slot = - let tl = List.map snd c.cl_params in + let tl = extract_param_types c.cl_params in maybe_gen_instance_accessor ctx c tl f acc alloc_slot MK3Getter (mk_instance_getter_func c tl c tl f) (fun prop_cf -> ([],prop_cf.cf_type)) @@ -2274,7 +2274,7 @@ let mk_instance_setter_func com c tl accessor_cl accessor_tl accessor_cf prop_cf } let maybe_gen_instance_setter ctx c f acc alloc_slot = - let tl = List.map snd c.cl_params in + let tl = extract_param_types c.cl_params in maybe_gen_instance_accessor ctx c tl f acc alloc_slot MK3Setter (mk_instance_setter_func ctx.com c tl c tl f) (fun prop_cf -> ([(mk_varg prop_cf.cf_type,None)],ctx.com.basic.tvoid)) @@ -2376,7 +2376,7 @@ let realize_required_accessors ctx cl = | _ -> false in - let tl = List.map snd cl.cl_params in + let tl = extract_param_types cl.cl_params in let fields = ref [] in Hashtbl.iter (fun name (read, write, native) -> match Type.class_field cl tl name with @@ -2526,7 +2526,7 @@ let generate_class ctx c = if read = AccCall then begin try begin - let tl = List.map snd c.cl_params in + let tl = extract_param_types c.cl_params in match Type.class_field c tl ("get_" ^ f.cf_name) with | Some (actual_cl, actual_tl), _, getter_cf when actual_cl != c -> let func = mk_instance_getter_func c tl actual_cl actual_tl getter_cf f in @@ -2551,7 +2551,7 @@ let generate_class ctx c = if write = AccCall then begin try begin - let tl = List.map snd c.cl_params in + let tl = extract_param_types c.cl_params in match Type.class_field c tl ("set_" ^ f.cf_name) with | Some (actual_cl, actual_tl), _, setter_cf when actual_cl != c -> let func = mk_instance_setter_func ctx.com c tl actual_cl actual_tl setter_cf f in diff --git a/src/generators/hl2c.ml b/src/generators/hl2c.ml index 29485cf6..af03a261 100644 --- a/src/generators/hl2c.ml +++ b/src/generators/hl2c.ml @@ -78,6 +78,7 @@ type context = { mutable file_prefix : string; mutable fun_index : int; mutable type_module : (ttype, code_module) PMap.t; + gcon : Common.context; } let sprintf = Printf.sprintf @@ -124,7 +125,7 @@ let tname str = if Hashtbl.mem keywords ("_" ^ n) then "__" ^ n else n let is_gc_ptr = function - | HVoid | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HType | HRef _ | HMethod _ -> false + | HVoid | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HType | HRef _ | HMethod _ | HPacked _ -> false | HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ | HStruct _ -> true let is_ptr = function @@ -153,6 +154,9 @@ let rec ctype_no_ptr = function | HEnum _ -> "venum",1 | HNull _ -> "vdynamic",1 | HMethod _ -> "void",1 + | HPacked t -> + let name,v = ctype_no_ptr t in + "struct _" ^ name, v let ctype t = let t, nptr = ctype_no_ptr t in @@ -199,6 +203,7 @@ let type_id t = | HNull _ -> "HNULL" | HMethod _ -> "HMETHOD" | HStruct _ -> "HSTRUCT" + | HPacked _ -> "HPACKED" let var_type n t = ctype t ^ " " ^ ident n @@ -223,7 +228,7 @@ let hash ctx sid = h let type_name ctx t = - try PMap.find t ctx.htypes with Not_found -> Globals.die "" __LOC__ + try PMap.find t ctx.htypes with Not_found -> Globals.die (tstr t) __LOC__ let define ctx s = if not (Hashtbl.mem ctx.hdefines s) then begin @@ -245,6 +250,8 @@ let rec define_type ctx t = | HVirtual vp when not (PMap.exists t ctx.defined_types) -> ctx.defined_types <- PMap.add t () ctx.defined_types; Array.iter (fun (_,_,t) -> define_type ctx t) vp.vfields + | HPacked t -> + define_type ctx t | HEnum _ | HObj _ | HStruct _ | HVirtual _ -> () @@ -291,10 +298,13 @@ let unamed_field fid = "f$" ^ string_of_int fid let obj_field fid name = if name = "" then unamed_field fid else ident name +let bom = "\xEF\xBB\xBF" + let close_file ctx = - let str = Buffer.contents ctx.out in + let out = Buffer.contents ctx.out in let defines = List.rev ctx.defines in - let str = (match defines with [] -> str | l -> String.concat "\n" l ^ "\n\n" ^ str) in + let content = (match defines with [] -> out | l -> String.concat "\n" l ^ "\n\n" ^ out) in + let str = if ctx.curfile = "hlc.json" then content else bom ^ content in ctx.defines <- []; ctx.defined_types <- PMap.empty; Hashtbl.clear ctx.hdefines; @@ -311,8 +321,6 @@ let close_file ctx = close_out ch; end -let bom = "\xEF\xBB\xBF" - let define_global ctx g = let t = ctx.hlcode.globals.(g) in define_type ctx t; @@ -337,10 +345,8 @@ let short_digest str = let open_file ctx file = if ctx.curfile <> "" then close_file ctx; - let version_major = ctx.version / 1000 in - let version_minor = (ctx.version mod 1000) / 100 in - let version_revision = (ctx.version mod 100) in - if file <> "hlc.json" then define ctx (sprintf "%s// Generated by HLC %d.%d.%d (HL v%d)" bom version_major version_minor version_revision ctx.hlcode.version); + if file <> "hlc.json" then + Codegen.map_source_header ctx.gcon (fun s -> define ctx (sprintf "// %s" s)); ctx.curfile <- file; ctx.fun_index <- 0; ctx.file_prefix <- (short_digest file) ^ "_" @@ -549,7 +555,9 @@ let generate_function ctx f = let assign = reg r ^ " = " in if tsame t rt then assign else if not (safe_cast t rt) then Globals.die "" __LOC__ - else assign ^ "(" ^ ctype rt ^ ")" + else + let cast = assign ^ "(" ^ ctype rt ^ ")" in + (match t with HPacked _ -> cast ^ "&" | _ -> cast) in let ocall r fid args = @@ -568,7 +576,7 @@ let generate_function ctx f = in let type_value_opt t = - match t with HF32 | HF64 -> "" | _ -> "," ^ type_value t + match t with HF32 | HF64 | HI64 -> "" | _ -> "," ^ type_value t in let dyn_call r f pl = @@ -735,7 +743,7 @@ let generate_function ctx f = one way for comparisons *) match rtype a, rtype b with - | (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool), (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool) -> + | (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HI64), (HUI8 | HUI16 | HI32 | HF32 | HF64 | HBool | HI64) -> phys_compare() | HType, HType -> sexpr "if( hl_same_type(%s,%s) %s 0 ) {} else goto %s" (reg a) (reg b) (s_comp op) (label d) @@ -1096,6 +1104,9 @@ let valid_ident = let e = Str.regexp "[^A-Za-z0-9_]+" in (fun str -> Str.global_replace e "_" str) +let native_name str = + if str.[0] = '?' then String.sub str 1 (String.length str - 1) else str + let make_types_idents htypes = let types_descs = ref PMap.empty in let rec make_desc t = @@ -1108,6 +1119,10 @@ let make_types_idents htypes = DFun (List.map make_desc tl, make_desc t, false) | HObj p | HStruct p -> DNamed p.pname + | HPacked t -> + (match make_desc t with + | DNamed n -> DNamed ("packed_" ^ n) + | _ -> Globals.die "" __LOC__) | HAbstract (n,_) -> DNamed n | HEnum e when e.ename = "" -> @@ -1125,8 +1140,13 @@ let make_types_idents htypes = Array.iteri (fun i (f,_,t) -> arr.(i) <- (f,make_desc t)) vp.vfields; td in + let hashes = Hashtbl.create 0 in let make_sign d = - String.sub (Digest.to_hex (Digest.bytes (Marshal.to_bytes d [Marshal.Compat_32]))) 0 7 + let dig = Digest.to_hex (Digest.bytes (Marshal.to_bytes d [Marshal.Compat_32])) in + let h = String.sub dig 0 7 in + let h = if Hashtbl.mem hashes h then dig else h in + Hashtbl.add hashes h (); + h in let rec desc_string d = match d with @@ -1182,8 +1202,11 @@ let make_function_table code = Array.iter (fun (lib,name,t,idx) -> let fname = let lib = code.strings.(lib) in - let lib = if lib = "std" then "hl" else lib in - lib ^ "_" ^ code.strings.(name) + let lib = if lib.[0] = '?' then String.sub lib 1 (String.length lib - 1) else lib in + let lib = if lib = "std" then "hl" else lib in + let str = lib ^ "_" ^ code.strings.(name) in + (* create wrappers defines for invalid definitions *) + if str = "hl_tls_get" then str ^ "_w" else str in match t with | HFun (args, t) -> @@ -1430,6 +1453,7 @@ let write_c com file (code:code) gnames = file_prefix = ""; fun_index = 0; type_module = PMap.empty; + gcon = com; } in let modules = make_modules ctx all_types in @@ -1442,6 +1466,7 @@ let write_c com file (code:code) gnames = define ctx "// Abstract decls"; let rec get_abstracts = function | [] -> [] + | HAbstract (("hl_tls" | "hl_mutex" | "hl_thread"), _) :: l -> get_abstracts l (* ignore / defined in hl.h already *) | HAbstract (name,_) :: l -> name :: get_abstracts l | _ :: l -> get_abstracts l in @@ -1453,10 +1478,18 @@ let write_c com file (code:code) gnames = let sorted_natives = Array.copy code.natives in Array.sort (fun n1 n2 -> let mk (lib,name,_,_) = code.strings.(lib), code.strings.(name) in compare (mk n1) (mk n2)) sorted_natives; Array.iter (fun (lib,_,_,idx) -> - Hashtbl.replace native_libs code.strings.(lib) (); + let name = code.strings.(lib) in + let name = if name.[0] = '?' then String.sub name 1 (String.length name - 1) else name in + Hashtbl.replace native_libs name (); let ft = ctx.ftable.(idx) in define_type ctx (HFun (ft.fe_args,ft.fe_ret)); - sexpr "HL_API %s %s(%s)" (ctype ft.fe_ret) ft.fe_name (args_repr ft.fe_args); + match ft.fe_name with + | "hl_tls_get_w" -> + define ctx "#define hl_tls_get_w(tls) ((vdynamic*)hl_tls_get(tls))"; + | "hl_tls_set" -> + () (* don't redefine *) + | _ -> + sexpr "HL_API %s %s(%s)" (ctype ft.fe_ret) ft.fe_name (args_repr ft.fe_args); ) sorted_natives; line "#endif"; line ""; @@ -1588,7 +1621,7 @@ let write_c com file (code:code) gnames = string_of_int (Array.length o.pproto); string_of_int (List.length o.pbindings); sprintf "(const uchar*)%s" (string ctx o.pid); - (match o.psuper with None -> "NULL" | Some c -> type_value ctx (HObj c)); + (match o.psuper with None -> "NULL" | Some c -> type_value ctx (match t with HObj _ -> HObj c | _ -> HStruct c)); fields; proto; bindings @@ -1661,7 +1694,7 @@ let write_c com file (code:code) gnames = define_global ctx g; sexpr "obj%s.global_value = (void**)&%s" name gnames.(g)); sexpr "%s.obj = &obj%s" name name - | HNull r | HRef r -> + | HNull r | HRef r | HPacked r -> sexpr "%s.tparam = %s" (type_name ctx t) (type_value ctx r) | HEnum e -> let name = type_name ctx t in @@ -1720,7 +1753,10 @@ let write_c com file (code:code) gnames = open_file ctx "hl/functions.c"; define ctx "#define HLC_BOOT"; define ctx "#include "; - sexpr "void *hl_functions_ptrs[] = {%s}" (String.concat "," (List.map (fun f -> define_function ctx f.fe_index) (Array.to_list ctx.ftable))); + sexpr "void *hl_functions_ptrs[] = {%s}" (String.concat ",\\\n\t" (List.map (fun f -> + let name = define_function ctx f.fe_index in + if name = "hl_tls_get_w" then "hl_tls_get" else name + ) (Array.to_list ctx.ftable))); let rec loop i = if i = Array.length ctx.ftable then [] else let ft = ctx.ftable.(i) in @@ -1728,7 +1764,7 @@ let write_c com file (code:code) gnames = define ctx (sprintf "extern hl_type %s;" n); ("&" ^ n) :: loop (i + 1) in - sexpr "hl_type *hl_functions_types[] = {%s}" (String.concat "," (loop 0)); + sexpr "hl_type *hl_functions_types[] = {%s}" (String.concat ",\\\n\t" (loop 0)); line ""; Array.iter (fun f -> if f.fe_module = None then (match f.fe_decl with None -> () | Some f -> generate_function ctx f); @@ -1780,10 +1816,14 @@ let write_c com file (code:code) gnames = block ctx; sline "\"version\" : %d," ctx.version; sline "\"libs\" : [%s]," (String.concat "," (Hashtbl.fold (fun k _ acc -> sprintf "\"%s\"" k :: acc) native_libs [])); - sline "\"defines\" : {%s\n\t}," (String.concat "," (PMap.foldi (fun k v acc -> sprintf "\n\t\t\"%s\" : \"%s\"" (String.escaped k) (String.escaped v) :: acc) com.Common.defines.Define.values [])); + let defines = Buffer.create 64 in + PMap.iter (fun key value -> + Printf.bprintf defines "\n\t\t\"%s\" : \"%s\"," (String.escaped key) (String.escaped value); + ) com.defines.values; + Buffer.truncate defines (Buffer.length defines - 1); + sline "\"defines\" : {%s\n\t}," (Buffer.contents defines); sline "\"files\" : [%s\n\t]" (String.concat "," (List.map (sprintf "\n\t\t\"%s\"") ctx.cfiles)); unblock ctx; line "}"; close_file ctx - diff --git a/src/generators/hlcode.ml b/src/generators/hlcode.ml index 7f4f3121..733a6b10 100644 --- a/src/generators/hlcode.ml +++ b/src/generators/hlcode.ml @@ -48,6 +48,7 @@ type ttype = | HNull of ttype | HMethod of ttype list * ttype | HStruct of class_proto + | HPacked of ttype and class_proto = { pname : string; @@ -256,10 +257,10 @@ let list_mapi f l = let is_nullable t = match t with | HBytes | HDyn | HFun _ | HObj _ | HArray | HVirtual _ | HDynObj | HAbstract _ | HEnum _ | HNull _ | HRef _ | HType | HMethod _ | HStruct _ -> true - | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HVoid -> false + | HUI8 | HUI16 | HI32 | HI64 | HF32 | HF64 | HBool | HVoid | HPacked _ -> false let is_struct = function - | HStruct _ -> true + | HStruct _ | HPacked _ -> true | _ -> false let is_int = function @@ -332,6 +333,8 @@ let rec safe_cast t1 t2 = p.pname = p2.pname || (match p.psuper with None -> false | Some p -> loop p) in loop p1 + | HPacked t1, HStruct _ -> + safe_cast t1 t2 | HFun (args1,t1), HFun (args2,t2) when List.length args1 = List.length args2 -> List.for_all2 (fun t1 t2 -> safe_cast t2 t1 || (t1 = HDyn && is_dynamic t2)) args1 args2 && safe_cast t1 t2 | _ -> @@ -382,7 +385,10 @@ let gather_types (code:code) = let types = ref PMap.empty in let arr = DynArray.create() in let rec get_type t = - (match t with HObj { psuper = Some p } -> get_type (HObj p) | _ -> ()); + (match t with + | HObj { psuper = Some p } -> get_type (HObj p) + | HStruct { psuper = Some p } -> get_type (HStruct p) + | _ -> ()); if PMap.mem t !types then () else let index = DynArray.length arr in DynArray.add arr t; @@ -462,6 +468,7 @@ let rec tstr ?(stack=[]) ?(detailed=false) t = | HEnum e -> "enum(" ^ e.ename ^ ")" | HNull t -> "null(" ^ tstr t ^ ")" + | HPacked t -> "packed(" ^ tstr t ^ ")" let ostr fstr o = match o with @@ -565,7 +572,6 @@ let ostr fstr o = | ORefData (r,d) -> Printf.sprintf "refdata %d, %d" r d | ORefOffset (r,r2,off) -> Printf.sprintf "refoffset %d, %d, %d" r r2 off | ONop s -> if s = "" then "nop" else "nop " ^ s - let fundecl_name f = if snd f.fpath = "" then "fun$" ^ (string_of_int f.findex) else (fst f.fpath) ^ "." ^ (snd f.fpath) let dump pr code = diff --git a/src/generators/hlinterp.ml b/src/generators/hlinterp.ml index e395662a..f6fc7f32 100644 --- a/src/generators/hlinterp.ml +++ b/src/generators/hlinterp.ml @@ -1083,7 +1083,8 @@ let interp ctx f args = | HEnum _ -> 18 | HNull _ -> 19 | HMethod _ -> 20 - | HStruct _ -> 21))) + | HStruct _ -> 21 + | HPacked _ -> 22))) | _ -> Globals.die "" __LOC__); | ORef (r,v) -> set r (VRef (RStack (v + spos),rtype v)) @@ -1533,12 +1534,12 @@ let load_native ctx lib name t = (match !cached_sys_name with | Some n -> n | None -> - let ic = Unix.open_process_in "uname" in + let ic, pid = Process_helper.open_process_args_in_pid "uname" [| "uname" |] in let uname = (match input_line ic with | "Darwin" -> "Mac" | n -> n ) in - close_in ic; + Pervasives.ignore (Process_helper.close_process_in_pid (ic, pid)); cached_sys_name := Some uname; uname) | "Win32" | "Cygwin" -> "Windows" @@ -2545,7 +2546,7 @@ let check code macros = reg r (rtype r2); reg off HI32; | ONop _ -> - () + (); ) f.code (* TODO : check that all path correctly initialize NULL values and reach a return *) in diff --git a/src/generators/hlopt.ml b/src/generators/hlopt.ml index cadb9087..2e5bd20c 100644 --- a/src/generators/hlopt.ml +++ b/src/generators/hlopt.ml @@ -746,6 +746,18 @@ let _optimize (f:fundecl) = r.ralias <- r; r ) in + + let is_packed_field o fid = + match f.regs.(o) with + | HStruct p | HObj p -> + let ft = (try snd (resolve_field p fid) with Not_found -> assert false) in + (match ft with + | HPacked _ -> true + | _ -> false) + | _ -> + false + in + (* let print_state i s = let state_str s = @@ -856,6 +868,13 @@ let _optimize (f:fundecl) = do_read o; do_write v; state.(v).rnullcheck <- state.(o).rnullcheck + | OField (r,o,fid) when (match f.regs.(r) with HStruct _ -> true | _ -> false) -> + do_read o; + do_write r; + if is_packed_field o fid then state.(r).rnullcheck <- true; + | OGetThis (r,fid) when (match f.regs.(r) with HStruct _ -> true | _ -> false) -> + do_write r; + if is_packed_field 0 fid then state.(r).rnullcheck <- true; | _ -> opcode_fx (fun r read -> if read then do_read r else do_write r diff --git a/src/generators/jvm/jvmAttribute.ml b/src/generators/jvm/jvmAttribute.ml index be6e512a..e39ad388 100644 --- a/src/generators/jvm/jvmAttribute.ml +++ b/src/generators/jvm/jvmAttribute.ml @@ -85,6 +85,7 @@ type j_attribute = | AttributeInnerClasses of jvm_inner_class array | AttributeEnclosingMethod of jvm_constant_pool_index * jvm_constant_pool_index | AttributeRuntimeVisibleAnnotations of j_annotation array + | AttributeRuntimeVisibleParameterAnnotations of j_annotation array array | AttributeBootstrapMethods of j_bootstrap_method array let write_verification_type ch = function @@ -166,7 +167,7 @@ let rec write_annotation ch ann = | ValClass i -> write_ui16 ch i | ValAnnotation a -> - write_annotation ch ann + write_annotation ch a | ValArray annl -> write_array16 ch loop annl in @@ -235,6 +236,10 @@ let write_attribute pool jvma = | AttributeRuntimeVisibleAnnotations al -> write_array16 ch write_annotation al; "RuntimeVisibleAnnotations" + | AttributeRuntimeVisibleParameterAnnotations al -> + write_byte ch (Array.length al); + Array.iter (write_array16 ch write_annotation) al; + "RuntimeVisibleParameterAnnotations" | AttributeBootstrapMethods a -> write_array16 ch (fun _ bm -> write_ui16 ch bm.bm_method_ref; @@ -245,4 +250,4 @@ let write_attribute pool jvma = { attr_index = pool#add (ConstUtf8 name); attr_data = IO.close_out ch - } \ No newline at end of file + } diff --git a/src/generators/jvm/jvmBuilder.ml b/src/generators/jvm/jvmBuilder.ml index 0e414bc0..f7afa5d0 100644 --- a/src/generators/jvm/jvmBuilder.ml +++ b/src/generators/jvm/jvmBuilder.ml @@ -29,6 +29,7 @@ type annotation_kind = | ABool of bool | AEnum of jsignature * string | AArray of annotation_kind list + | AAnnotation of jsignature * annotation and annotation = (string * annotation_kind) list @@ -36,6 +37,40 @@ type export_config = { export_debug : bool; } +let convert_annotations pool annotations = + let rec process_annotation (jsig, l) = + let offset = pool#add_string (generate_signature false jsig) in + let l = List.map (fun (name,ak) -> + let offset = pool#add_string name in + let rec loop ak = match ak with + | AInt i32 -> + 'I',ValConst(pool#add (ConstInt i32)) + | ADouble f -> + 'D',ValConst(pool#add (ConstDouble f)) + | AString s -> + 's',ValConst(pool#add_string s) + | ABool b -> + 'Z',ValConst(pool#add (ConstInt (if b then Int32.one else Int32.zero))) + | AEnum(jsig,name) -> + 'e',ValEnum(pool#add_string (generate_signature false jsig),pool#add_string name) + | AArray l -> + let l = List.map (fun ak -> loop ak) l in + '[',ValArray(Array.of_list l) + | AAnnotation (jsig, a) -> + let ann = process_annotation (jsig, a) in + '@',ValAnnotation(ann) + + in + offset,loop ak + ) l in + { + ann_type = offset; + ann_elements = Array.of_list l; + } + in + let a = Array.map process_annotation annotations in + a + class base_builder = object(self) val mutable access_flags = 0 val attributes = DynArray.create () @@ -54,36 +89,10 @@ class base_builder = object(self) method private commit_annotations pool = if DynArray.length annotations > 0 then begin let open JvmAttribute in - let a = DynArray.to_array annotations in - let a = Array.map (fun (jsig,l) -> - let offset = pool#add_string (generate_signature false jsig) in - let l = List.map (fun (name,ak) -> - let offset = pool#add_string name in - let rec loop ak = match ak with - | AInt i32 -> - 'I',ValConst(pool#add (ConstInt i32)) - | ADouble f -> - 'D',ValConst(pool#add (ConstDouble f)) - | AString s -> - 's',ValConst(pool#add_string s) - | ABool b -> - 'Z',ValConst(pool#add (ConstInt (if b then Int32.one else Int32.zero))) - | AEnum(jsig,name) -> - 'e',ValEnum(pool#add_string (generate_signature false jsig),pool#add_string name) - | AArray l -> - let l = List.map (fun ak -> loop ak) l in - '[',ValArray(Array.of_list l) - in - offset,loop ak - ) l in - { - ann_type = offset; - ann_elements = Array.of_list l; - } - ) a in + let a = convert_annotations pool (DynArray.to_array annotations) in self#add_attribute (AttributeRuntimeVisibleAnnotations a) end method export_attributes (pool : JvmConstantPool.constant_pool) = DynArray.to_array (DynArray.map (write_attribute pool) attributes) -end \ No newline at end of file +end diff --git a/src/generators/jvm/jvmClass.ml b/src/generators/jvm/jvmClass.ml index 45e3e013..4e5591cd 100644 --- a/src/generators/jvm/jvmClass.ml +++ b/src/generators/jvm/jvmClass.ml @@ -40,6 +40,7 @@ class builder path_this path_super = object(self) val methods = DynArray.create () val method_sigs = Hashtbl.create 0 val inner_classes = Hashtbl.create 0 + val closure_ids_per_name = Hashtbl.create 0 val mutable spawned_methods = [] val mutable static_init_method = None val mutable source_file = None @@ -78,6 +79,16 @@ class builder path_this path_super = object(self) method has_method (name : string) (jsig : jsignature) = Hashtbl.mem method_sigs (name,generate_method_signature false jsig) + method get_next_closure_id (name : string) = + try + let r = Hashtbl.find closure_ids_per_name name in + incr r; + !r + with Not_found -> + let r = ref 0 in + Hashtbl.add closure_ids_per_name name r; + !r + method spawn_inner_class (jm : JvmMethod.builder option) (path_super : jpath) (name : string option) = let path = match name with | None -> (fst path_this,Printf.sprintf "%s$%i" (snd path_this) (Hashtbl.length inner_classes)) diff --git a/src/generators/jvm/jvmFunctions.ml b/src/generators/jvm/jvmFunctions.ml index a29e8395..64621f88 100644 --- a/src/generators/jvm/jvmFunctions.ml +++ b/src/generators/jvm/jvmFunctions.ml @@ -281,7 +281,7 @@ class typed_functions = object(self) end type typed_function_kind = - | FuncLocal + | FuncLocal of string option | FuncMember of jpath * string | FuncStatic of jpath * string @@ -294,39 +294,17 @@ module JavaFunctionalInterfaces = struct jparams : string list; } - let java_functional_interfaces = - let juf = ["java";"util";"function"] in - let tp name = TTypeParameter name in - [ - { - jargs = []; - jret = None; - jpath = ["java";"lang"],"Runnable"; - jname = "run"; - jparams = [] - }; - { - jargs = [tp "T"]; - jret = None; - jpath = juf,"Consumer"; - jname = "accept"; - jparams = ["T"] - }; - { - jargs = [tp "T";tp "U"]; - jret = None; - jpath = juf,"BiConsumer"; - jname = "accept"; - jparams = ["T";"U"] - }; - { - jargs = [tp "T"]; - jret = Some (tp "R"); - jpath = juf,"Function"; - jname = "apply"; - jparams = ["T";"R"] - }; - ] + let java_functional_interfaces = DynArray.create () + + let add args ret path name params = + let jfi = { + jargs = args; + jret = ret; + jpath = path; + jname = name; + jparams = params; + } in + DynArray.add java_functional_interfaces jfi let unify jfi args ret = let rec loop params want have = match want,have with @@ -357,15 +335,22 @@ module JavaFunctionalInterfaces = struct None - let find_compatible args ret = - ExtList.List.filter_map (fun jfi -> - if jfi.jparams = [] then begin - if jfi.jargs = args && jfi.jret = ret then - Some (jfi,[]) - else None + let find_compatible args ret filter = + DynArray.fold_left (fun acc jfi -> + if filter = [] || List.mem jfi.jpath filter then begin + if jfi.jparams = [] then begin + if jfi.jargs = args && jfi.jret = ret then + (jfi,[]) :: acc + else + acc + end else match unify jfi args ret with + | Some x -> + x :: acc + | None -> + acc end else - unify jfi args ret - ) java_functional_interfaces + acc + ) [] java_functional_interfaces end open JavaFunctionalInterfaces @@ -382,8 +367,13 @@ class typed_function val jc_closure = let name = match kind with - | FuncLocal -> - Printf.sprintf "Closure_%s_%i" (patch_name host_method#get_name) host_method#get_next_closure_id + | FuncLocal s -> + let name = patch_name host_method#get_name in + let name = match s with + | None -> name + | Some s -> name ^ "_" ^ s + in + Printf.sprintf "Closure_%s_%i" name (host_class#get_next_closure_id name) | FuncStatic(path,name) -> Printf.sprintf "%s_%s" (snd path) (patch_name name) | FuncMember(path,name) -> @@ -406,7 +396,7 @@ class typed_function jm_ctor#return; jm_ctor - method generate_invoke (args : (string * jsignature) list) (ret : jsignature option)= + method generate_invoke (args : (string * jsignature) list) (ret : jsignature option) (functional_interface_filter : jpath list) = let arg_sigs = List.map snd args in let meth = functions#register_signature arg_sigs ret in let jsig_invoke = method_sig arg_sigs ret in @@ -419,14 +409,17 @@ class typed_function end in let spawn_forward_function meth_from meth_to is_bridge = - let flags = [MPublic] in - let flags = if is_bridge then MBridge :: MSynthetic :: flags else flags in - let jm_invoke_next = jc_closure#spawn_method meth_from.name (method_sig meth_from.dargs meth_from.dret) flags in - functions#make_forward_method jc_closure jm_invoke_next meth_from meth_to; + let msig = method_sig meth_from.dargs meth_from.dret in + if not (jc_closure#has_method meth_from.name msig) then begin + let flags = [MPublic] in + let flags = if is_bridge then MBridge :: MSynthetic :: flags else flags in + let jm_invoke_next = jc_closure#spawn_method meth_from.name msig flags in + functions#make_forward_method jc_closure jm_invoke_next meth_from meth_to; + end in let check_functional_interfaces meth = try - let l = JavaFunctionalInterfaces.find_compatible meth.dargs meth.dret in + let l = JavaFunctionalInterfaces.find_compatible meth.dargs meth.dret functional_interface_filter in List.iter (fun (jfi,params) -> add_interface jfi.jpath params; spawn_forward_function {meth with name=jfi.jname} meth false; diff --git a/src/generators/jvm/jvmMethod.ml b/src/generators/jvm/jvmMethod.ml index 537692a8..a80e2d00 100644 --- a/src/generators/jvm/jvmMethod.ml +++ b/src/generators/jvm/jvmMethod.ml @@ -31,15 +31,28 @@ let rec pow a b = match b with | _ -> Int32.mul a (pow a (b - 1)) let java_hash s = + let high_surrogate c = (c lsr 10) + 0xD7C0 in + let low_surrogate c = (c land 0x3FF) lor 0xDC00 in let h = ref Int32.zero in - let l = UTF8.length s in - let i31 = Int32.of_int 31 in - let i = ref 0 in - UTF8.iter (fun char -> - let char = Int32.of_int (UCharExt.uint_code char) in - h := Int32.add !h (Int32.mul char (pow i31 (l - (!i + 1)))); - incr i; - ) s; + let thirtyone = Int32.of_int 31 in + (try + UTF8.validate s; + UTF8.iter (fun c -> + let c = (UCharExt.code c) in + if c > 0xFFFF then + (h := Int32.add (Int32.mul thirtyone !h) + (Int32.of_int (high_surrogate c)); + h := Int32.add (Int32.mul thirtyone !h) + (Int32.of_int (low_surrogate c))) + else + h := Int32.add (Int32.mul thirtyone !h) + (Int32.of_int c) + ) s + with UTF8.Malformed_code -> + String.iter (fun c -> + h := Int32.add (Int32.mul thirtyone !h) + (Int32.of_int (Char.code c))) s + ); !h module HashtblList = struct @@ -141,8 +154,8 @@ class builder jc name jsig = object(self) val mutable stack_frames = [] val mutable exceptions = [] val mutable argument_locals = [] + val mutable argument_annotations = Hashtbl.create 0 val mutable thrown_exceptions = Hashtbl.create 0 - val mutable closure_count = 0 val mutable regex_count = 0 (* per-frame *) @@ -191,11 +204,6 @@ class builder jc name jsig = object(self) | _ -> JvmVerificationTypeInfo.of_signature jc#get_pool t ) locals - method get_next_closure_id = - let id = closure_count in - closure_count <- closure_count + 1; - id - method get_next_regex_id = let id = regex_count in regex_count <- regex_count + 1; @@ -1002,6 +1010,10 @@ class builder jc name jsig = object(self) method replace_top jsig = code#get_stack#replace jsig + method add_argument_annotation (slot : int) (a : (path * annotation) list) = + let a = Array.of_list (List.map (fun (path,annot) -> TObject(path,[]),annot) a) in + Hashtbl.add argument_annotations slot a + (** This function has to be called once all arguments are declared. *) method finalize_arguments = argument_locals <- locals @@ -1123,6 +1135,18 @@ class builder jc name jsig = object(self) end; if Hashtbl.length thrown_exceptions > 0 then self#add_attribute (AttributeExceptions (Array.of_list (Hashtbl.fold (fun k _ c -> k :: c) thrown_exceptions []))); + if Hashtbl.length argument_annotations > 0 then begin + let l = List.length argument_locals in + let offset = if self#has_method_flag MStatic then 0 else 1 in + let a = Array.init (l - offset) (fun i -> + try + let annot = Hashtbl.find argument_annotations (i + offset) in + convert_annotations jc#get_pool annot + with Not_found -> + [||] + ) in + DynArray.add attributes (AttributeRuntimeVisibleParameterAnnotations a) + end; let attributes = self#export_attributes jc#get_pool in let offset_name = jc#get_pool#add_string name in let jsig = generate_method_signature false jsig in @@ -1139,6 +1163,7 @@ class builder jc name jsig = object(self) assert (code#get_fp = 0); assert (not was_exported); was_exported <- true; + self#commit_annotations jc#get_pool; let attributes = self#export_attributes jc#get_pool in let offset_name = jc#get_pool#add_string name in let jsig = generate_signature false jsig in diff --git a/src/macro/eval/evalContext.ml b/src/macro/eval/evalContext.ml index 76a42d7b..c23cd1df 100644 --- a/src/macro/eval/evalContext.ml +++ b/src/macro/eval/evalContext.ml @@ -287,6 +287,8 @@ and context = { mutable evals : eval IntMap.t; mutable exception_stack : (pos * env_kind) list; max_stack_depth : int; + max_print_depth : int; + print_indentation : string option; } module GlobalState = struct @@ -353,10 +355,13 @@ let rec kind_name eval kind = let call_function f vl = f vl -let object_fields o = - IntMap.fold (fun key index acc -> - (key,(o.ofields.(index))) :: acc - ) o.oproto.pinstance_names [] +let object_fields o = match o.oproto with + | OProto proto -> + IntMap.fold (fun key index acc -> + (key,(o.ofields.(index))) :: acc + ) proto.pinstance_names [] + | ODictionary d -> + IntMap.fold (fun k v acc -> (k,v) :: acc) d [] let instance_fields i = IntMap.fold (fun name key acc -> @@ -400,6 +405,8 @@ let exc v = throw v null_pos let exc_string str = exc (vstring (EvalString.create_ascii str)) +let exc_string_p str p = throw (vstring (EvalString.create_ascii str)) p + let error_message = exc_string let flush_core_context f = @@ -499,7 +506,7 @@ let get_static_prototype_raise ctx path = let get_static_prototype ctx path p = try get_static_prototype_raise ctx path - with Not_found -> Error.error (Printf.sprintf "[%i] Type not found: %s" ctx.ctx_id (rev_hash path)) p + with Not_found -> Error.typing_error (Printf.sprintf "[%i] Type not found: %s" ctx.ctx_id (rev_hash path)) p let get_static_prototype_as_value ctx path p = (get_static_prototype ctx path p).pvalue @@ -509,14 +516,14 @@ let get_instance_prototype_raise ctx path = let get_instance_prototype ctx path p = try get_instance_prototype_raise ctx path - with Not_found -> Error.error (Printf.sprintf "[%i] Instance prototype not found: %s" ctx.ctx_id (rev_hash path)) p + with Not_found -> Error.typing_error (Printf.sprintf "[%i] Instance prototype not found: %s" ctx.ctx_id (rev_hash path)) p let get_instance_constructor_raise ctx path = IntMap.find path ctx.constructors let get_instance_constructor ctx path p = try get_instance_constructor_raise ctx path - with Not_found -> Error.error (Printf.sprintf "[%i] Instance constructor not found: %s" ctx.ctx_id (rev_hash path)) p + with Not_found -> Error.typing_error (Printf.sprintf "[%i] Instance constructor not found: %s" ctx.ctx_id (rev_hash path)) p let get_special_instance_constructor_raise ctx path = Hashtbl.find (get_ctx()).builtins.constructor_builtins path @@ -526,14 +533,14 @@ let get_proto_field_index_raise proto name = let get_proto_field_index proto name = try get_proto_field_index_raise proto name - with Not_found -> Error.error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash name) (rev_hash proto.ppath)) null_pos + with Not_found -> Error.typing_error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash name) (rev_hash proto.ppath)) null_pos let get_instance_field_index_raise proto name = IntMap.find name proto.pinstance_names let get_instance_field_index proto name p = try get_instance_field_index_raise proto name - with Not_found -> Error.error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash name) (rev_hash proto.ppath)) p + with Not_found -> Error.typing_error (Printf.sprintf "Field index for %s not found on prototype %s" (rev_hash name) (rev_hash proto.ppath)) p let is v path = if path = key_Dynamic then diff --git a/src/macro/eval/evalDebugMisc.ml b/src/macro/eval/evalDebugMisc.ml index 5785418a..d994a037 100644 --- a/src/macro/eval/evalDebugMisc.ml +++ b/src/macro/eval/evalDebugMisc.ml @@ -197,32 +197,34 @@ let safe_call eval f a = eval.debug_state <- old; raise exc +exception NoValueExpr + let rec expr_to_value ctx env e = let rec loop e = match fst e with | EConst cst -> begin match cst with | String(s,_) -> EvalString.create_unknown s - | Int s -> VInt32 (Int32.of_string s) - | Float s -> VFloat (float_of_string s) + | Int (s,_) -> VInt32 (Int32.of_string s) + | Float (s,_) -> VFloat (float_of_string s) | Ident "true" -> VTrue | Ident "false" -> VFalse | Ident "null" -> VNull | Ident s -> let value = resolve_ident ctx env s in value - | _ -> raise Exit + | _ -> raise NoValueExpr end | EArray(e1,eidx) -> let v1 = loop e1 in let vidx = loop eidx in - let idx = match vidx with VInt32 i -> Int32.to_int i | _ -> raise Exit in + let idx = match vidx with VInt32 i -> Int32.to_int i | _ -> raise NoValueExpr in begin match v1 with | VArray va -> EvalArray.get va idx | VVector vv -> Array.get vv idx | VEnumValue ev -> Array.get ev.eargs idx - | _ -> raise Exit + | _ -> raise NoValueExpr end - | EField(e1,s) -> + | EField(e1,s,_) -> let v1 = loop e1 in let s' = hash s in begin match v1 with @@ -257,18 +259,20 @@ let rec expr_to_value ctx env e = let v2 = loop e2 in write_expr ctx env e1 v2; | OpAssignOp op -> - raise Exit (* Nobody does that, right? *) + raise NoValueExpr | OpBoolAnd -> if is_true (loop e1) then loop e2 else VFalse | OpBoolOr -> if is_true (loop e1) then VTrue else loop e2 + | OpInterval | OpArrow | OpIn | OpNullCoal -> + raise NoValueExpr | _ -> let v1 = loop e1 in let v2 = loop e2 in let p = pos e in - (try get_binop_fun op p with _ -> raise Exit) v1 v2 + (get_binop_fun op p) v1 v2 end | EUnop(op,flag,e1) -> begin match op with @@ -281,16 +285,16 @@ let rec expr_to_value ctx env e = begin match loop e1 with | VFloat f -> VFloat (-.f) | VInt32 i -> vint32 (Int32.neg i) - | _ -> raise Exit + | _ -> raise NoValueExpr end | NegBits -> op_sub (pos e) (vint32 (Int32.minus_one)) (loop e1) | Increment | Decrement | Spread -> - raise Exit + raise NoValueExpr end | ECall(e1,el) -> begin match fst e1 with - | EField(ethis,s) -> + | EField(ethis,s,_) -> let vthis = loop ethis in let v1 = EvalField.field vthis (hash s) in let vl = List.map loop el in @@ -359,14 +363,14 @@ let rec expr_to_value ctx env e = let vc = loop2 ctx.toplevel ["Type";"createInstance"] in safe_call env.env_eval (call_value vc) [v1;encode_array vl] | ETry _ | ESwitch _ | EFunction _ | EFor _ | EDisplay _ - | EDisplayNew _ | ECast(_,Some _) | EIs _ -> - raise Exit + | ECast(_,Some _) | EIs _ -> + raise NoValueExpr in loop e and write_expr ctx env expr value = begin match fst expr with - | EField(e1,s) -> + | EField(e1,s,_) -> let s' = hash s in let v1 = expr_to_value ctx env e1 in begin match v1 with @@ -394,23 +398,23 @@ and write_expr ctx env expr value = env.env_locals.(slot) <- value; value with Not_found -> - raise Exit + raise NoValueExpr end | EArray(e1,e2) -> let v1 = expr_to_value ctx env e1 in let vidx = expr_to_value ctx env e2 in - let idx = match vidx with VInt32 i -> Int32.to_int i | _ -> raise Exit in + let idx = match vidx with VInt32 i -> Int32.to_int i | _ -> raise NoValueExpr in begin match v1 with | VArray va -> EvalArray.set va idx value | VVector vv -> Array.set vv idx value | VEnumValue ev -> Array.set ev.eargs idx value - | _ -> raise Exit + | _ -> raise NoValueExpr end; value | _ -> - raise Exit + raise NoValueExpr end let expr_to_value_safe ctx env e = try expr_to_value ctx env e - with Exit -> VNull \ No newline at end of file + with NoValueExpr -> VNull diff --git a/src/macro/eval/evalDebugSocket.ml b/src/macro/eval/evalDebugSocket.ml index ad178a89..9912edf9 100644 --- a/src/macro/eval/evalDebugSocket.ml +++ b/src/macro/eval/evalDebugSocket.ml @@ -23,6 +23,8 @@ let handle_in_temp_thread ctx env f = let v = try f() with + | NoValueExpr -> + vnull | RunTimeException(v,stack,p) -> prerr_endline (EvalExceptions.get_exc_error_message ctx v stack p); vnull diff --git a/src/macro/eval/evalEmitter.ml b/src/macro/eval/evalEmitter.ml index b9c172bc..5e078635 100644 --- a/src/macro/eval/evalEmitter.ml +++ b/src/macro/eval/evalEmitter.ml @@ -89,14 +89,25 @@ let emit_capture_declaration i exec env = let emit_const v _ = v +let emit_null_check exec p env = match exec env with + | VNull -> throw_string "Null Access" p + | v -> v + let emit_new_array env = encode_array_instance (EvalArray.create [||]) -let emit_new_vector_int i env = - encode_vector_instance (Array.make i vnull) +let emit_new_vector_int i p env = + if i < 0 then exc_string_p "Vector size must be >= 0" p; + let a = try + Array.make i vnull + with Invalid_argument _ -> + exc_string_p (Printf.sprintf "Not enough memory to allocate Vector of size %i" i) p; + in + encode_vector_instance a let emit_new_vector exec p env = - encode_vector_instance (Array.make (decode_int_p (exec env) p) vnull) + let i = decode_int_p (exec env) p in + emit_new_vector_int i p env let emit_special_instance f execs env = let vl = List.map (apply env) execs in @@ -107,7 +118,7 @@ let emit_object_declaration proto fa env = Array.iter (fun (i,exec) -> a.(i) <- exec env) fa; vobject { ofields = a; - oproto = proto; + oproto = OProto proto; } let emit_array_declaration execs env = @@ -408,8 +419,12 @@ let emit_field_closure exec name env = let emit_anon_field_read exec proto i name p env = match vresolve (exec env) with | VObject o -> - if proto == o.oproto then o.ofields.(i) - else object_field o name + begin match o.oproto with + | OProto proto' when proto' == proto -> + o.ofields.(i) + | _ -> + object_field o name + end | VNull -> throw_string "field access on null" p | v -> field v name @@ -481,10 +496,14 @@ let emit_anon_field_write exec1 p proto i name exec2 env = let v2 = exec2 env in begin match vresolve v1 with | VObject o -> - if proto == o.oproto then begin + begin match o.oproto with + | OProto proto' when proto' == proto -> o.ofields.(i) <- v2; - end else set_object_field o name v2 - | VNull -> throw_string "field access on null" p + | _ -> + set_object_field o name v2 + end + | VNull -> + throw_string "field access on null" p | _ -> set_field v1 name v2; end; diff --git a/src/macro/eval/evalEncode.ml b/src/macro/eval/evalEncode.ml index a2433649..2f1b9eb0 100644 --- a/src/macro/eval/evalEncode.ml +++ b/src/macro/eval/evalEncode.ml @@ -141,7 +141,7 @@ let encode_obj l = let proto,sorted = ctx.get_object_prototype ctx l in vobject { ofields = Array.of_list (List.map snd sorted); - oproto = proto; + oproto = OProto proto; } let encode_obj_s l = @@ -161,6 +161,7 @@ let encode_enum i pos index pl = let open MacroApi in let key = match i with | IExpr -> key_haxe_macro_ExprDef + | IEFieldKind -> key_haxe_macro_EFieldKind | IBinop -> key_haxe_macro_Binop | IUnop -> key_haxe_macro_Unop | IConst -> key_haxe_macro_Constant @@ -181,6 +182,11 @@ let encode_enum i pos index pl = | IImportMode -> key_haxe_macro_ImportMode | IQuoteStatus -> key_haxe_macro_QuoteStatus | IDisplayKind -> key_haxe_macro_DisplayKind + | IDisplayMode -> key_haxe_macro_DisplayMode + | ICapturePolicy -> key_haxe_macro_CapturePolicy + | IVarScope -> key_haxe_macro_VarScope + | IVarScopingFlags -> key_haxe_macro_VarScopingFlags + | IPackageRule -> key_haxe_macro_PackageRule | IMessage -> key_haxe_macro_Message | IFunctionKind -> key_haxe_macro_FunctionKind | IStringLiteralKind -> key_haxe_macro_StringLiteralKind @@ -325,4 +331,4 @@ let encode_option encode_value o = let encode_nullable encode_value o = match o with | Some v -> encode_value v - | None -> VNull \ No newline at end of file + | None -> VNull diff --git a/src/macro/eval/evalExceptions.ml b/src/macro/eval/evalExceptions.ml index 062774b0..8f246887 100644 --- a/src/macro/eval/evalExceptions.ml +++ b/src/macro/eval/evalExceptions.ml @@ -67,9 +67,12 @@ let format_pos p = let uncaught_exception_string v p extra = (Printf.sprintf "%s : Uncaught exception %s%s" (format_pos p) (value_string v) extra) -let get_exc_error_message ctx v stack p = +let get_exc_error_stack ctx stack = let pl = List.map (fun env -> {pfile = rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) stack in - let pl = List.filter (fun p -> p <> null_pos) pl in + List.filter (fun p -> p <> null_pos) pl + +let get_exc_error_message ctx v stack p = + let pl = get_exc_error_stack ctx stack in match pl with | [] -> uncaught_exception_string v p "" @@ -116,13 +119,29 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p = final(); Some v with - | RunTimeException(v,stack,p') -> + | RunTimeException(v,eval_stack,p') -> eval.caught_exception <- vnull; Option.may (build_exception_stack ctx) env; eval.env <- env; if is v key_haxe_macro_Error then begin let v1 = field v key_exception_message in let v2 = field v key_pos in + let v3 = field v key_child_errors in + let stack = match v3 with + | VArray sub -> + List.map (fun v -> + if is v key_haxe_macro_Error then begin + let v1 = field v key_exception_message in + let v2 = match (field v key_pos) with + | VInstance {ikind=IPos p} -> p + | _ -> null_pos + in + (Error.Custom (value_string v1), v2) + end else + Error.typing_error "Something went wrong" null_pos + ) (EvalArray.to_list sub) + | _ -> [] + in reset_ctx(); final(); match v1 with @@ -143,20 +162,27 @@ let catch_exceptions ctx ?(final=(fun() -> ())) f p = ) | _ -> null_pos in - raise (Error.Error (Error.Custom s.sstring,p)) + (match stack with + | [] -> raise (Error.Error (Error.Custom s.sstring,p,0)) + | _ -> raise (Error.Error (Stack ((Error.Custom (s.sstring),p) :: stack),p,0)) + ); | _ -> - Error.error "Something went wrong" null_pos + Error.typing_error "Something went wrong" null_pos end else begin (* Careful: We have to get the message before resetting the context because toString() might access it. *) - let stack = match stack with + let stack = match eval_stack with | [] -> [] | l when p' = null_pos -> l (* If the exception position is null_pos, we're "probably" in a built-in function. *) | _ :: l -> l (* Otherwise, ignore topmost frame position. *) in - let msg = get_exc_error_message ctx v stack (if p' = null_pos then p else p') in + let stack = get_exc_error_stack ctx stack in reset_ctx(); final(); - Error.error msg null_pos + let p = if p' = null_pos then p else p' in + raise (Error.Error (Stack ( + (Error.Custom ("Uncaught exception " ^ (value_string v)),p) + :: (List.map (fun p -> ((Error.Custom "Called from here"),p)) stack) + ),p,0)) end | MacroApi.Abort -> final(); diff --git a/src/macro/eval/evalField.ml b/src/macro/eval/evalField.ml index b78dbd45..d4d7b050 100644 --- a/src/macro/eval/evalField.ml +++ b/src/macro/eval/evalField.ml @@ -37,8 +37,11 @@ let rec proto_field_raise proto name = let instance_field vi name = vi.ifields.(get_instance_field_index_raise vi.iproto name) -let object_field_raise o name = - o.ofields.(get_instance_field_index_raise o.oproto name) +let object_field_raise o name = match o.oproto with + | OProto proto -> + o.ofields.(get_instance_field_index_raise proto name) + | ODictionary l -> + IntMap.find name l let object_field o name = try object_field_raise o name with Not_found -> vnull diff --git a/src/macro/eval/evalHash.ml b/src/macro/eval/evalHash.ml index 09e920c0..bab9e2a8 100644 --- a/src/macro/eval/evalHash.ml +++ b/src/macro/eval/evalHash.ml @@ -94,6 +94,7 @@ let key_sys_io_FileOutput = hash "sys.io.FileOutput" let key_sys_io_FileInput = hash "sys.io.FileInput" let key_haxe_io_Eof = hash "haxe.io.Eof" let key_haxe_macro_ExprDef = hash "haxe.macro.ExprDef" +let key_haxe_macro_EFieldKind = hash "haxe.macro.EFieldKind" let key_haxe_macro_Binop = hash "haxe.macro.Binop" let key_haxe_macro_Unop = hash "haxe.macro.Unop" let key_haxe_macro_Constant = hash "haxe.macro.Constant" @@ -114,6 +115,11 @@ let key_haxe_macro_AnonStatus = hash "haxe.macro.AnonStatus" let key_haxe_macro_ImportMode = hash "haxe.macro.ImportMode" let key_haxe_macro_QuoteStatus = hash "haxe.macro.QuoteStatus" let key_haxe_macro_DisplayKind = hash "haxe.macro.DisplayKind" +let key_haxe_macro_DisplayMode = hash "haxe.macro.DisplayMode" +let key_haxe_macro_CapturePolicy = hash "haxe.macro.CapturePolicy" +let key_haxe_macro_VarScope = hash "haxe.macro.VarScope" +let key_haxe_macro_VarScopingFlags = hash "haxe.macro.VarScopingFlags" +let key_haxe_macro_PackageRule = hash "haxe.macro.PackageRule" let key_haxe_macro_Message = hash "haxe.macro.Message" let key_haxe_macro_FunctionKind = hash "haxe.macro.FunctionKind" let key_haxe_macro_StringLiteralKind = hash "haxe.macro.StringLiteralKind" @@ -197,9 +203,10 @@ let key_fspare = hash "fspare" let key_kind = hash "kind" let key_end = hash "end" let key_events = hash "events" +let key_child_errors = hash "childErrors" let key_isInternal = hash "isInternal" let key_physical = hash "physical" let key_address = hash "address" let key_netmask = hash "netmask" let key_previous = hash "previous" -let key_current = hash "current" \ No newline at end of file +let key_current = hash "current" diff --git a/src/macro/eval/evalIntegers.ml b/src/macro/eval/evalIntegers.ml index e603e934..dde2afed 100644 --- a/src/macro/eval/evalIntegers.ml +++ b/src/macro/eval/evalIntegers.ml @@ -29,7 +29,7 @@ let decode_haxe_i64 v = let high = decode_i32 (vi.ifields.(get_instance_field_index_raise vi.iproto key_high)) and low = decode_i32 (vi.ifields.(get_instance_field_index_raise vi.iproto key_low)) in let high64 = GInt64.shift_left (Int32.to_int64 high) 32 - and low64 = Int32.to_int64 low in + and low64 = GInt64.logand (Int32.to_int64 low) 0xffffffffL in GInt64.logor high64 low64 | _ -> unexpected_value v "haxe.Int64" @@ -267,4 +267,4 @@ let int64_fields = [ let i = decode_i64 v in VInt64 (Int64.lognot i) ); -] \ No newline at end of file +] diff --git a/src/macro/eval/evalJit.ml b/src/macro/eval/evalJit.ml index b3e82e3c..6218f394 100644 --- a/src/macro/eval/evalJit.ml +++ b/src/macro/eval/evalJit.ml @@ -235,7 +235,7 @@ and jit_expr jit return e = List.iter (fun var -> ignore(get_capture_slot jit var)) jit_closure.captures_outside_scope; let captures = ExtList.List.filter_map (fun (i,vid,declared) -> if declared then None - else Some (i,fst (try Hashtbl.find jit.captures vid with Not_found -> Error.error "Something went wrong" e.epos)) + else Some (i,fst (try Hashtbl.find jit.captures vid with Not_found -> Error.typing_error "Something went wrong" e.epos)) ) captures in let mapping = Array.of_list captures in emit_closure ctx mapping eci hasret exec fl @@ -429,14 +429,18 @@ and jit_expr jit return e = let i = get_proto_field_index proto name in lazy (match proto.pfields.(i) with VFunction (f,_) -> f | v -> cannot_call v e.epos) in - let instance_call c = + let jit_with_null_check ef = let exec = jit_expr jit false ef in + emit_null_check exec ef.epos + in + let instance_call c = + let exec = jit_with_null_check ef in let proto = get_instance_prototype ctx (path_hash c.cl_path) ef.epos in let v = lazy_proto_field proto in emit_proto_field_call v (exec :: execs) e.epos in let default () = - let exec = jit_expr jit false ef in + let exec = jit_with_null_check ef in emit_method_call exec name execs e.epos in begin match fa with @@ -472,7 +476,7 @@ and jit_expr jit return e = end else default() | _ -> - let exec = jit_expr jit false ef in + let exec = jit_with_null_check ef in emit_field_call exec name execs e.epos end | TConst TSuper -> @@ -507,7 +511,7 @@ and jit_expr jit return e = | TNew({cl_path=["eval"],"Vector"},_,[e1]) -> begin match e1.eexpr with | TConst (TInt i32) -> - emit_new_vector_int (Int32.to_int i32) + emit_new_vector_int (Int32.to_int i32) e1.epos | _ -> let exec1 = jit_expr jit false e1 in emit_new_vector exec1 e1.epos @@ -633,7 +637,7 @@ and jit_expr jit return e = | TParenthesis e1 | TMeta(_,e1) | TCast(e1,None) -> loop e1 | TIdent s -> - Error.error ("Unknown identifier: " ^ s) e.epos + Error.typing_error ("Unknown identifier: " ^ s) e.epos in let f = loop e in begin match ctx.debug.debug_socket with diff --git a/src/macro/eval/evalLuv.ml b/src/macro/eval/evalLuv.ml index 40f29ea1..990d7923 100644 --- a/src/macro/eval/evalLuv.ml +++ b/src/macro/eval/evalLuv.ml @@ -92,6 +92,8 @@ let encode_uv_error (e:Error.t) = | `ENOTTY -> 75 | `EFTYPE -> 76 | `EILSEQ -> 77 + | `EOVERFLOW -> 78 + | `ESOCKTNOSUPPORT -> 79 ) let decode_uv_error v : Error.t = @@ -174,6 +176,8 @@ let decode_uv_error v : Error.t = | 75 -> `ENOTTY | 76 -> `EFTYPE | 77 -> `EILSEQ + | 78 -> `EOVERFLOW + | 79 -> `ESOCKTNOSUPPORT | _ -> unexpected_value v "eval.luv.UVError" let luv_exception e = @@ -542,11 +546,20 @@ let uv_error_fields = [ Error.set_on_unhandled_exception (fun ex -> let msg = match ex with - | HaxeError.Error (Custom msg,_) -> + | HaxeError.Error (Custom msg,_,_) -> (* Eval interpreter rethrows runtime exceptions as `Custom "Exception message\nException stack"` *) (try fst (ExtString.String.split msg "\n") with _ -> msg) - | HaxeError.Error (err,_) -> HaxeError.error_msg err + | HaxeError.Error (err,p,_) -> + (* TODO hook global error reporting *) + (match (extract_located (HaxeError.error_msg p err)) with + | [] -> "" + | (s,_) :: [] -> s + | (s,_) :: stack -> + List.fold_left (fun acc (s,p) -> + Printf.sprintf "%s%s\n" acc (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") p) + ) (s ^ "\n") stack + ); | _ -> Printexc.to_string ex in let e = create_haxe_exception ~stack:(get_ctx()).exception_stack msg in @@ -1579,9 +1592,9 @@ module F = struct end let file_fields = [ - "get_stdin", VHandle (HFile File.stdin); - "get_stdout", VHandle (HFile File.stdout); - "get_stderr", VHandle (HFile File.stderr); + "stdin", VHandle (HFile File.stdin); + "stdout", VHandle (HFile File.stdout); + "stderr", VHandle (HFile File.stderr); "createRequest", vfun0 (fun() -> VHandle (HFileRequest (File.Request.make())) ); @@ -2141,6 +2154,10 @@ let env_fields = [ and value = decode_native_string v2 in encode_unit_result (Env.setenv name ~value) ); + "unsetEnv", vfun1 (fun v -> + let name = decode_string v in + encode_unit_result (Env.unsetenv name) + ); "environ", vfun0 (fun() -> let encode env = let map = diff --git a/src/macro/eval/evalMain.ml b/src/macro/eval/evalMain.ml index 83a3ea80..70a82813 100644 --- a/src/macro/eval/evalMain.ml +++ b/src/macro/eval/evalMain.ml @@ -123,12 +123,15 @@ let create com api is_macro = (* eval *) toplevel = vobject { ofields = [||]; - oproto = fake_proto key_eval_toplevel; + oproto = OProto (fake_proto key_eval_toplevel); }; eval = eval; evals = evals; exception_stack = []; max_stack_depth = int_of_string (Common.defined_value_safe ~default:"1000" com Define.EvalCallStackDepth); + max_print_depth = int_of_string (Common.defined_value_safe ~default:"5" com Define.EvalPrintDepth); + print_indentation = match Common.defined_value_safe com Define.EvalPrettyPrint + with | "" -> None | "1" -> Some " " | indent -> Some indent; } in if debug.support_debugger && not !GlobalState.debugger_initialized then begin (* Let's wait till the debugger says we're good to continue. This allows it to finish configuration. @@ -148,7 +151,16 @@ let create com api is_macro = | _ -> let msg = match ex with - | Error.Error (err,_) -> Error.error_msg err + | Error.Error (err,p,_) -> + (* TODO hook global error reporting *) + (match (extract_located (Error.error_msg p err)) with + | [] -> "" + | (s,_) :: [] -> s + | (s,_) :: stack -> + List.fold_left (fun acc (s,p) -> + Printf.sprintf "%s%s\n" acc (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") p) + ) (s ^ "\n") stack + ); | _ -> Printexc.to_string ex in Printf.eprintf "%s\n" msg; @@ -398,25 +410,43 @@ let set_error ctx b = let add_types ctx types ready = if not ctx.had_error then ignore(catch_exceptions ctx (fun () -> ignore(add_types ctx types ready)) null_pos) -let compiler_error msg pos = +let make_runtime_error msg pos = let vi = encode_instance key_haxe_macro_Error in match vi with | VInstance i -> - let msg = EvalString.create_unknown msg in - set_instance_field i key_exception_message msg; + let s = EvalString.create_unknown msg in + set_instance_field i key_exception_message s; set_instance_field i key_pos (encode_pos pos); - set_instance_field i key_native_exception msg; - let ctx = get_ctx() in - let eval = get_eval ctx in - (match eval.env with - | Some _ -> - let stack = EvalStackTrace.make_stack_value (call_stack eval) in - set_instance_field i key_native_stack stack; - | None -> ()); - exc vi + set_instance_field i key_native_exception s; + vi | _ -> die "" __LOC__ +let compiler_error msg = + let pos = extract_located_pos msg in + let items = extract_located msg in + let vi = make_runtime_error (fst (List.hd items)) pos in + match vi with + | VInstance i -> + (match items with + | [] | _ :: [] -> + let ctx = get_ctx() in + let eval = get_eval ctx in + (match eval.env with + | Some _ -> + let stack = EvalStackTrace.make_stack_value (call_stack eval) in + set_instance_field i key_native_stack stack; + | None -> ()); + + | (hd :: stack) -> + let stack = List.map (fun (s,p) -> make_runtime_error s p) stack in + set_instance_field i key_child_errors (encode_array stack); + ); + + exc vi + | _ -> + die "" __LOC__ + let rec value_to_expr v p = let path i = let mt = IntMap.find i (get_ctx()).type_cache in @@ -424,7 +454,7 @@ let rec value_to_expr v p = let rec loop = function | [] -> die "" __LOC__ | [name] -> (EConst (Ident name),p) - | name :: l -> (EField (loop l,name),p) + | name :: l -> (efield (loop l,name),p) in let t = t_infos t in loop (List.rev (if t.mt_module.m_path = t.mt_path then fst t.mt_path @ [snd t.mt_path] else fst t.mt_module.m_path @ [snd t.mt_module.m_path;snd t.mt_path])) @@ -439,7 +469,7 @@ let rec value_to_expr v p = | VNull -> (EConst (Ident "null"),p) | VTrue -> (EConst (Ident "true"),p) | VFalse -> (EConst (Ident "false"),p) - | VInt32 i -> (EConst (Int (Int32.to_string i)),p) + | VInt32 i -> (EConst (Int (Int32.to_string i,None)),p) | VFloat f -> haxe_float f p | VString s -> (EConst (String(s.sstring,SDoubleQuotes)),p) | VArray va -> (EArrayDecl (List.map (fun v -> value_to_expr v p) (EvalArray.to_list va)),p) @@ -459,7 +489,7 @@ let rec value_to_expr v p = | PEnum names -> fst (List.nth names e.eindex) | _ -> die "" __LOC__ in - (EField (expr, name), p) + (efield (expr, name), p) in begin match e.eargs with @@ -470,7 +500,7 @@ let rec value_to_expr v p = end | VInstance {ikind = IIntMap m} -> let el = IntHashtbl.fold (fun k v acc -> - let e_key = (EConst (Int (string_of_int k)),p) in + let e_key = (EConst (Int (string_of_int k, None)),p) in (make_map_entry e_key v) :: acc ) m [] in (EArrayDecl el,p) @@ -555,7 +585,7 @@ let handle_decoding_error f v t = | _ -> error "expected Bool" v end | TType(t,tl) -> - loop tabs (apply_params t.t_params tl t.t_type) v + loop tabs (apply_typedef t tl) v | TAbstract({a_path=["haxe";"macro"],"Position"},_) -> begin match v with | VInstance {ikind=IPos _} -> f "#pos" diff --git a/src/macro/eval/evalMisc.ml b/src/macro/eval/evalMisc.ml index 2fbd5e2b..def1e74c 100644 --- a/src/macro/eval/evalMisc.ml +++ b/src/macro/eval/evalMisc.ml @@ -39,7 +39,7 @@ let update_object_prototype o fields = let ctx = get_ctx() in let proto,fields = ctx.get_object_prototype (get_ctx()) fields in o.ofields <- Array.of_list (List.map snd fields); - o.oproto <- proto + o.oproto <- OProto proto (* Calls *) @@ -59,12 +59,38 @@ let set_instance_field vi name v2 = vi.ifields.(get_instance_field_index_raise vi.iproto name) <- v2 let set_object_field o name v2 = - try - o.ofields.(get_instance_field_index_raise o.oproto name) <- v2; - with Not_found -> - let fields = IntMap.fold (fun name i acc -> (name,o.ofields.(i)) :: acc) o.oproto.pinstance_names [] in - let fields = (name,v2) :: fields in - update_object_prototype o fields + match o.oproto with + | OProto proto -> + begin try + o.ofields.(get_instance_field_index_raise proto name) <- v2; + with Not_found -> + let fields = IntMap.fold (fun name i acc -> (name,o.ofields.(i)) :: acc) proto.pinstance_names [] in + let fields = (name,v2) :: fields in + update_object_prototype o fields + end + | ODictionary d -> + o.oproto <- ODictionary (IntMap.add name v2 d) + +(* Turns prototypes into dictionaries if the field doesn't exist. *) +let set_object_field_runtime o name v2 = + let update_dictionary d = + IntMap.add name v2 d + in + let make_dictionary proto = + IntMap.map (fun i -> o.ofields.(i)) proto.pinstance_names + in + match o.oproto with + | OProto proto -> + begin try + o.ofields.(get_instance_field_index_raise proto name) <- v2; + with Not_found -> + let d = make_dictionary proto in + let d = update_dictionary d in + o.oproto <- ODictionary d + end + | ODictionary d -> + let d = update_dictionary d in + o.oproto <- ODictionary d let set_bytes_length_field v1 v2 = match v1 with @@ -88,6 +114,10 @@ let set_field v1 name v2 = match vresolve v1 with | VInstance vi -> set_instance_field vi name v2 | _ -> unexpected_value v1 "object" +let set_field_runtime v1 name v2 = match vresolve v1 with + | VObject o -> set_object_field_runtime o name v2 + | _ -> set_field v1 name v2 + (* Equality/compare *) let fcmp (a:float) b = if a = b then CEq else if a < b then CInf else if a > b then CSup else CUndef @@ -218,15 +248,24 @@ let op_xor p v1 v2 = match v1,v2 with | _ -> invalid_binop OpXor v1 v2 p let op_shl p v1 v2 = match v1,v2 with - | VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_left i1 (Int32.to_int i2)) + | VInt32 i1,VInt32 i2 -> + let i2 = Int32.logand i2 i32_31 in + let i2 = Int32.to_int i2 in + vint32 (Int32.shift_left i1 i2) | _ -> invalid_binop OpShl v1 v2 p let op_shr p v1 v2 = match v1,v2 with - | VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_right i1 (Int32.to_int i2)) + | VInt32 i1,VInt32 i2 -> + let i2 = Int32.logand i2 i32_31 in + let i2 = Int32.to_int i2 in + vint32 (Int32.shift_right i1 i2) | _ -> invalid_binop OpShr v1 v2 p let op_ushr p v1 v2 = match v1,v2 with - | VInt32 i1,VInt32 i2 -> vint32 (Int32.shift_right_logical i1 (Int32.to_int i2)) + | VInt32 i1,VInt32 i2 -> + let i2 = Int32.logand i2 i32_31 in + let i2 = Int32.to_int i2 in + vint32 (Int32.shift_right_logical i1 i2) | _ -> invalid_binop OpUShr v1 v2 p let op_mod p v1 v2 = match v1,v2 with @@ -254,7 +293,7 @@ let get_binop_fun op p = match op with | OpShr -> op_shr p | OpUShr -> op_ushr p | OpMod -> op_mod p - | OpAssign | OpBoolAnd | OpBoolOr | OpAssignOp _ | OpInterval | OpArrow | OpIn -> die "" __LOC__ + | OpAssign | OpBoolAnd | OpBoolOr | OpAssignOp _ | OpInterval | OpArrow | OpIn | OpNullCoal -> die ~p "" __LOC__ let prepare_callback v n = match v with diff --git a/src/macro/eval/evalPrinting.ml b/src/macro/eval/evalPrinting.ml index 49f537a9..ec5f5745 100644 --- a/src/macro/eval/evalPrinting.ml +++ b/src/macro/eval/evalPrinting.ml @@ -49,31 +49,64 @@ let s_date d = let s_hash key = create_ascii (EvalHash.rev_hash key) -let rec s_object depth o = +let rec indent buf s n = + match n with + | 0 -> () + | _ -> begin + Buffer.add_string buf s; + indent buf s (n - 1) + end + +let rec s_object depth indent_level o = let fields = object_fields o in let buf = Buffer.create 0 in + let inner_indent_level = indent_level + 1 in + Buffer.add_string buf "{"; + (match (get_ctx()).print_indentation with + | None -> () + | Some s -> begin + Buffer.add_string buf "\n"; + indent buf s inner_indent_level + end); + List.iteri (fun i (k,v) -> - if i > 0 then Buffer.add_string buf ", "; + if i > 0 then begin + match (get_ctx()).print_indentation with + | None -> Buffer.add_string buf ", " + | Some s -> begin + Buffer.add_string buf ",\n"; + indent buf s inner_indent_level + end; + end; + Buffer.add_string buf (rev_hash k); Buffer.add_string buf ": "; - Buffer.add_string buf (s_value depth v).sstring; + Buffer.add_string buf (s_value ~indent_level:inner_indent_level depth v).sstring; ) fields; + + (match (get_ctx()).print_indentation with + | None -> () + | Some s -> begin + Buffer.add_string buf "\n"; + indent buf s indent_level + end); + Buffer.add_string buf "}"; let s = Buffer.contents buf in create_with_length s (try UTF8.length s with _ -> String.length s) -and s_array depth va = +and s_array depth indent_level va = join empty_string [ rbkopen; - EvalArray.join va (s_value depth) rcomma; + EvalArray.join va (s_value ~indent_level depth) rcomma; rbkclose; ] -and s_vector depth vv = +and s_vector depth indent_level vv = join empty_string [ rbkopen; - EvalArray.join (EvalArray.create vv) (s_value depth) rcomma; + EvalArray.join (EvalArray.create vv) (s_value ~indent_level depth) rcomma; rbkclose; ] @@ -85,7 +118,7 @@ and s_enum_ctor_name ve = end with Not_found -> "#unknown" -and s_enum_value depth ve = +and s_enum_value depth indent_level ve = let name = s_enum_ctor_name ve in match ve.eargs with | [||] -> create_ascii name @@ -93,7 +126,7 @@ and s_enum_value depth ve = join empty_string [ create_ascii name; rpopen; - join rcomma (Array.to_list (Array.map (s_value (depth + 1)) vl)); + join rcomma (Array.to_list (Array.map (s_value ~indent_level (depth + 1)) vl)); rpclose; ] @@ -102,12 +135,12 @@ and s_proto_kind proto = match proto.pkind with | PEnum _ -> join empty_string [create_ascii "Enum<"; s_hash proto.ppath; rgt] | PInstance | PObject -> die "" __LOC__ -and s_value depth v = +and s_value ?(indent_level=0) depth v = let call_to_string () = let vf = field_raise v EvalHash.key_toString in - s_value (depth + 1) (call_value_on v vf []) + s_value ~indent_level (depth + 1) (call_value_on v vf []) in - if depth > 5 then rstop + if depth > (get_ctx()).max_print_depth then rstop else match v with | VNull -> rnull | VInt32 i32 -> create_ascii(Int32.to_string i32) @@ -122,17 +155,17 @@ and s_value depth v = | VFunction (f,_) -> rfun | VFieldClosure _ -> rclosure | VHandle _ -> rhandle - | VEnumValue ve -> s_enum_value depth ve + | VEnumValue ve -> s_enum_value depth indent_level ve | VString s -> s | VNativeString s -> create_unknown_vstring s - | VArray va -> s_array (depth + 1) va - | VVector vv -> s_vector (depth + 1) vv + | VArray va -> s_array (depth + 1) indent_level va + | VVector vv -> s_vector (depth + 1) indent_level vv | VInstance {ikind=IDate d} -> s_date d | VInstance {ikind=IPos p} -> create_ascii ("#pos(" ^ Lexer.get_error_pos (Printf.sprintf "%s:%d:") p ^ ")") (* STODO: not ascii? *) | VInstance {ikind=IRegex r} -> r.r_rex_string | VInstance i -> (try call_to_string () with Not_found -> s_hash i.iproto.ppath) - | VObject o -> (try call_to_string () with Not_found -> s_object (depth + 1) o) - | VLazy f -> s_value depth (!f()) + | VObject o -> (try call_to_string () with Not_found -> s_object (depth + 1) indent_level o) + | VLazy f -> s_value ~indent_level depth (!f()) | VPrototype proto -> try call_to_string() diff --git a/src/macro/eval/evalPrototype.ml b/src/macro/eval/evalPrototype.ml index 008bfd99..e0789891 100644 --- a/src/macro/eval/evalPrototype.ml +++ b/src/macro/eval/evalPrototype.ml @@ -289,16 +289,19 @@ let create_instance_prototype ctx c = let get_object_prototype ctx l = let l = List.sort (fun (i1,_) (i2,_) -> if i1 = i2 then 0 else if i1 < i2 then -1 else 1) l in - let sfields = String.concat "," (List.map (fun (i,_) -> (Printf.sprintf ":%s" (rev_hash i))) l) in - let name = hash (Printf.sprintf "eval.object.Object[%s]" sfields) in - try - IntMap.find name ctx.instance_prototypes,l - with Not_found -> - let pctx = PrototypeBuilder.create ctx name None PObject None in - List.iter (fun (name,_) -> PrototypeBuilder.add_instance_field pctx name (lazy vnull)) l; - let proto = fst (PrototypeBuilder.finalize pctx) in - ctx.instance_prototypes <- IntMap.add name proto ctx.instance_prototypes; - proto,l + let proto = + let sfields = String.concat "," (List.map (fun (i,_) -> (Printf.sprintf ":%s" (rev_hash i))) l) in + let name = hash (Printf.sprintf "eval.object.Object[%s]" sfields) in + try + IntMap.find name ctx.instance_prototypes + with Not_found -> + let pctx = PrototypeBuilder.create ctx name None PObject None in + List.iter (fun (name,_) -> PrototypeBuilder.add_instance_field pctx name (lazy vnull)) l; + let proto = fst (PrototypeBuilder.finalize pctx) in + ctx.instance_prototypes <- IntMap.add name proto ctx.instance_prototypes; + proto + in + proto,l let add_types ctx types ready = let t = Timer.timer [(if ctx.is_macro then "macro" else "interp");"add_types"] in diff --git a/src/macro/eval/evalStdLib.ml b/src/macro/eval/evalStdLib.ml index 1fa44fa2..50138756 100644 --- a/src/macro/eval/evalStdLib.ml +++ b/src/macro/eval/evalStdLib.ml @@ -239,7 +239,7 @@ module StdArray = struct ) let toString = vifun0 (fun vthis -> - vstring (s_array 0 (this vthis)) + vstring (s_array 0 0 (this vthis)) ) let unshift = vifun1 (fun vthis v -> @@ -440,7 +440,12 @@ module StdBytes = struct ) let toString = vifun0 (fun vthis -> - (create_unknown (Bytes.to_string (this vthis))) + let this = this vthis in + try + UTF8.validate (Bytes.unsafe_to_string this); + (create_unknown (Bytes.to_string this)) + with UTF8.Malformed_code -> + exc_string "Invalid string" ) end @@ -754,18 +759,18 @@ module StdDeque = struct end module StdEReg = struct - open Pcre + open Pcre2 let create r opt = - let open Pcre in + let open Pcre2 in let string_of_pcre_error = function | BadPattern(s,i) -> Printf.sprintf "at %i: %s" i s | Partial -> "Partial" - | BadPartial -> "BadPartial" - | BadUTF8 -> "BadUTF8" - | BadUTF8Offset -> "BadUTF8Offset" + | BadUTF -> "BadUTF" + | BadUTFOffset -> "BadUTFOffset" | MatchLimit -> "MatchLimit" - | RecursionLimit -> "RecursionLimit" + | DepthLimit -> "DepthLimit" + | WorkspaceSize -> "WorkspaceSize" | InternalError s -> "InternalError: " ^ s in let global = ref false in @@ -777,7 +782,7 @@ module StdEReg = struct | 'g' -> global := true; None | c -> failwith ("Unsupported regexp option '" ^ String.make 1 c ^ "'") ) (ExtString.String.explode opt) in - let flags = `UTF8 :: `UCP :: flags in + let flags = `UTF :: `UCP :: flags in let rex = try regexp ~flags r with Error error -> failwith (string_of_pcre_error error) in let pcre = { r = rex; @@ -844,17 +849,17 @@ module StdEReg = struct let match' = vifun1 (fun vthis s -> let this = this vthis in - let open Pcre in + let open Pcre2 in let s = decode_string s in this.r_string <- s; try - let a = exec_all ~iflags:0x2000 ~rex:this.r s in + let a = exec_all ~flags:[`NO_UTF_CHECK] ~rex:this.r s in this.r_groups <- a; vtrue with Not_found -> this.r_groups <- [||]; vfalse - | Pcre.Error _ -> + | Pcre2.Error _ -> exc_string "PCRE Error (invalid unicode string?)" ) @@ -908,7 +913,7 @@ module StdEReg = struct begin try if pos + len > String.length s then raise Not_found; let str = String.sub s 0 (pos + len) in - let a = Pcre.exec_all ~iflags:0x2000 ~rex:this.r ~pos str in + let a = Pcre2.exec_all ~flags:[`NO_UTF_CHECK] ~rex:this.r ~pos str in this.r_string <- s; this.r_groups <- a; vtrue @@ -921,7 +926,7 @@ module StdEReg = struct let this = this vthis in let s = decode_string s in let by = decode_string by in - let s = (if this.r_global then Pcre.replace else Pcre.replace_first) ~iflags:0x2000 ~rex:this.r ~templ:by s in + let s = (if this.r_global then Pcre2.replace else Pcre2.replace_first) ~flags:[`NO_UTF_CHECK] ~rex:this.r ~templ:by s in create_unknown s ) @@ -938,11 +943,11 @@ module StdEReg = struct let sub = String.sub s first (last - first) in DynArray.add acc (create_unknown sub) in - let exec = Pcre.exec ~iflags:0x2000 ~rex:this.r in + let exec = Pcre2.exec ~flags:[`NO_UTF_CHECK] ~rex:this.r in let step pos = try let substrings = exec ~pos s in - let (first,last) = Pcre.get_substring_ofs substrings 0 in + let (first,last) = Pcre2.get_substring_ofs substrings 0 in add !copy_offset first; copy_offset := last; let next_start = if pos = last then last + 1 else last in @@ -1396,7 +1401,7 @@ module StdHost = struct let resolve = vfun1 (fun name -> let name = decode_string name in - let h = catch_unix_error Unix.gethostbyname name in + let h = try Unix.gethostbyname name with Not_found -> exc_string (Printf.sprintf "Could not resolve host %s" name) in let addr = catch_unix_error Unix.string_of_inet_addr h.h_addr_list.(0) in let a, b, c, d = Scanf.sscanf addr "%d.%d.%d.%d" (fun a b c d -> a,b,c,d) in vint32 (Int32.logor (Int32.shift_left (Int32.of_int a) 24) (Int32.of_int (d lor (c lsl 8) lor (b lsl 16)))) @@ -1538,7 +1543,7 @@ module StdIntMap = struct let l = IntHashtbl.fold (fun key vvalue acc -> (join empty_string [create_ascii (string_of_int key); create_ascii " => "; s_value 0 vvalue]) :: acc) this [] in let s = join rcomma l in - let s = join empty_string [rbropen;s;rbrclose] in + let s = join empty_string [rbkopen;s;rbkclose] in vstring s ) @@ -1597,7 +1602,7 @@ module StdStringMap = struct let l = StringHashtbl.fold (fun _ (key,vvalue) acc -> (join empty_string [key; create_ascii " => "; s_value 0 vvalue]) :: acc) this [] in let s = join rcomma l in - let s = join empty_string [rbropen;s;rbrclose] in + let s = join empty_string [rbkopen;s;rbkclose] in vstring s ) @@ -1655,7 +1660,7 @@ module StdObjectMap = struct let l = ValueHashtbl.fold (fun key vvalue acc -> (join empty_string [s_value 0 key; create_ascii " => "; s_value 0 vvalue]) :: acc) this [] in let s = join rcomma l in - let s = join empty_string [rbropen;s;rbrclose] in + let s = join empty_string [rbkopen;s;rbkclose] in vstring s ) @@ -1882,19 +1887,26 @@ module StdReflect = struct let name = hash (decode_vstring name).sstring in match vresolve o with | VObject o -> - let found = ref false in - let fields = IntMap.fold (fun name' i acc -> - if name = name' then begin - found := true; - acc + begin match o.oproto with + | OProto proto -> + let found = ref false in + let fields = IntMap.fold (fun name' i acc -> + if name = name' then begin + found := true; + acc + end else + (name',o.ofields.(i)) :: acc + ) proto.pinstance_names [] in + if !found then begin + update_object_prototype o fields; + vtrue end else - (name',o.ofields.(i)) :: acc - ) o.oproto.pinstance_names [] in - if !found then begin - update_object_prototype o fields; - vtrue - end else - vfalse + vfalse + | ODictionary d -> + let has = IntMap.mem name d in + if has then o.oproto <- ODictionary (IntMap.remove name d); + vbool has + end | _ -> vfalse ) @@ -1931,10 +1943,14 @@ module StdReflect = struct let hasField = vfun2 (fun o field -> let name = hash (decode_vstring field).sstring in let b = match vresolve o with - | VObject o -> IntMap.mem name o.oproto.pinstance_names + | VObject o -> + begin match o.oproto with + | OProto proto -> IntMap.mem name proto.pinstance_names + | ODictionary d -> IntMap.mem name d + end | VInstance vi -> IntMap.mem name vi.iproto.pinstance_names || IntMap.mem name vi.iproto.pnames | VPrototype proto -> IntMap.mem name proto.pnames - | _ -> unexpected_value o "object" + | _ -> false (* issue #10993 *) in vbool b ) @@ -1960,7 +1976,7 @@ module StdReflect = struct ) let setField = vfun3 (fun o name v -> - (try set_field o (hash (decode_vstring name).sstring) v with Not_found -> ()); vnull + (try set_field_runtime o (hash (decode_vstring name).sstring) v with Not_found -> ()); vnull ) let setProperty = vfun3 (fun o name v -> @@ -1969,7 +1985,7 @@ module StdReflect = struct let vset = field o name_set in if vset <> VNull then call_value_on o vset [v] else begin - (try set_field o (hash name.sstring) v with Not_found -> ()); + (try set_field_runtime o (hash name.sstring) v with Not_found -> ()); vnull end ) @@ -2574,8 +2590,6 @@ module StdSys = struct ) let exit = vfun1 (fun code -> - (* TODO: Borrowed from interp.ml *) - if (get_ctx()).curapi.use_cache() then raise (Error.Fatal_error ("",Globals.null_pos)); raise (Sys_exit(decode_int code)); ) @@ -2626,11 +2640,14 @@ module StdSys = struct | _ -> vnull ) - let putEnv = vfun2 (fun s v -> - let s = decode_string s in - let v = decode_string v in - catch_unix_error Unix.putenv s v; - vnull + let putEnv = vfun2 (fun s -> function + | v when v = vnull -> + let _ = Luv.Env.unsetenv (decode_string s) in vnull + | v -> + let s = decode_string s in + let v = decode_string v in + catch_unix_error Unix.putenv s v; + vnull ) let setCwd = vfun1 (fun s -> @@ -2668,12 +2685,12 @@ module StdSys = struct (match !cached_sys_name with | Some n -> n | None -> - let ic = catch_unix_error Unix.open_process_in "uname" in + let ic, pid = catch_unix_error Process_helper.open_process_args_in_pid "uname" [| "uname" |] in let uname = (match input_line ic with | "Darwin" -> "Mac" | n -> n ) in - close_in ic; + Pervasives.ignore (Process_helper.close_process_in_pid (ic, pid)); cached_sys_name := Some uname; uname) | "Win32" | "Cygwin" -> "Windows" @@ -3331,7 +3348,7 @@ let init_empty_constructors builtins = Hashtbl.add h key_Array (fun () -> encode_array_instance (EvalArray.create [||])); Hashtbl.add h key_eval_Vector (fun () -> encode_vector_instance (Array.make 0 vnull)); Hashtbl.add h key_Date (fun () -> encode_instance key_Date ~kind:(IDate 0.)); - Hashtbl.add h key_EReg (fun () -> encode_instance key_EReg ~kind:(IRegex {r = Pcre.regexp ""; r_rex_string = create_ascii "~//"; r_global = false; r_string = ""; r_groups = [||]})); + Hashtbl.add h key_EReg (fun () -> encode_instance key_EReg ~kind:(IRegex {r = Pcre2.regexp ""; r_rex_string = create_ascii "~//"; r_global = false; r_string = ""; r_groups = [||]})); Hashtbl.add h key_String (fun () -> v_empty_string); Hashtbl.add h key_haxe_ds_StringMap (fun () -> encode_instance key_haxe_ds_StringMap ~kind:(IStringMap (StringHashtbl.create ()))); Hashtbl.add h key_haxe_ds_IntMap (fun () -> encode_instance key_haxe_ds_IntMap ~kind:(IIntMap (IntHashtbl.create ()))); @@ -3823,4 +3840,4 @@ let init_standard_library builtins = init_fields builtins (["eval";"luv";"_Prepare"], "Prepare_Impl_") EvalLuv.prepare_fields []; init_fields builtins (["eval";"luv";"_Check"], "Check_Impl_") EvalLuv.check_fields []; init_fields builtins (["eval";"luv"], "Version") EvalLuv.version_fields []; - EvalSsl.init_fields init_fields builtins \ No newline at end of file + EvalSsl.init_fields init_fields builtins diff --git a/src/macro/eval/evalValue.ml b/src/macro/eval/evalValue.ml index 78c20abc..88f65678 100644 --- a/src/macro/eval/evalValue.ml +++ b/src/macro/eval/evalValue.ml @@ -75,11 +75,11 @@ module IntHashtbl = struct end type vregex = { - r : Pcre.regexp; + r : Pcre2.regexp; r_rex_string : vstring; r_global : bool; mutable r_string : string; - mutable r_groups : Pcre.substrings array; + mutable r_groups : Pcre2.substrings array; } type vzlib = { @@ -154,9 +154,13 @@ and vobject = { (* The fields of the object known when it is created. *) mutable ofields : value array; (* The prototype of the object. *) - mutable oproto : vprototype; + mutable oproto : vobject_proto; } +and vobject_proto = + | OProto of vprototype + | ODictionary of value IntMap.t + and vprototype = { (* The path of the prototype. Using rev_hash on this gives the original dot path. *) ppath : int; diff --git a/src/macro/macroApi.ml b/src/macro/macroApi.ml index 25c97514..d106b155 100644 --- a/src/macro/macroApi.ml +++ b/src/macro/macroApi.ml @@ -1,10 +1,19 @@ open Ast +open DisplayTypes.DisplayMode open Type open Common +open DefineList +open MetaList +open Globals exception Invalid_expr exception Abort +type compiler_options = { + opt_inlining : bool option; + opt_transform : bool option; +} + (** Our access to the compiler from the macro api **) @@ -12,16 +21,19 @@ exception Abort type 'value compiler_api = { pos : Globals.pos; get_com : unit -> Common.context; + get_macro_stack : unit -> pos list; + init_macros_done : unit -> bool; get_type : string -> Type.t option; get_module : string -> Type.t list; + after_init_macros : (unit -> unit) -> unit; after_typing : (module_type list -> unit) -> unit; on_generate : (Type.t list -> unit) -> bool -> unit; after_generate : (unit -> unit) -> unit; on_type_not_found : (string -> 'value) -> unit; parse_string : string -> Globals.pos -> bool -> Ast.expr; + parse : 'a . ((Ast.token * Globals.pos) Stream.t -> 'a) -> string -> 'a; type_expr : Ast.expr -> Type.texpr; resolve_type : Ast.complex_type -> Globals.pos -> t; - type_macro_expr : Ast.expr -> Type.texpr; store_typed_expr : Type.texpr -> Ast.expr; allow_package : string -> unit; type_patch : string -> string -> bool -> string option -> unit; @@ -44,17 +56,25 @@ type 'value compiler_api = { format_string : string -> Globals.pos -> Ast.expr; cast_or_unify : Type.t -> texpr -> Globals.pos -> bool; add_global_metadata : string -> string -> (bool * bool * bool) -> pos -> unit; + register_define : string -> Define.user_define -> unit; + register_metadata : string -> Meta.user_meta -> unit; add_module_check_policy : string list -> int list -> bool -> int -> unit; decode_expr : 'value -> Ast.expr; encode_expr : Ast.expr -> 'value; encode_ctype : Ast.type_hint -> 'value; decode_type : 'value -> t; flush_context : (unit -> t) -> t; + info : ?depth:int -> string -> pos -> unit; + warning : ?depth:int -> Warning.warning -> string -> pos -> unit; + display_error : ?depth:int -> (string -> pos -> unit); + with_imports : 'a . import list -> placed_name list list -> (unit -> 'a) -> 'a; + with_options : 'a . compiler_options -> (unit -> 'a) -> 'a; } type enum_type = | IExpr + | IEFieldKind | IBinop | IUnop | IConst @@ -75,6 +95,11 @@ type enum_type = | IQuoteStatus | IImportMode | IDisplayKind + | IDisplayMode + | ICapturePolicy + | IVarScope + | IVarScopingFlags + | IPackageRule | IMessage | IFunctionKind | IStringLiteralKind @@ -131,7 +156,7 @@ module type InterpApi = sig val encode_ref : 'a -> ('a -> value) -> (unit -> string) -> value val decode_ref : value -> 'a - val compiler_error : string -> Globals.pos -> 'a + val compiler_error : Globals.located -> 'a val error_message : string -> 'a val value_to_expr : value -> Globals.pos -> Ast.expr val value_signature : value -> string @@ -150,55 +175,20 @@ module type InterpApi = sig val get_api_call_pos : unit -> pos end -let enum_name = function - | IExpr -> "ExprDef" - | IBinop -> "Binop" - | IUnop -> "Unop" - | IConst -> "Constant" - | ITParam -> "TypeParam" - | ICType -> "ComplexType" - | IField -> "FieldType" - | IType -> "Type" - | IFieldKind -> "FieldKind" - | IMethodKind -> "MethodKind" - | IVarAccess -> "VarAccess" - | IAccess -> "Access" - | IClassKind -> "ClassKind" - | ITypedExpr -> "TypedExprDef" - | ITConstant -> "TConstant" - | IModuleType -> "ModuleType" - | IFieldAccess -> "FieldAccess" - | IAnonStatus -> "AnonStatus" - | IImportMode -> "ImportMode" - | IQuoteStatus -> "QuoteStatus" - | IDisplayKind -> "DisplayKind" - | IMessage -> "Message" - | IFunctionKind -> "FunctionKind" - | IStringLiteralKind -> "StringLiteralKind" - -let all_enums = - let last = IImportMode in - let rec loop i = - let e : enum_type = Obj.magic i in - if e = last then [e] else e :: loop (i + 1) - in - loop 0 - - let s_type_path = Globals.s_type_path (* convert float value to haxe expression, handling inf/-inf/nan *) let haxe_float f p = let std = (Ast.EConst (Ast.Ident "std"), p) in - let math = (Ast.EField (std, "Math"), p) in + let math = (efield (std, "Math"), p) in if (f = infinity) then - (Ast.EField (math, "POSITIVE_INFINITY"), p) + (efield (math, "POSITIVE_INFINITY"), p) else if (f = neg_infinity) then - (Ast.EField (math, "NEGATIVE_INFINITY"), p) + (efield (math, "NEGATIVE_INFINITY"), p) else if (f <> f) then - (Ast.EField (math, "NaN"), p) + (efield (math, "NaN"), p) else - (Ast.EConst (Ast.Float (Numeric.float_repres f)), p) + (Ast.EConst (Ast.Float (Numeric.float_repres f, None)), p) (* ------------------------------------------------------------------------------------------------------------- *) (* Our macro api functor *) @@ -226,8 +216,8 @@ let encode_string_literal_kind qs = let encode_const c = let tag, pl = match c with - | Int s -> 0, [encode_string s] - | Float s -> 1, [encode_string s] + | Int (s, suffix) -> 0, [encode_string s;null encode_string suffix] + | Float (s, suffix) -> 1, [encode_string s;null encode_string suffix] | String(s,qs) -> 2, [encode_string s;encode_string_literal_kind qs] | Ident s -> 3, [encode_string s] | Regexp (s,opt) -> 4, [encode_string s;encode_string opt] @@ -260,6 +250,7 @@ let rec encode_binop op = | OpInterval -> 21, [] | OpArrow -> 22, [] | OpIn -> 23, [] + | OpNullCoal -> 24, [] in encode_enum IBinop tag pl @@ -289,7 +280,15 @@ let encode_import (path,mode) = let encode_placed_name (s,p) = encode_string s -let rec encode_path (t,p) = +(* Globals.path *) +let encode_path (p,n) = + encode_obj [ + "pack", encode_array (List.map encode_string p); + "name", encode_string n; + ] + +(* Ast.placed_type_path *) +let rec encode_ast_path (t,p) = let fields = [ "pack", encode_array (List.map encode_string t.tpackage); "name", encode_string t.tname; @@ -349,7 +348,7 @@ and encode_field (f:class_field) = and encode_ctype t = let tag, pl = match fst t with | CTPath p -> - 0, [encode_path (p,Globals.null_pos)] + 0, [encode_ast_path (p,Globals.null_pos)] | CTFunction (pl,r) -> 1, [encode_array (List.map encode_ctype pl);encode_ctype r] | CTAnonymous fl -> @@ -357,7 +356,7 @@ and encode_ctype t = | CTParent t -> 3, [encode_ctype t] | CTExtend (tl,fields) -> - 4, [encode_array (List.map encode_path tl); encode_array (List.map encode_field fields)] + 4, [encode_array (List.map encode_ast_path tl); encode_array (List.map encode_field fields)] | CTOptional t -> 5, [encode_ctype t] | CTNamed (n,t) -> @@ -405,14 +404,114 @@ and encode_display_kind dk = in encode_enum ~pos:None IDisplayKind tag pl -and encode_message msg = - let tag, pl = match msg with - | CMInfo(msg,p) -> 0, [(encode_string msg); (encode_pos p)] - | CMWarning(msg,p) -> 1, [(encode_string msg); (encode_pos p)] - | CMError(_,_) -> Globals.die "" __LOC__ +and encode_display_mode dm = + let tag, pl = match dm with + | DMNone -> 0, [] + | DMDefault -> 1, [] + | DMDefinition -> 2, [] + | DMTypeDefinition -> 3, [] + | DMImplementation -> 4, [] + | DMPackage -> 5, [] + | DMHover -> 6, [] + | DMUsage (withDefinition,findDescendants,findBase) -> 7, [(vbool withDefinition); (vbool findDescendants); (vbool findBase)] + | DMModuleSymbols None -> 8, [] + | DMModuleSymbols (Some s) -> 9, [(encode_string s)] + | DMSignature -> 10, [] + in + encode_enum ~pos:None IDisplayMode tag pl + +(** encoded to haxe.display.Display.Platform, an enum abstract of String *) +and encode_platform p = + encode_string (platform_name p) + +and encode_platform_config pc = + encode_obj [ + "staticTypeSystem", vbool pc.pf_static; + "sys", vbool pc.pf_sys; + "capturePolicy", encode_capture_policy pc.pf_capture_policy; + "padNulls", vbool pc.pf_pad_nulls; + "addFinalReturn", vbool pc.pf_add_final_return; + "overloadFunctions", vbool pc.pf_overload; + "canSkipNonNullableArgument", vbool pc.pf_can_skip_non_nullable_argument; + "reservedTypePaths", encode_array (List.map encode_path pc.pf_reserved_type_paths); + "supportsFunctionEquality", vbool pc.pf_supports_function_equality; + "usesUtf16", vbool pc.pf_uses_utf16; + "thisBeforeSuper", vbool pc.pf_this_before_super; + "supportsThreads", vbool pc.pf_supports_threads; + "supportsUnicode", vbool pc.pf_supports_unicode; + "supportsRestArgs", vbool pc.pf_supports_rest_args; + "exceptions", encode_exceptions_config pc.pf_exceptions; + "scoping", encode_var_scoping_config pc.pf_scoping; + "supportsAtomics", vbool pc.pf_supports_atomics; + ] + +and encode_capture_policy cp = + let tag = match cp with + | CPNone -> 0 + | CPWrapRef -> 1 + | CPLoopVars -> 2 + in + encode_enum ~pos:None ICapturePolicy tag [] + +and encode_var_scoping_config vsc = + encode_obj [ + "scope", encode_var_scope vsc.vs_scope; + "flags", encode_array (List.map encode_var_scoping_flags vsc.vs_flags); + ] + +and encode_var_scope vs = + let tag = match vs with + | FunctionScope -> 0 + | BlockScope -> 1 + in + encode_enum ~pos:None IVarScope tag [] + +and encode_var_scoping_flags vsf = + let tag, pl = match vsf with + | VarHoisting -> 0, [] + | NoShadowing -> 1, [] + | NoCatchVarShadowing -> 2, [] + | ReserveCurrentTopLevelSymbol -> 3, [] + | ReserveAllTopLevelSymbols -> 4, [] + | ReserveAllTypesFlat -> 5, [] + | ReserveNames (names) -> 6, [encode_array (List.map encode_string names)] + | SwitchCasesNoBlocks -> 7, [] + in + encode_enum ~pos:None IVarScopingFlags tag pl + +and encode_exceptions_config ec = + encode_obj [ + "nativeThrows", encode_array (List.map encode_path ec.ec_native_throws); + "nativeCatches", encode_array (List.map encode_path ec.ec_native_catches); + "avoidWrapping", vbool ec.ec_avoid_wrapping; + "wildcardCatch", encode_path ec.ec_wildcard_catch; + "baseThrow", encode_path ec.ec_base_throw; + (* skipping "specialThrow" since cannot use "decode_texpr" here *) + ] + +and encode_package_rule pr = + let tag, pl = match pr with + | Forbidden -> 0, [] + | Directory (path) -> 1, [encode_string path] + | Remap (path) -> 2, [encode_string path] + in + encode_enum ~pos:None IPackageRule tag pl + +and encode_message cm = + let tag, pl = match cm.cm_severity with + | Globals.MessageSeverity.Information -> 0, [(encode_string cm.cm_message); (encode_pos cm.cm_pos)] + | Warning | Hint -> 1, [(encode_string cm.cm_message); (encode_pos cm.cm_pos)] + | Error -> Globals.die "" __LOC__ in encode_enum ~pos:None IMessage tag pl +and encode_efield_kind efk = + let i = match efk with + | EFNormal -> 0 + | EFSafe -> 1 + in + encode_enum IEFieldKind i [] + and encode_expr e = let rec loop (e,p) = let tag, pl = match e with @@ -422,8 +521,8 @@ and encode_expr e = 1, [loop e1;loop e2] | EBinop (op,e1,e2) -> 2, [encode_binop op;loop e1;loop e2] - | EField (e,f) -> - 3, [loop e;encode_string f] + | EField (e,f,efk) -> + 3, [loop e;encode_string f;encode_efield_kind efk] | EParenthesis e -> 4, [loop e] | EObjectDecl fl -> @@ -438,7 +537,7 @@ and encode_expr e = | ECall (e,el) -> 7, [loop e;encode_array (List.map loop el)] | ENew (p,el) -> - 8, [encode_path p; encode_array (List.map loop el)] + 8, [encode_ast_path p; encode_array (List.map loop el)] | EUnop (op,flag,e) -> 9, [encode_unop op; vbool (match flag with Prefix -> false | Postfix -> true); loop e] | EVars vl -> @@ -447,6 +546,7 @@ and encode_expr e = "name",encode_placed_name v.ev_name; "name_pos",encode_pos (pos v.ev_name); "isFinal",vbool v.ev_final; + "isStatic",vbool v.ev_static; "type",null encode_ctype v.ev_type; "expr",null loop v.ev_expr; "meta",encode_meta_content v.ev_meta; @@ -495,16 +595,14 @@ and encode_expr e = 23, [loop e; null encode_ctype t] | EDisplay (e,dk) -> 24, [loop e; encode_display_kind dk] - | EDisplayNew t -> - 25, [encode_path t] | ETernary (econd,e1,e2) -> - 26, [loop econd;loop e1;loop e2] + 25, [loop econd;loop e1;loop e2] | ECheckType (e,t) -> - 27, [loop e; encode_ctype t] + 26, [loop e; encode_ctype t] | EMeta (m,e) -> - 28, [encode_meta_entry m;loop e] + 27, [encode_meta_entry m;loop e] | EIs (e,t) -> - 29, [loop e;encode_ctype t] + 28, [loop e;encode_ctype t] in encode_obj [ "pos", encode_pos p; @@ -549,8 +647,20 @@ let decode_string_literal_kind v = let decode_const c = match decode_enum c with - | 0, [s] -> Int (decode_string s) - | 1, [s] -> Float (decode_string s) + | 0, [s;suffix] -> + let decoded_suffix = opt decode_string suffix in + (match decoded_suffix with + | None | Some "i32" | Some "i64" | Some "u32" -> + Int (decode_string s, decoded_suffix) + | Some other -> + raise Invalid_expr) + | 1, [s;suffix] -> + let decoded_suffix = opt decode_string suffix in + (match decoded_suffix with + | None | Some "f64" -> + Float (decode_string s, opt decode_string suffix) + | Some other -> + raise Invalid_expr) | 2, [s;qs] -> String (decode_string s,decode_string_literal_kind qs) | 3, [s] -> Ident (decode_string s) | 4, [s;opt] -> Regexp (decode_string s, decode_string opt) @@ -583,6 +693,7 @@ let rec decode_op op = | 21, [] -> OpInterval | 22,[] -> OpArrow | 23,[] -> OpIn + | 24,[] -> OpNullCoal | _ -> raise Invalid_expr let decode_unop op = @@ -614,7 +725,8 @@ let decode_placed_name vp v = let decode_opt_array f v = if v = vnull then [] else List.map f (decode_array v) -let rec decode_path t = +(* Ast.placed_type_path *) +let rec decode_ast_path t = let p = field t "pos" in let pack = List.map decode_string (decode_array (field t "pack")) and name = decode_string (field t "name") @@ -633,6 +745,7 @@ and decode_tparams v = and decode_tparam_decl v = let vconstraints = field v "constraints" in + let vdefault = field v "defaultType" in { tp_name = decode_placed_name (field v "name_pos") (field v "name"); tp_constraints = if vconstraints = vnull then None else (match decode_array vconstraints with @@ -640,6 +753,7 @@ and decode_tparam_decl v = | [t] -> Some (decode_ctype t) | tl -> Some (CTIntersection (List.map decode_ctype tl),Globals.null_pos) ); + tp_default = opt decode_ctype vdefault; tp_params = decode_tparams (field v "params"); tp_meta = decode_meta_content (field v "meta"); } @@ -710,7 +824,7 @@ and decode_ctype t = let (i,args),p = decode_enum_with_pos t in (match i,args with | 0, [p] -> - CTPath (fst (decode_path p)) + CTPath (fst (decode_ast_path p)) | 1, [a;r] -> CTFunction (List.map decode_ctype (decode_array a), decode_ctype r) | 2, [fl] -> @@ -718,7 +832,7 @@ and decode_ctype t = | 3, [t] -> CTParent (decode_ctype t) | 4, [tl;fl] -> - CTExtend (List.map decode_path (decode_array tl), List.map decode_field (decode_array fl)) + CTExtend (List.map decode_ast_path (decode_array tl), List.map decode_field (decode_array fl)) | 5, [t] -> CTOptional (decode_ctype t) | 6, [n;t] -> @@ -754,8 +868,15 @@ and decode_expr v = EArray (loop e1, loop e2) | 2, [op;e1;e2] -> EBinop (decode_op op, loop e1, loop e2) - | 3, [e;f] -> - EField (loop e, decode_string f) + | 3, [e;f;efk] -> + let efk = if efk == vnull then + EFNormal + else match decode_enum efk with + | 0,[] -> EFNormal + | 1,[] -> EFSafe + | _ -> raise Invalid_expr + in + EField (loop e, decode_string f, efk) | 4, [e] -> EParenthesis (loop e) | 5, [a] -> @@ -775,19 +896,21 @@ and decode_expr v = | 7, [e;el] -> ECall (loop e,List.map loop (decode_array el)) | 8, [t;el] -> - ENew (decode_path t,List.map loop (decode_array el)) + ENew (decode_ast_path t,List.map loop (decode_array el)) | 9, [op;f;e] -> EUnop (decode_unop op,(if decode_bool f then Postfix else Prefix),loop e) | 10, [vl] -> EVars (List.map (fun v -> let vfinal = field v "isFinal" in let final = if vfinal == vnull then false else decode_bool vfinal in + let vstatic = field v "isStatic" in + let static = if vstatic == vnull then false else decode_bool vstatic in let vmeta = field v "meta" in let meta = if vmeta == vnull then [] else decode_meta_content vmeta in let name = (decode_placed_name (field v "name_pos") (field v "name")) and t = opt decode_ctype (field v "type") and eo = opt loop (field v "expr") in - mk_evar ~final ?t ?eo ~meta name + mk_evar ~final ~static ?t ?eo ~meta name ) (decode_array vl)) | 11, [kind;f] -> EFunction (decode_function_kind kind,decode_fun f) @@ -803,7 +926,7 @@ and decode_expr v = let cases = List.map (fun c -> (List.map loop (decode_array (field c "values")),opt loop (field c "guard"),opt loop (field c "expr"),maybe_decode_pos (field c "pos")) ) (decode_array cases) in - ESwitch (loop e,cases,opt (fun v -> (if field v "expr" = vnull then None else Some (decode_expr v)),Globals.null_pos) eo) + ESwitch (loop e,cases,opt (fun v -> (if field v "expr" = vnull then None else Some (decode_expr v)),p) eo) | 17, [e;catches] -> let catches = List.map (fun c -> ((decode_placed_name (field c "name_pos") (field c "name")),(opt decode_ctype (field c "type")),loop (field c "expr"),maybe_decode_pos (field c "pos")) @@ -823,15 +946,13 @@ and decode_expr v = ECast (loop e,opt decode_ctype t) | 24, [e;dk] -> EDisplay (loop e,decode_display_kind dk) - | 25, [t] -> - EDisplayNew (decode_path t) - | 26, [e1;e2;e3] -> + | 25, [e1;e2;e3] -> ETernary (loop e1,loop e2,loop e3) - | 27, [e;t] -> + | 26, [e;t] -> ECheckType (loop e, (decode_ctype t)) - | 28, [m;e] -> + | 27, [m;e] -> EMeta (decode_meta_entry m,loop e) - | 29, [e;t] -> + | 28, [e;t] -> EIs (loop e,decode_ctype t) | _ -> raise Invalid_expr @@ -902,7 +1023,13 @@ let rec encode_mtype t fields = ] @ fields) and encode_type_params tl = - encode_array (List.map (fun (n,t) -> encode_obj ["name",encode_string n;"t",encode_type t]) tl) + encode_array (List.map (fun tp -> + encode_obj [ + "name",encode_string tp.ttp_name; + "t",encode_type tp.ttp_type; + "defaultType",(match tp.ttp_default with None -> vnull | Some t -> encode_type t); + ] + ) tl) and encode_tenum e = encode_mtype (TEnumDecl e) [ @@ -917,7 +1044,7 @@ and encode_tabstract a = "type", encode_type a.a_this; "impl", (match a.a_impl with None -> vnull | Some c -> encode_clref c); "binops", encode_array (List.map (fun (op,cf) -> encode_obj [ "op",encode_binop op; "field",encode_cfield cf]) a.a_ops); - "unops", encode_array (List.map (fun (op,postfix,cf) -> encode_obj [ "op",encode_unop op; "isPostfix",vbool (match postfix with Postfix -> true | Prefix -> false); "field",encode_cfield cf]) a.a_unops); + "unops", encode_array (List.map (fun (op,postfix,cf) -> encode_obj [ "op",encode_unop op; "postFix",vbool (match postfix with Postfix -> true | Prefix -> false); "field",encode_cfield cf]) a.a_unops); "from", encode_array ((List.map (fun t -> encode_obj [ "t",encode_type t; "field",vnull]) a.a_from) @ (List.map (fun (t,cf) -> encode_obj [ "t",encode_type t; "field",encode_cfield cf]) a.a_from_field)); "to", encode_array ((List.map (fun t -> encode_obj [ "t",encode_type t; "field",vnull]) a.a_to) @ (List.map (fun (t,cf) -> encode_obj [ "t",encode_type t; "field",encode_cfield cf]) a.a_to_field)); "array", encode_array (List.map encode_cfield a.a_array); @@ -1084,11 +1211,10 @@ and encode_type t = 4 , [encode_array pl; encode_type ret] | TAnon a -> 5, [encode_ref a encode_tanon (fun() -> "")] - | TDynamic tsub as t -> - if t == t_dynamic then - 6, [vnull] - else - 6, [encode_type tsub] + | TDynamic None -> + 6, [vnull] + | TDynamic (Some tsub) -> + 6, [encode_type tsub] | TLazy f -> loop (lazy_type f) | TAbstract (a, pl) -> @@ -1132,7 +1258,7 @@ and decode_type t = | 3, [t; pl] -> TType (decode_ref t, List.map decode_type (decode_array pl)) | 4, [pl; r] -> TFun (List.map (fun p -> decode_string (field p "name"), decode_bool (field p "opt"), decode_type (field p "t")) (decode_array pl), decode_type r) | 5, [a] -> TAnon (decode_ref a) - | 6, [t] -> if t = vnull then t_dynamic else TDynamic (decode_type t) + | 6, [t] -> if t = vnull then t_dynamic else TDynamic (Some (decode_type t)) | 7, [f] -> TLazy (decode_lazytype f) | 8, [a; pl] -> TAbstract (decode_ref a, List.map decode_type (decode_array pl)) | _ -> raise Invalid_expr @@ -1284,7 +1410,12 @@ let decode_tconst c = | _ -> raise Invalid_expr let decode_type_params v = - List.map (fun v -> decode_string (field v "name"),decode_type (field v "t")) (decode_array v) + List.map (fun v -> + let name = decode_string (field v "name") in + let t = decode_type (field v "t") in + let default = opt decode_type (field v "defaultType") in + mk_type_param name t default + ) (decode_array v) let decode_tvar v = (Obj.obj (decode_unsafe (field v "$")) : tvar) @@ -1384,8 +1515,9 @@ let rec decode_tfunc v = and decode_texpr v = let rec loop v = - mk (decode (field v "expr")) (decode_type (field v "t")) (decode_pos (field v "pos")) - and decode e = + let p = decode_pos (field v "pos") in + mk (decode (field v "expr") p) (decode_type (field v "t")) p + and decode e p = match decode_enum e with | 0, [c] -> TConst(decode_tconst c) | 1, [v] -> TLocal(decode_tvar v) @@ -1394,7 +1526,7 @@ and decode_texpr v = | 4, [v1;fa] -> TField(loop v1,decode_field_access fa) | 5, [mt] -> TTypeExpr(decode_module_type mt) | 6, [v1] -> TParenthesis(loop v1) - | 7, [v] -> TObjectDecl(List.map (fun v -> (decode_string (field v "name"),Globals.null_pos,NoQuotes),loop (field v "expr")) (decode_array v)) + | 7, [v] -> TObjectDecl(List.map (fun v -> (decode_string (field v "name"),p,NoQuotes),loop (field v "expr")) (decode_array v)) | 8, [vl] -> TArrayDecl(List.map loop (decode_array vl)) | 9, [v1;vl] -> TCall(loop v1,List.map loop (decode_array vl)) | 10, [c;tl;vl] -> TNew(decode_ref c,List.map decode_type (decode_array tl),List.map loop (decode_array vl)) @@ -1469,14 +1601,14 @@ let decode_type_def v = in EEnum (mk (if isExtern then [EExtern] else []) (List.map conv fields)) | 1, [] -> - ETypedef (mk (if isExtern then [EExtern] else []) (CTAnonymous fields,Globals.null_pos)) + ETypedef (mk (if isExtern then [EExtern] else []) (CTAnonymous fields,pos)) | 2, [ext;impl;interf;final;abstract] -> let flags = if isExtern then [HExtern] else [] in let is_interface = decode_opt_bool interf in let is_final = decode_opt_bool final in let is_abstract = decode_opt_bool abstract in - let interfaces = (match opt (fun v -> List.map decode_path (decode_array v)) impl with Some l -> l | _ -> [] ) in - let flags = (match opt decode_path ext with None -> flags | Some t -> HExtends t :: flags) in + let interfaces = (match opt (fun v -> List.map decode_ast_path (decode_array v)) impl with Some l -> l | _ -> [] ) in + let flags = (match opt decode_ast_path ext with None -> flags | Some t -> HExtends t :: flags) in let flags = if is_interface then begin let flags = HInterface :: flags in List.map (fun t -> HExtends t) interfaces @ flags @@ -1561,27 +1693,45 @@ let macro_api ccom get_api = "current_pos", vfun0 (fun() -> encode_pos (get_api()).pos ); - "error", vfun2 (fun msg p -> + "get_macro_stack", vfun0 (fun () -> + let stack = ((get_api()).get_macro_stack ()) in + encode_array (List.map encode_pos stack) + ); + "init_macros_done", vfun0 (fun () -> + vbool ((get_api()).init_macros_done ()) + ); + "error", vfun3 (fun msg p depth -> let msg = decode_string msg in let p = decode_pos p in - (ccom()).error msg p; + let depth = decode_int depth in + (get_api()).display_error ~depth msg p; raise Abort ); - "fatal_error", vfun2 (fun msg p -> + "fatal_error", vfun3 (fun msg p depth -> let msg = decode_string msg in let p = decode_pos p in - raise (Error.Fatal_error (msg,p)) + let depth = decode_int depth in + raise (Error.Fatal_error ((Globals.located msg p),depth)) ); - "warning", vfun2 (fun msg p -> + "report_error", vfun3 (fun msg p depth -> let msg = decode_string msg in let p = decode_pos p in - (ccom()).warning msg p; + let depth = decode_int depth in + (get_api()).display_error ~depth msg p; vnull ); - "info", vfun2 (fun msg p -> + "warning", vfun3 (fun msg p depth -> let msg = decode_string msg in let p = decode_pos p in - (ccom()).info msg p; + let depth = decode_int depth in + (get_api()).warning ~depth WUser msg p; + vnull + ); + "info", vfun3 (fun msg p depth -> + let msg = decode_string msg in + let p = decode_pos p in + let depth = decode_int depth in + (get_api()).info ~depth msg p; vnull ); "get_messages", vfun0 (fun() -> @@ -1604,22 +1754,21 @@ let macro_api ccom get_api = "define", vfun2 (fun s v -> let s = decode_string s in let com = ccom() in - if com.stage <> CInitMacrosStart then begin - let v = if v = vnull then "" else ", " ^ (decode_string v) in - com.warning ("Should be used in initialization macros only: haxe.macro.Compiler.define(" ^ s ^ v ^ ")") Globals.null_pos; - end; - let v = if v = vnull then "" else "=" ^ (decode_string v) in - Common.raw_define com (s ^ v); + (* TODO: use external_define and external_define_value for #8690 *) + if v = vnull then + Common.external_define_no_check com s + else + Common.external_define_value_no_check com s (decode_string v); vnull ); "defined", vfun1 (fun s -> - vbool (Common.raw_defined (ccom()) (decode_string s)) + vbool (Common.external_defined (ccom()) (decode_string s)) ); "defined_value", vfun1 (fun s -> - try encode_string (Common.raw_defined_value (ccom()) (decode_string s)) with Not_found -> vnull + try encode_string (Common.external_defined_value (ccom()) (decode_string s)) with Not_found -> vnull ); "get_defines", vfun0 (fun() -> - encode_string_map encode_string (ccom()).defines.Define.values + encode_string_map encode_string (Common.defines_for_external (ccom())) ); "get_type", vfun1 (fun s -> let tname = decode_string s in @@ -1630,6 +1779,11 @@ let macro_api ccom get_api = "get_module", vfun1 (fun s -> encode_array (List.map encode_type ((get_api()).get_module (decode_string s))) ); + "on_after_init_macros", vfun1 (fun f -> + let f = prepare_callback f 1 in + (get_api()).after_init_macros (fun tl -> ignore(f [])); + vnull + ); "on_after_typing", vfun1 (fun f -> let f = prepare_callback f 1 in (get_api()).after_typing (fun tl -> ignore(f [encode_array (List.map encode_module_type tl)])); @@ -1704,6 +1858,56 @@ let macro_api ccom get_api = (get_api()).add_global_metadata (decode_string s1) (decode_string s2) (decode_bool b1,decode_bool b2,decode_bool b3) (get_api_call_pos()); vnull ); + "register_define_impl", vfun2 (fun d src -> + let flags : define_parameter list = [] in + + let platforms = decode_opt_array decode_string (field d "platforms") in + let flags = match platforms with + | [] -> flags + | _ ->(Platforms (List.map (fun p -> (Globals.parse_platform p)) platforms)) :: flags + in + + let params = decode_opt_array decode_string (field d "params") in + let flags = List.append flags (List.map (fun p -> (HasParam p : define_parameter)) params) in + + let links = decode_opt_array decode_string (field d "links") in + let flags = List.append flags (List.map (fun l -> (Link l : define_parameter)) links) in + + (get_api()).register_define (decode_string (field d "define")) { + doc = decode_string (field d "doc"); + flags = flags; + source = opt decode_string src; + }; + vnull + ); + "register_metadata_impl", vfun2 (fun m src -> + let flags : meta_parameter list = [] in + + let platforms = decode_opt_array decode_string (field m "platforms") in + let flags = + if (List.length platforms) = 0 then flags + else (Platforms (List.map (fun p -> (Globals.parse_platform p)) platforms)) :: flags + in + + let targets = decode_opt_array decode_string (field m "targets") in + let flags = + if (List.length targets) = 0 then flags + else (UsedOn (List.map MetaList.parse_meta_usage targets)) :: flags + in + + let params = decode_opt_array decode_string (field m "params") in + let flags = List.append flags (List.map (fun p -> HasParam p) params) in + + let links = decode_opt_array decode_string (field m "links") in + let flags = List.append flags (List.map (fun l -> Link l) links) in + + (get_api()).register_metadata (decode_string (field m "metadata")) { + doc = decode_string (field m "doc"); + flags = flags; + source = opt decode_string src; + }; + vnull + ); "set_custom_js_generator", vfun1 (fun f -> let f = prepare_callback f 1 in (get_api()).set_js_generator (fun js_ctx -> @@ -1759,8 +1963,8 @@ let macro_api ccom get_api = ); "flush_disk_cache", vfun0 (fun () -> let com = (get_api()).get_com() in - Hashtbl.clear com.file_lookup_cache; - Hashtbl.clear com.readdir_cache; + com.file_lookup_cache#clear; + com.readdir_cache#clear; vnull ); "get_pos_infos", vfun1 (fun p -> @@ -1831,7 +2035,7 @@ let macro_api ccom get_api = | TAbstract _ | TEnum _ | TInst _ | TFun _ | TAnon _ | TDynamic _ -> t | TType (t,tl) -> - apply_params t.t_params tl t.t_type + apply_typedef t tl | TLazy f -> lazy_type f in @@ -1848,15 +2052,17 @@ let macro_api ccom get_api = (get_api()).define_type v (opt decode_string m); vnull ); + "make_monomorph", vfun0 (fun() -> + let t = TMono (Monomorph.create ()) in + encode_type t + ); "define_module", vfun4 (fun path vl ui ul -> - (get_api()).define_module (decode_string path) (decode_array vl) (List.map decode_import (decode_array ui)) (List.map fst (List.map decode_path (decode_array ul))); + (get_api()).define_module (decode_string path) (decode_array vl) (List.map decode_import (decode_array ui)) (List.map fst (List.map decode_ast_path (decode_array ul))); vnull ); "add_class_path", vfun1 (fun cp -> let com = ccom() in let cp = decode_string cp in - if com.stage <> CInitMacrosStart then - com.warning ("Should be used in initialization macros only: haxe.macro.Compiler.addClassPath(" ^ cp ^ ")") Globals.null_pos; let cp = Path.add_trailing_slash cp in com.class_path <- cp :: com.class_path; (match com.get_macros() with @@ -1864,15 +2070,13 @@ let macro_api ccom get_api = mcom.class_path <- cp :: mcom.class_path; | None -> ()); - Hashtbl.clear com.file_lookup_cache; - Hashtbl.clear com.readdir_cache; + com.file_lookup_cache#clear; + com.readdir_cache#clear; vnull ); "add_native_lib", vfun1 (fun file -> let file = decode_string file in let com = ccom() in - if com.stage <> CInitMacrosStart then - com.warning ("Should be used in initialization macros only: haxe.macro.Compiler.addNativeLib(" ^ file ^ ")") Globals.null_pos; NativeLibraryHandler.add_native_lib com file false (); vnull ); @@ -1902,6 +2106,13 @@ let macro_api ccom get_api = let e = decode_texpr e in encode_expr ((get_api()).store_typed_expr e) ); + "type_and_store_expr", vfun1 (fun e -> + let api = get_api() in + let te = (api.type_expr (decode_expr e)) in + let v_e = encode_expr (api.store_typed_expr te) in + let v_to_string () = (s_type (print_context())) te.etype in + encode_obj ["expr",v_e;"type",encode_ref te.etype encode_type v_to_string] + ); "get_output", vfun0 (fun() -> encode_string (ccom()).file ); @@ -1916,14 +2127,49 @@ let macro_api ccom get_api = else encode_obj ["file",encode_string p.Globals.pfile;"pos",vint p.Globals.pmin] ); + "get_display_mode", vfun0 (fun() -> + encode_display_mode !Parser.display_mode + ); + "get_configuration", vfun0 (fun() -> + let com = ccom() in + encode_obj [ + "version", vint com.version; + "args", encode_array (List.map encode_string com.args); + "debug", vbool com.debug; + "verbose", vbool com.verbose; + "foptimize", vbool com.foptimize; + "platform", encode_platform com.platform; + "platformConfig", encode_platform_config com.config; + "stdPath", encode_array (List.map encode_string com.std_path); + "mainClass", (match com.main_class with None -> vnull | Some path -> encode_path path); + "packageRules", encode_string_map encode_package_rule com.package_rules; + ] + ); + "get_main_expr", vfun0 (fun() -> + match (ccom()).main with None -> vnull | Some e -> encode_texpr e + ); + "get_module_types", vfun0 (fun() -> + encode_array (List.map encode_module_type (ccom()).types) + ); + "type_to_module_type", vfun1 (fun(t) -> + encode_module_type (module_type_of_type (decode_type t)) + ); + "module_type_to_type", vfun1 (fun(t) -> + encode_type (type_of_module_type (decode_module_type t)) + ); "apply_params", vfun3 (fun tpl tl t -> let tl = List.map decode_type (decode_array tl) in - let tpl = List.map (fun v -> decode_string (field v "name"), decode_type (field v "t")) (decode_array tpl) in + let tpl = List.map (fun v -> + let name = decode_string (field v "name") in + let t = decode_type (field v "t") in + let default = None in (* we don't care here *) + mk_type_param name t default + ) (decode_array tpl) in let rec map t = match t with | TInst({cl_kind = KTypeParameter _},_) -> begin try (* use non-physical equality check here to make apply_params work *) - snd (List.find (fun (_,t2) -> type_iseq t t2) tpl) + extract_param_type (List.find (fun tp2 -> type_iseq t tp2.ttp_type) tpl) with Not_found -> Type.map map t end @@ -1951,12 +2197,12 @@ let macro_api ccom get_api = vnull ); "server_invalidate_files", vfun1 (fun a -> - let cs = match CompilationServer.get() with Some cs -> cs | None -> failwith "compilation server not running" in let com = ccom() in + let cs = com.cs in List.iter (fun v -> let s = decode_string v in let s = com.file_keys#get s in - cs#taint_modules s; + cs#taint_modules s "server_invalidate_files"; cs#remove_files s; ) (decode_array a); vnull @@ -2003,8 +2249,40 @@ let macro_api ccom get_api = match map (fun t -> decode_type (fn [encode_type t])) (TAnon a) with | TAnon a -> encode_ref a encode_tanon (fun() -> "") | _ -> Globals.die "" __LOC__ - ) + ); + "with_imports", vfun3(fun imports usings f -> + let imports = List.map decode_string (decode_array imports) in + let imports = List.map ((get_api()).parse (fun s -> Grammar.parse_import' s Globals.null_pos)) imports in + let usings = List.map decode_string (decode_array usings) in + let usings = List.map ((get_api()).parse (fun s -> Grammar.parse_using' s Globals.null_pos)) usings in + let f = prepare_callback f 0 in + (get_api()).with_imports imports usings (fun () -> f []) + ); + "with_options", vfun2(fun opts f -> + let o = { + opt_inlining = opt decode_bool (field opts "allowInlining"); + opt_transform = opt decode_bool (field opts "allowTransform"); + } in + let f = prepare_callback f 0 in + (get_api()).with_options o (fun() -> f []); + ); + "set_var_name", vfun2(fun v name -> + let v = decode_tvar v in + let name = decode_string name in + v.v_name <- name; + vnull; + ); + "send_json", vfun1 (fun json -> + begin match (ccom()).json_out with + | Some api -> + let json = decode_string json in + let lexbuf = Sedlexing.Utf8.from_string json in + let parse = Json.Reader.read_json lexbuf in + api.send_result parse; + vbool true + | None -> + vbool false + end + ); ] - - end diff --git a/src/optimization/analyzer.ml b/src/optimization/analyzer.ml index b3a70eed..8b79d8d6 100644 --- a/src/optimization/analyzer.ml +++ b/src/optimization/analyzer.ml @@ -671,8 +671,14 @@ module LocalDce = struct add_var_flag v VUsed; (try expr (get_var_value ctx.graph v) with Not_found -> ()); begin match Ssa.get_reaching_def ctx.graph v with - | None -> use (get_var_origin ctx.graph v) - | Some v -> use v; + | None -> + (* We don't want to fully recurse for the origin variable because we don't care about its + reaching definition (issue #10972). Simply marking it as being used should be sufficient. *) + let v' = get_var_origin ctx.graph v in + if not (is_used v') then + add_var_flag v' VUsed + | Some v -> + use v; end end and expr e = match e.eexpr with @@ -684,9 +690,8 @@ module LocalDce = struct | _ -> Type.iter expr e in - let bb_marked = ref [] in let rec mark bb = - bb_marked := bb :: !bb_marked; + add_block_flag bb BlockDce; DynArray.iter expr bb.bb_el; DynArray.iter expr bb.bb_phi; terminator_iter expr bb.bb_terminator; @@ -694,11 +699,11 @@ module LocalDce = struct if not (has_flag edge FlagDce) then begin edge.cfg_flags <- FlagDce :: edge.cfg_flags; if not ctx.config.const_propagation || has_flag edge FlagExecutable then - mark edge.cfg_from; + mark edge.cfg_to; end - ) bb.bb_incoming + ) bb.bb_outgoing in - mark ctx.graph.g_exit; + mark ctx.graph.g_root; let rec sweep e = match e.eexpr with | TBinop(OpAssign,{eexpr = TLocal v},e2) | TVar(v,Some e2) when not (keep v) -> if has_side_effect e2 then @@ -710,10 +715,12 @@ module LocalDce = struct | _ -> Type.map_expr sweep e in - List.iter (fun bb -> - dynarray_map sweep bb.bb_el; - bb.bb_terminator <- terminator_map sweep bb.bb_terminator; - ) !bb_marked; + Graph.iter_dom_tree ctx.graph (fun bb -> + if has_block_flag bb BlockDce then begin + dynarray_map sweep bb.bb_el; + bb.bb_terminator <- terminator_map sweep bb.bb_terminator; + end + ) end module Debug = struct @@ -743,6 +750,8 @@ module Debug = struct | BKFunctionBegin _ -> "\n" | BKFunctionEnd -> "\n" | BKLoopHead -> "\n" + | BKCatch v -> Printf.sprintf ">" v.v_name v.v_id + | BKException -> "" | _ -> "" in Printf.fprintf ch "n%i [shape=box,label=\"%s%s\"];\n" bb.bb_id s_kind (s_escape s) @@ -781,8 +790,7 @@ module Debug = struct edge bb_then "then"; edge bb_else "else"; edge bb_next "next"; - | SEWhile(bb_head,bb_body,bb_next,_) -> - edge bb_head "loop-head"; + | SEWhile(bb_body,bb_next,_) -> edge bb_body "loop-body"; edge bb_next "next"; | SEMerge bb_next -> @@ -931,13 +939,20 @@ module Run = struct open AnalyzerConfig open Graph - let with_timer detailed s f = - let timer = Timer.timer (if detailed then "analyzer" :: s else ["analyzer"]) in + let with_timer level identifier s f = + let name = match level with + | 0 -> ["analyzer"] + | 1 -> "analyzer" :: s + | 2 when identifier = "" -> "analyzer" :: s + | 2 -> "analyzer" :: s @ [identifier] + | _ -> ["analyzer"] (* whatever *) + in + let timer = Timer.timer name in let r = f() in timer(); r - let create_analyzer_context com config e = + let create_analyzer_context com config identifier e = let g = Graph.create e.etype e.epos in let ctx = { com = com; @@ -946,12 +961,17 @@ module Run = struct (* For CPP we want to use variable names which are "probably" not used by users in order to avoid problems with the debugger, see https://github.com/HaxeFoundation/hxcpp/issues/365 *) temp_var_name = (match com.platform with Cpp -> "_hx_tmp" | _ -> "tmp"); + with_timer = (fun s f -> + with_timer config.detail_times identifier s f + ); + identifier = identifier; entry = g.g_unreachable; has_unbound = false; loop_counter = 0; loop_stack = []; debug_exprs = []; name_stack = []; + did_optimize = false; } in ctx @@ -960,7 +980,7 @@ module Run = struct let there actx e = if actx.com.debug then add_debug_expr actx "initial" e; - let e = with_timer actx.config.detail_times ["->";"filter-apply"] (fun () -> TexprFilter.apply actx.com e) in + let e = actx.with_timer ["->";"filter-apply"] (fun () -> TexprFilter.apply actx.com e) in if actx.com.debug then add_debug_expr actx "after filter-apply" e; let tf,t,is_real_function = match e.eexpr with | TFunction tf -> @@ -972,18 +992,18 @@ module Run = struct let tf = { tf_args = []; tf_type = t; tf_expr = e; } in tf,tfun [] t,false in - with_timer actx.config.detail_times ["->";"from-texpr"] (fun () -> AnalyzerTexprTransformer.from_tfunction actx tf t e.epos); + actx.with_timer ["->";"from-texpr"] (fun () -> AnalyzerTexprTransformer.from_tfunction actx tf t e.epos); is_real_function let back_again actx is_real_function = - let e = with_timer actx.config.detail_times ["<-";"to-texpr"] (fun () -> AnalyzerTexprTransformer.to_texpr actx) in + let e = actx.with_timer ["<-";"to-texpr"] (fun () -> AnalyzerTexprTransformer.to_texpr actx) in if actx.com.debug then add_debug_expr actx "after to-texpr" e; DynArray.iter (fun vi -> vi.vi_var.v_extra <- vi.vi_extra; ) actx.graph.g_var_infos; - let e = if actx.config.fusion then with_timer actx.config.detail_times ["<-";"fusion"] (fun () -> Fusion.apply actx.com actx.config e) else e in + let e = if actx.config.fusion then actx.with_timer ["<-";"fusion"] (fun () -> Fusion.apply actx e) else e in if actx.com.debug then add_debug_expr actx "after fusion" e; - let e = with_timer actx.config.detail_times ["<-";"cleanup"] (fun () -> Cleanup.apply actx.com e) in + let e = actx.with_timer ["<-";"cleanup"] (fun () -> Cleanup.apply actx.com e) in if actx.com.debug then add_debug_expr actx "after cleanup" e; let e = if is_real_function then e @@ -1037,26 +1057,27 @@ module Run = struct let run_on_expr actx e = let is_real_function = there actx e in - with_timer actx.config.detail_times ["->";"idom"] (fun () -> Graph.infer_immediate_dominators actx.graph); - with_timer actx.config.detail_times ["->";"infer_scopes"] (fun () -> Graph.infer_scopes actx.graph); - with_timer actx.config.detail_times ["->";"var writes"] (fun () -> Graph.infer_var_writes actx.graph); + actx.with_timer ["->";"idom"] (fun () -> Graph.infer_immediate_dominators actx.graph); + actx.with_timer ["->";"infer_scopes"] (fun () -> Graph.infer_scopes actx.graph); + actx.with_timer ["->";"var writes"] (fun () -> Graph.infer_var_writes actx.graph); if actx.com.debug then Graph.check_integrity actx.graph; if actx.config.optimize && not actx.has_unbound then begin - with_timer actx.config.detail_times ["optimize";"ssa-apply"] (fun () -> Ssa.apply actx); - if actx.config.const_propagation then with_timer actx.config.detail_times ["optimize";"const-propagation"] (fun () -> ConstPropagation.apply actx); - if actx.config.copy_propagation then with_timer actx.config.detail_times ["optimize";"copy-propagation"] (fun () -> CopyPropagation.apply actx); - with_timer actx.config.detail_times ["optimize";"local-dce"] (fun () -> LocalDce.apply actx); + actx.did_optimize <- true; + actx.with_timer ["optimize";"ssa-apply"] (fun () -> Ssa.apply actx); + if actx.config.const_propagation then actx.with_timer ["optimize";"const-propagation"] (fun () -> ConstPropagation.apply actx); + if actx.config.copy_propagation then actx.with_timer ["optimize";"copy-propagation"] (fun () -> CopyPropagation.apply actx); + actx.with_timer ["optimize";"local-dce"] (fun () -> LocalDce.apply actx); end; back_again actx is_real_function - let rec reduce_control_flow ctx e = - let e = Type.map_expr (reduce_control_flow ctx) e in - Optimizer.reduce_control_flow ctx e + let rec reduce_control_flow com e = + let e = Type.map_expr (reduce_control_flow com) e in + Optimizer.reduce_control_flow com e - let run_on_field ctx config c cf = match cf.cf_expr with - | Some e when not (is_ignored cf.cf_meta) && not (Typecore.is_removable_field ctx cf) -> - let config = update_config_from_meta ctx.Typecore.com config cf.cf_meta in - let actx = create_analyzer_context ctx.Typecore.com config e in + let run_on_field com config c cf = match cf.cf_expr with + | Some e when not (is_ignored cf.cf_meta) && not (Typecore.is_removable_field com cf) && not (has_class_field_flag cf CfPostProcessed) -> + let config = update_config_from_meta com config cf.cf_meta in + let actx = create_analyzer_context com config (Printf.sprintf "%s.%s" (s_type_path c.cl_path) cf.cf_name) e in let debug() = print_endline (Printf.sprintf "While analyzing %s.%s" (s_type_path c.cl_path) cf.cf_name); List.iter (fun (s,e) -> @@ -1067,33 +1088,35 @@ module Run = struct Debug.dot_debug actx c cf; print_endline (Printf.sprintf "dot graph written to %s" (String.concat "/" (Debug.get_dump_path actx c cf))); in + let maybe_debug () = match config.debug_kind with + | DebugNone -> () + | DebugDot -> Debug.dot_debug actx c cf; + | DebugFull -> debug() + in let e = try run_on_expr actx e with | Error.Error _ | Abort _ | Sys.Break as exc -> + maybe_debug(); raise exc | exc -> debug(); raise exc in - let e = reduce_control_flow ctx e in - begin match config.debug_kind with - | DebugNone -> () - | DebugDot -> Debug.dot_debug actx c cf; - | DebugFull -> debug() - end; + let e = reduce_control_flow com e in + maybe_debug(); cf.cf_expr <- Some e; | _ -> () - let run_on_field ctx config c cf = - run_on_field ctx config c cf; - List.iter (run_on_field ctx config c) cf.cf_overloads + let run_on_field com config c cf = + run_on_field com config c cf; + List.iter (run_on_field com config c) cf.cf_overloads - let run_on_class ctx config c = - let config = update_config_from_meta ctx.Typecore.com config c.cl_meta in + let run_on_class com config c = + let config = update_config_from_meta com config c.cl_meta in let process_field stat cf = match cf.cf_kind with | Var _ when not stat -> () - | _ -> run_on_field ctx config c cf + | _ -> run_on_field com config c cf in List.iter (process_field false) c.cl_ordered_fields; List.iter (process_field true) c.cl_ordered_statics; @@ -1107,7 +1130,7 @@ module Run = struct | Some e -> let tf = { tf_args = []; tf_type = e.etype; tf_expr = e; } in let e = mk (TFunction tf) (tfun [] e.etype) e.epos in - let actx = create_analyzer_context ctx.Typecore.com {config with optimize = false} e in + let actx = create_analyzer_context com {config with optimize = false} (Printf.sprintf "%s.__init__" (s_type_path c.cl_path)) e in let e = run_on_expr actx e in let e = match e.eexpr with | TFunction tf -> tf.tf_expr @@ -1116,29 +1139,28 @@ module Run = struct c.cl_init <- Some e end - let run_on_type ctx config t = + let run_on_type com config t = match t with | TClassDecl c when (is_ignored c.cl_meta) -> () - | TClassDecl c -> run_on_class ctx config c + | TClassDecl c -> run_on_class com config c | TEnumDecl _ -> () | TTypeDecl _ -> () | TAbstractDecl _ -> () - let run_on_types ctx types = - let com = ctx.Typecore.com in + let run_on_types com types = let config = get_base_config com in - with_timer config.detail_times ["other"] (fun () -> + with_timer config.detail_times "" ["other"] (fun () -> if config.optimize && config.purity_inference then - with_timer config.detail_times ["optimize";"purity-inference"] (fun () -> Purity.infer com); - List.iter (run_on_type ctx config) types + with_timer config.detail_times "" ["optimize";"purity-inference"] (fun () -> Purity.infer com); + List.iter (run_on_type com config) types ) end ;; -Typecore.analyzer_run_on_expr_ref := (fun com e -> +Typecore.analyzer_run_on_expr_ref := (fun com identifier e -> let config = AnalyzerConfig.get_base_config com in (* We always want to optimize because const propagation might be required to obtain a constant expression for inline field initializations (see issue #4977). *) let config = {config with AnalyzerConfig.optimize = true} in - let actx = Run.create_analyzer_context com config e in + let actx = Run.create_analyzer_context com config identifier e in Run.run_on_expr actx e ) \ No newline at end of file diff --git a/src/optimization/analyzerConfig.ml b/src/optimization/analyzerConfig.ml index c0e0a01f..1d59a6c9 100644 --- a/src/optimization/analyzerConfig.ml +++ b/src/optimization/analyzerConfig.ml @@ -35,7 +35,7 @@ type t = { fusion : bool; purity_inference : bool; debug_kind : debug_kind; - detail_times : bool; + detail_times : int; user_var_fusion : bool; fusion_debug : bool; } @@ -65,18 +65,18 @@ let is_ignored meta = let get_base_config com = { optimize = Common.defined com Define.AnalyzerOptimize; - const_propagation = not (Common.raw_defined com "analyzer-no-const-propagation"); - copy_propagation = not (Common.raw_defined com "analyzer-no-copy-propagation"); - local_dce = not (Common.raw_defined com "analyzer-no-local-dce"); - fusion = not (Common.raw_defined com "analyzer-no-fusion"); - purity_inference = not (Common.raw_defined com "analyzer-no-purity-inference"); + const_propagation = not (Common.raw_defined com "analyzer_no_const_propagation"); + copy_propagation = not (Common.raw_defined com "analyzer_no_copy_propagation"); + local_dce = not (Common.raw_defined com "analyzer_no_local_dce"); + fusion = not (Common.raw_defined com "analyzer_no_fusion"); + purity_inference = not (Common.raw_defined com "analyzer_no_purity_inference"); debug_kind = DebugNone; - detail_times = Common.raw_defined com "analyzer-times"; - user_var_fusion = (match com.platform with Flash | Java -> false | _ -> true) && (Common.raw_defined com "analyzer-user-var-fusion" || (not com.debug && not (Common.raw_defined com "analyzer-no-user-var-fusion"))); + detail_times = (try int_of_string (Common.defined_value_safe com ~default:"0" Define.AnalyzerTimes) with _ -> 0); + user_var_fusion = (match com.platform with Flash | Java -> false | _ -> true) && (Common.raw_defined com "analyzer_user_var_fusion" || (not com.debug && not (Common.raw_defined com "analyzer_no_user_var_fusion"))); fusion_debug = false; } -let update_config_from_meta com config meta = +let update_config_from_meta com config ml = List.fold_left (fun config meta -> match meta with | (Meta.Analyzer,el,_) -> List.fold_left (fun config e -> match fst e with @@ -99,19 +99,21 @@ let update_config_from_meta com config meta = | "fusion_debug" -> { config with fusion_debug = true } | "as_var" -> config | _ -> - com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e); + let options = Warning.from_meta ml in + com.warning WOptimizer options (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e); config end | _ -> let s = Ast.Printer.s_expr e in - com.warning (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e); + let options = Warning.from_meta ml in + com.warning WOptimizer options (StringError.string_error s all_flags ("Unrecognized analyzer option: " ^ s)) (pos e); config ) config el | (Meta.HasUntyped,_,_) -> {config with optimize = false} | _ -> config - ) config meta + ) config ml let get_class_config com c = let config = get_base_config com in diff --git a/src/optimization/analyzerTexpr.ml b/src/optimization/analyzerTexpr.ml index d74eb6b2..abdb85f1 100644 --- a/src/optimization/analyzerTexpr.ml +++ b/src/optimization/analyzerTexpr.ml @@ -25,11 +25,6 @@ open Globals let s_expr_pretty e = s_expr_pretty false "" false (s_type (print_context())) e -let rec is_true_expr e1 = match e1.eexpr with - | TConst(TBool true) -> true - | TParenthesis e1 -> is_true_expr e1 - | _ -> false - let is_stack_allocated c = Meta.has Meta.StructAccess c.cl_meta let map_values ?(allow_control_flow=true) f e = @@ -167,7 +162,7 @@ let type_change_ok com t1 t2 = | TLazy f -> is_nullable_or_whatever (lazy_type f) | TType (t,tl) -> - is_nullable_or_whatever (apply_params t.t_params tl t.t_type) + is_nullable_or_whatever (apply_typedef t tl) | TFun _ -> false | TInst ({ cl_kind = KTypeParameter _ },_) -> @@ -504,6 +499,7 @@ module Fusion = struct | OpAssignOp _ | OpInterval | OpIn + | OpNullCoal | OpArrow -> false @@ -518,9 +514,11 @@ module Fusion = struct | Cs when is_null e1.etype || is_null e2.etype -> false (* C# hates OpAssignOp on Null *) | _ -> true - let apply com config e = + let apply actx e = + let config = actx.AnalyzerTypes.config in + let com = actx.com in let state = new fusion_state in - state#infer_from_texpr e; + actx.with_timer ["<-";"fusion";"infer_from_texpr"] (fun () -> state#infer_from_texpr e); (* Handles block-level expressions, e.g. by removing side-effect-free ones and recursing into compound constructs like array or object declarations. The resulting element list is reversed. INFO: `el` is a reversed list of expressions in a block. @@ -584,28 +582,237 @@ module Fusion = struct | [] -> acc in + let block_element ?(loop_bottom=false) acc el = + actx.with_timer ["<-";"fusion";"block_element"] (fun () -> block_element ~loop_bottom acc el) + in let can_be_fused v e = let num_uses = state#get_reads v in let num_writes = state#get_writes v in let can_be_used_as_value = can_be_used_as_value com e in let is_compiler_generated = match v.v_kind with VUser _ | VInlined -> false | _ -> true in let has_type_params = match v.v_extra with Some ve when ve.v_params <> [] -> true | _ -> false in + let rec is_impure_extern e = match e.eexpr with + | TField(ef,(FStatic(cl,cf) | FInstance(cl,_,cf))) when has_class_flag cl CExtern -> + not ( + Meta.has Meta.CoreApi cl.cl_meta || + PurityState.is_pure cl cf + ) + | _ -> check_expr is_impure_extern e + in + let is_impure_extern = (is_impure_extern e) in + let has_variable_semantics = ExtType.has_variable_semantics v.v_type in + let is_variable_expression = (match e.eexpr with TLocal { v_kind = VUser _ } -> false | _ -> true) in let b = num_uses <= 1 && num_writes = 0 && can_be_used_as_value && not ( - ExtType.has_variable_semantics v.v_type && - (match e.eexpr with TLocal { v_kind = VUser _ } -> false | _ -> true) - ) && - (is_compiler_generated || config.optimize && config.fusion && config.user_var_fusion && not has_type_params) + has_variable_semantics && + is_variable_expression + ) && ( + is_compiler_generated || + config.optimize && config.fusion && config.user_var_fusion && not has_type_params && not is_impure_extern + ) in if config.fusion_debug then begin print_endline (Printf.sprintf "\nFUSION: %s\n\tvar %s<%i> = %s" (if b then "true" else "false") v.v_name v.v_id (s_expr_pretty e)); - print_endline (Printf.sprintf "CONDITION:\n\tnum_uses:%i <= 1 && num_writes:%i = 0 && can_be_used_as_value:%b && (is_compiler_generated:%b || config.optimize:%b && config.fusion:%b && config.user_var_fusion:%b)" - num_uses num_writes can_be_used_as_value is_compiler_generated config.optimize config.fusion config.user_var_fusion) + print_endline (Printf.sprintf "CONDITION:\n\tnum_uses:%i <= 1 && num_writes:%i = 0 && can_be_used_as_value:%b && not (has_variable_semantics:%b && e.eexpr=TLocal:%b) (is_compiler_generated:%b || config.optimize:%b && config.fusion:%b && config.user_var_fusion:%b && not has_type_params:%b && not is_impuare_extern:%b)" + num_uses num_writes can_be_used_as_value has_variable_semantics is_variable_expression is_compiler_generated config.optimize config.fusion config.user_var_fusion has_type_params is_impure_extern) end; b in + let handle_assigned_local v1 e1 el = + let found = ref false in + let blocked = ref false in + let ir = InterferenceReport.from_texpr e1 in + if config.fusion_debug then print_endline (Printf.sprintf "INTERFERENCE: %s\nINTO: %s" + (InterferenceReport.to_string ir) (Type.s_expr_pretty true "" false (s_type (print_context())) (mk (TBlock el) t_dynamic null_pos))); + (* This function walks the AST in order of evaluation and tries to find an occurrence of v1. If successful, that occurrence is + replaced with e1. If there's an interference "on the way" the replacement is canceled. *) + let rec replace e = + let explore e = + let old = !blocked in + blocked := true; + let e = replace e in + blocked := old; + e + in + let handle_el' el = + (* This mess deals with the fact that the order of evaluation is undefined for call + arguments on these targets. Even if we find a replacement, we pretend that we + didn't in order to find possible interferences in later call arguments. *) + let temp_found = false in + let really_found = ref !found in + let el = List.map (fun e -> + found := temp_found; + let e = replace e in + if !found then really_found := true; + e + ) el in + found := !really_found; + el + in + let handle_el = if not (target_handles_side_effect_order com) then handle_el' else List.map replace in + let handle_call e2 el = match com.platform with + | Neko -> + (* Neko has this reversed at the moment (issue #4787) *) + let el = List.map replace el in + let e2 = replace e2 in + e2,el + | Cpp -> + let e2 = replace e2 in + let el = handle_el el in + e2,el + | _ -> + let e2 = replace e2 in + let el = List.map replace el in + e2,el + in + if !found then e else match e.eexpr with + | TWhile _ | TTry _ -> + raise Exit + | TFunction _ -> + e + | TIf(e1,e2,eo) -> + let e1 = replace e1 in + if not !found && (has_state_write ir || has_any_field_write ir || has_any_var_write ir) then raise Exit; + let e2 = replace e2 in + let eo = Option.map replace eo in + {e with eexpr = TIf(e1,e2,eo)} + | TSwitch(e1,cases,edef) -> + let e1 = match com.platform with + | Lua | Python -> explore e1 + | _ -> replace e1 + in + if not !found then raise Exit; + {e with eexpr = TSwitch(e1,cases,edef)} + (* locals *) + | TLocal v2 when v1 == v2 && not !blocked -> + found := true; + if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos + | TLocal v -> + if has_var_write ir v || ((has_var_flag v VCaptured || ExtType.has_reference_semantics v.v_type) && (has_state_write ir)) then raise Exit; + e + | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) -> + let e2 = replace e2 in + if not !found && has_var_read ir v then raise Exit; + {e with eexpr = TBinop(OpAssign,e1,e2)} + (* Never fuse into write-positions (issue #7298) *) + | TBinop(OpAssignOp _,{eexpr = TLocal v2},_) | TUnop((Increment | Decrement),_,{eexpr = TLocal v2}) when v1 == v2 -> + raise Exit + | TBinop(OpAssignOp _ as op,({eexpr = TLocal v} as e1),e2) -> + let e2 = replace e2 in + if not !found && (has_var_read ir v || has_var_write ir v) then raise Exit; + {e with eexpr = TBinop(op,e1,e2)} + | TUnop((Increment | Decrement),_,{eexpr = TLocal v}) when has_var_read ir v || has_var_write ir v -> + raise Exit + (* fields *) + | TField(e1,fa) -> + let e1 = replace e1 in + if not !found && not (is_read_only_field_access e1 fa) && (has_field_write ir (field_name fa) || has_state_write ir) then raise Exit; + {e with eexpr = TField(e1,fa)} + | TBinop(OpAssign,({eexpr = TField(e1,fa)} as ef),e2) -> + let e1 = replace e1 in + let e2 = replace e2 in + if not !found && (has_field_read ir (field_name fa) || has_state_read ir) then raise Exit; + {e with eexpr = TBinop(OpAssign,{ef with eexpr = TField(e1,fa)},e2)} + | TBinop(OpAssignOp _ as op,({eexpr = TField(e1,fa)} as ef),e2) -> + let e1 = replace e1 in + let s = field_name fa in + if not !found && (has_field_write ir s || has_state_write ir) then raise Exit; + let e2 = replace e2 in + if not !found && (has_field_read ir s || has_state_read ir) then raise Exit; + {e with eexpr = TBinop(op,{ef with eexpr = TField(e1,fa)},e2)} + | TUnop((Increment | Decrement),_,{eexpr = TField(e1,fa)}) when has_field_read ir (field_name fa) || has_state_read ir + || has_field_write ir (field_name fa) || has_state_write ir -> + raise Exit + (* array *) + | TArray(e1,e2) -> + let e1 = replace e1 in + let e2 = replace e2 in + if not !found && has_state_write ir then raise Exit; + {e with eexpr = TArray(e1,e2)} + | TBinop(OpAssign,({eexpr = TArray(e1,e2)} as ef),e3) -> + let e1 = replace e1 in + let e2 = replace e2 in + let e3 = replace e3 in + if not !found && (has_state_read ir) then raise Exit; + {e with eexpr = TBinop(OpAssign,{ef with eexpr = TArray(e1,e2)},e3)} + | TBinop(OpAssignOp _ as op,({eexpr = TArray(e1,e2)} as ef),e3) -> + let e1 = replace e1 in + let e2 = replace e2 in + if not !found && has_state_write ir then raise Exit; + let e3 = replace e3 in + if not !found && has_state_read ir then raise Exit; + {e with eexpr = TBinop(op,{ef with eexpr = TArray(e1,e2)},e3)} + | TUnop((Increment | Decrement),_,{eexpr = TArray _}) when has_state_read ir || has_state_write ir -> + raise Exit + (* state *) + | TCall({eexpr = TIdent s},el) when not (is_unbound_call_that_might_have_side_effects s el) -> + e + | TNew(c,tl,el) when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) -> + let el = handle_el el in + if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; + {e with eexpr = TNew(c,tl,el)} + | TNew(c,tl,el) -> + let el = handle_el el in + if not !found && (has_state_write ir || has_state_read ir || has_any_field_read ir || has_any_field_write ir) then raise Exit; + {e with eexpr = TNew(c,tl,el)} + | TCall({eexpr = TField(_,FEnum _)} as ef,el) -> + let el = handle_el el in + {e with eexpr = TCall(ef,el)} + | TCall({eexpr = TField(_,fa)} as ef,el) when PurityState.is_pure_field_access fa -> + let ef,el = handle_call ef el in + if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; + {e with eexpr = TCall(ef,el)} + | TCall(e1,el) -> + let e1,el = match e1.eexpr with + | TIdent s when s <> "`trace" && s <> "__int__" -> e1,el + | _ -> handle_call e1 el + in + if not !found && (((has_state_read ir || has_any_field_read ir)) || has_state_write ir || has_any_field_write ir) then raise Exit; + {e with eexpr = TCall(e1,el)} + | TObjectDecl fl -> + (* The C# generator has trouble with evaluation order in structures (#7531). *) + let el = (match com.platform with Cs -> handle_el' | _ -> handle_el) (List.map snd fl) in + if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; + {e with eexpr = TObjectDecl (List.map2 (fun (s,_) e -> s,e) fl el)} + | TArrayDecl el -> + let el = handle_el el in + (*if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;*) + {e with eexpr = TArrayDecl el} + | TBinop(op,e1,e2) when (match com.platform with Cpp -> true | _ -> false) -> + let e1 = replace e1 in + let temp_found = !found in + found := false; + let e2 = replace e2 in + found := !found || temp_found; + {e with eexpr = TBinop(op,e1,e2)} + | _ -> + Type.map_expr replace e + in + let replace e = + actx.with_timer ["<-";"fusion";"fuse";"replace"] (fun () -> replace e) + in + begin try + let rec loop acc el = match el with + | e :: el -> + let e = replace e in + if !found then (List.rev (e :: acc)) @ el + else loop (e :: acc) el + | [] -> + List.rev acc + in + let el = loop [] el in + if not !found then raise Exit; + state#changed; + state#dec_reads v1; + if config.fusion_debug then print_endline (Printf.sprintf "YES: %s" (s_expr_pretty (mk (TBlock el) t_dynamic null_pos))); + Some el + with Exit -> + if config.fusion_debug then print_endline (Printf.sprintf "NO: %s" (Printexc.get_backtrace())); + None + end + in let rec fuse acc el = match el with | ({eexpr = TVar(v1,None)} as e1) :: {eexpr = TBinop(OpAssign,{eexpr = TLocal v2},e2)} :: el when v1 == v2 -> state#changed; @@ -678,215 +885,34 @@ module Fusion = struct with Exit -> fuse (ev :: acc) el end + | ({eexpr = TVar(v1,Some e1)} as ev) :: el when can_be_fused v1 e1 -> - let found = ref false in - let blocked = ref false in - let ir = InterferenceReport.from_texpr e1 in - if config.fusion_debug then print_endline (Printf.sprintf "INTERFERENCE: %s\nINTO: %s" - (InterferenceReport.to_string ir) (Type.s_expr_pretty true "" false (s_type (print_context())) (mk (TBlock el) t_dynamic null_pos))); - (* This function walks the AST in order of evaluation and tries to find an occurrence of v1. If successful, that occurrence is - replaced with e1. If there's an interference "on the way" the replacement is canceled. *) - let rec replace e = - let explore e = - let old = !blocked in - blocked := true; - let e = replace e in - blocked := old; - e - in - let handle_el' el = - (* This mess deals with the fact that the order of evaluation is undefined for call - arguments on these targets. Even if we find a replacement, we pretend that we - didn't in order to find possible interferences in later call arguments. *) - let temp_found = false in - let really_found = ref !found in - let el = List.map (fun e -> - found := temp_found; - let e = replace e in - if !found then really_found := true; - e - ) el in - found := !really_found; - el - in - let handle_el = if not (target_handles_side_effect_order com) then handle_el' else List.map replace in - let handle_call e2 el = match com.platform with - | Neko -> - (* Neko has this reversed at the moment (issue #4787) *) - let el = List.map replace el in - let e2 = replace e2 in - e2,el - | Cpp -> - let e2 = replace e2 in - let el = handle_el el in - e2,el - | _ -> - let e2 = replace e2 in - let el = List.map replace el in - e2,el - in - if !found then e else match e.eexpr with - | TWhile _ | TTry _ -> - raise Exit - | TFunction _ -> - e - | TIf(e1,e2,eo) -> - let e1 = replace e1 in - if not !found && (has_state_write ir || has_any_field_write ir || has_any_var_write ir) then raise Exit; - let e2 = replace e2 in - let eo = Option.map replace eo in - {e with eexpr = TIf(e1,e2,eo)} - | TSwitch(e1,cases,edef) -> - let e1 = match com.platform with - | Lua | Python -> explore e1 - | _ -> replace e1 - in - if not !found then raise Exit; - {e with eexpr = TSwitch(e1,cases,edef)} - (* locals *) - | TLocal v2 when v1 == v2 && not !blocked -> + begin match el with + | ({eexpr = TUnop((Increment | Decrement) as op,Prefix,{eexpr = TLocal v1})} as e2) :: el -> + let found = ref false in + let rec replace e = match e.eexpr with + | TLocal v2 when v1 == v2 -> + if !found then raise Exit; found := true; - if type_change_ok com v1.v_type e1.etype then e1 else mk (TCast(e1,None)) v1.v_type e.epos - | TLocal v -> - if has_var_write ir v || ((has_var_flag v VCaptured || ExtType.has_reference_semantics v.v_type) && (has_state_write ir)) then raise Exit; - e - | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) -> - let e2 = replace e2 in - if not !found && has_var_read ir v then raise Exit; - {e with eexpr = TBinop(OpAssign,e1,e2)} - (* Never fuse into write-positions (issue #7298) *) - | TBinop(OpAssignOp _,{eexpr = TLocal v2},_) | TUnop((Increment | Decrement),_,{eexpr = TLocal v2}) when v1 == v2 -> + {e with eexpr = TUnop(op,Postfix,e)} + | TIf _ | TSwitch _ | TTry _ | TWhile _ | TFor _ -> raise Exit - | TBinop(OpAssignOp _ as op,({eexpr = TLocal v} as e1),e2) -> - let e2 = replace e2 in - if not !found && (has_var_read ir v || has_var_write ir v) then raise Exit; - {e with eexpr = TBinop(op,e1,e2)} - | TUnop((Increment | Decrement),_,{eexpr = TLocal v}) when has_var_read ir v || has_var_write ir v -> - raise Exit - (* fields *) - | TField(e1,fa) -> - let e1 = replace e1 in - if not !found && not (is_read_only_field_access e1 fa) && (has_field_write ir (field_name fa) || has_state_write ir) then raise Exit; - {e with eexpr = TField(e1,fa)} - | TBinop(OpAssign,({eexpr = TField(e1,fa)} as ef),e2) -> - let e1 = replace e1 in - let e2 = replace e2 in - if not !found && (has_field_read ir (field_name fa) || has_state_read ir) then raise Exit; - {e with eexpr = TBinop(OpAssign,{ef with eexpr = TField(e1,fa)},e2)} - | TBinop(OpAssignOp _ as op,({eexpr = TField(e1,fa)} as ef),e2) -> - let e1 = replace e1 in - let s = field_name fa in - if not !found && (has_field_write ir s || has_state_write ir) then raise Exit; - let e2 = replace e2 in - if not !found && (has_field_read ir s || has_state_read ir) then raise Exit; - {e with eexpr = TBinop(op,{ef with eexpr = TField(e1,fa)},e2)} - | TUnop((Increment | Decrement),_,{eexpr = TField(e1,fa)}) when has_field_read ir (field_name fa) || has_state_read ir - || has_field_write ir (field_name fa) || has_state_write ir -> - raise Exit - (* array *) - | TArray(e1,e2) -> - let e1 = replace e1 in - let e2 = replace e2 in - if not !found && has_state_write ir then raise Exit; - {e with eexpr = TArray(e1,e2)} - | TBinop(OpAssign,({eexpr = TArray(e1,e2)} as ef),e3) -> - let e1 = replace e1 in - let e2 = replace e2 in - let e3 = replace e3 in - if not !found && (has_state_read ir) then raise Exit; - {e with eexpr = TBinop(OpAssign,{ef with eexpr = TArray(e1,e2)},e3)} - | TBinop(OpAssignOp _ as op,({eexpr = TArray(e1,e2)} as ef),e3) -> - let e1 = replace e1 in - let e2 = replace e2 in - if not !found && has_state_write ir then raise Exit; - let e3 = replace e3 in - if not !found && has_state_read ir then raise Exit; - {e with eexpr = TBinop(op,{ef with eexpr = TArray(e1,e2)},e3)} - | TUnop((Increment | Decrement),_,{eexpr = TArray _}) when has_state_read ir || has_state_write ir -> - raise Exit - (* state *) - | TCall({eexpr = TIdent s},el) when not (is_unbound_call_that_might_have_side_effects s el) -> - e - | TNew(c,tl,el) when (match c.cl_constructor with Some cf when PurityState.is_pure c cf -> true | _ -> false) -> - let el = handle_el el in - if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; - {e with eexpr = TNew(c,tl,el)} - | TNew(c,tl,el) -> - let el = handle_el el in - if not !found && (has_state_write ir || has_state_read ir || has_any_field_read ir || has_any_field_write ir) then raise Exit; - {e with eexpr = TNew(c,tl,el)} - | TCall({eexpr = TField(_,FEnum _)} as ef,el) -> - let el = handle_el el in - {e with eexpr = TCall(ef,el)} - | TCall({eexpr = TField(_,fa)} as ef,el) when PurityState.is_pure_field_access fa -> - let ef,el = handle_call ef el in - if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; - {e with eexpr = TCall(ef,el)} - | TCall(e1,el) -> - let e1,el = match e1.eexpr with - | TIdent s when s <> "`trace" && s <> "__int__" -> e1,el - | _ -> handle_call e1 el - in - if not !found && (((has_state_read ir || has_any_field_read ir)) || has_state_write ir || has_any_field_write ir) then raise Exit; - {e with eexpr = TCall(e1,el)} - | TObjectDecl fl -> - (* The C# generator has trouble with evaluation order in structures (#7531). *) - let el = (match com.platform with Cs -> handle_el' | _ -> handle_el) (List.map snd fl) in - if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit; - {e with eexpr = TObjectDecl (List.map2 (fun (s,_) e -> s,e) fl el)} - | TArrayDecl el -> - let el = handle_el el in - (*if not !found && (has_state_write ir || has_any_field_write ir) then raise Exit;*) - {e with eexpr = TArrayDecl el} - | TBinop(op,e1,e2) when (match com.platform with Cpp -> true | _ -> false) -> - let e1 = replace e1 in - let temp_found = !found in - found := false; - let e2 = replace e2 in - found := !found || temp_found; - {e with eexpr = TBinop(op,e1,e2)} | _ -> Type.map_expr replace e - in - begin try - let rec loop acc el = match el with - | e :: el -> - let e = replace e in - if !found then (List.rev (e :: acc)) @ el - else loop (e :: acc) el - | [] -> - List.rev acc in - let el = loop [] el in - if not !found then raise Exit; - state#changed; - state#dec_reads v1; - if config.fusion_debug then print_endline (Printf.sprintf "YES: %s" (s_expr_pretty (mk (TBlock el) t_dynamic null_pos))); - fuse acc el - with Exit -> - if config.fusion_debug then print_endline (Printf.sprintf "NO: %s" (Printexc.get_backtrace())); - begin match el with - | ({eexpr = TUnop((Increment | Decrement) as op,Prefix,{eexpr = TLocal v1})} as e2) :: el -> - let found = ref false in - let rec replace e = match e.eexpr with - | TLocal v2 when v1 == v2 -> - if !found then raise Exit; - found := true; - {e with eexpr = TUnop(op,Postfix,e)} - | TIf _ | TSwitch _ | TTry _ | TWhile _ | TFor _ -> - raise Exit - | _ -> - Type.map_expr replace e - in - begin try - let ev = replace ev in - if not !found then raise Exit; - state#changed; - fuse acc (ev :: el) - with Exit -> - fuse (ev :: acc) (e2 :: el) - end - | _ -> + begin try + let ev = replace ev in + if not !found then raise Exit; + state#changed; + fuse acc (ev :: el) + with Exit -> + fuse (ev :: acc) (e2 :: el) + end + | _ -> + begin match handle_assigned_local v1 e1 el with + | Some el -> + fuse acc el + | None -> fuse (ev :: acc) el end end @@ -939,6 +965,9 @@ module Fusion = struct | [] -> acc in + let fuse acc el = + actx.with_timer ["<-";"fusion";"fuse"] (fun () -> fuse [] el) + in let rec loop e = match e.eexpr with | TWhile(condition,{ eexpr = TBlock el; etype = t; epos = p },flag) -> let condition = loop condition @@ -1003,22 +1032,56 @@ module Cleanup = struct | TWhile(e1,e2,NormalWhile) -> let e1 = loop e1 in let e2 = loop e2 in + let rec has_continue e = match e.eexpr with + | TContinue -> + true + | _ -> + check_expr has_continue e + in + let has_continue = has_continue e2 in begin match e2.eexpr with | TBlock ({eexpr = TIf(e1,({eexpr = TBlock[{eexpr = TBreak}]} as eb),None)} :: el2) -> let e1 = Texpr.skip e1 in let e1 = match e1.eexpr with TUnop(_,_,e1) -> e1 | _ -> {e1 with eexpr = TUnop(Not,Prefix,e1)} in {e with eexpr = TWhile(e1,{eb with eexpr = TBlock el2},NormalWhile)} | TBlock el -> + let do_while = ref None in + let locals = ref IntMap.empty in + let rec collect_vars e = match e.eexpr with + | TVar(v,e1) -> + locals := IntMap.add v.v_id true !locals; + Option.may collect_vars e1 + | _ -> + Type.iter collect_vars e + in + let rec references_local e = match e.eexpr with + | TLocal v when IntMap.mem v.v_id !locals -> true + | _ -> check_expr references_local e + in + let can_do = match com.platform with Hl -> false | _ -> true in let rec loop2 el = match el with + | [{eexpr = TBreak}] when is_true_expr e1 && can_do && not has_continue -> + do_while := Some (Texpr.Builder.make_bool com.basic true e1.epos); + [] + | [{eexpr = TIf(econd,{eexpr = TBlock[{eexpr = TBreak}]},None)}] when is_true_expr e1 && not (references_local econd) && can_do && not has_continue -> + do_while := Some econd; + [] | {eexpr = TBreak | TContinue | TReturn _ | TThrow _} as e :: el -> [e] | e :: el -> + collect_vars e; e :: (loop2 el) | [] -> [] in let el = loop2 el in - {e with eexpr = TWhile(e1,{e2 with eexpr = TBlock el},NormalWhile)} + begin match !do_while with + | None -> + {e with eexpr = TWhile(e1,{e2 with eexpr = TBlock el},NormalWhile)} + | Some econd -> + let econd = {econd with eexpr = TUnop(Not,Prefix,econd)} in + {e with eexpr = TWhile(econd,{e2 with eexpr = TBlock el},DoWhile)} + end; | _ -> {e with eexpr = TWhile(e1,e2,NormalWhile)} end @@ -1179,7 +1242,7 @@ module Purity = struct apply_to_class com c with Purity_conflict(impure,p) -> com.error "Impure field overrides/implements field which was explicitly marked as @:pure" impure.pn_field.cf_pos; - Error.error (Error.compl_msg "Pure field is here") p; + Error.typing_error ~depth:1 (Error.compl_msg "Pure field is here") p; end | _ -> () ) com.types; diff --git a/src/optimization/analyzerTexprTransformer.ml b/src/optimization/analyzerTexprTransformer.ml index 2aafe5b9..8a519d0d 100644 --- a/src/optimization/analyzerTexprTransformer.ml +++ b/src/optimization/analyzerTexprTransformer.ml @@ -85,12 +85,16 @@ let rec func ctx bb tf t p = if is_unbound_call_that_might_have_side_effects s el then ctx.has_unbound <- true; in let no_void t p = - if ExtType.is_void (follow t) then Error.error "Cannot use Void as value" p + if ExtType.is_void (follow t) then Error.typing_error "Cannot use Void as value" p in let push_name s = ctx.name_stack <- s :: ctx.name_stack; (fun () -> ctx.name_stack <- List.tl ctx.name_stack) in + let check_ref v e = if ExtType.has_reference_semantics v.v_type then match (Texpr.skip e).eexpr with + | TLocal v' -> add_var_flag v' VCaptured + | _ -> () + in let rec value' bb e = match e.eexpr with | TLocal _ | TIdent _ -> bb,e @@ -99,7 +103,7 @@ let rec func ctx bb tf t p = | TBlock [e1] -> value bb e1 | TBlock _ | TIf _ | TSwitch _ | TTry _ -> - bind_to_temp bb false e + bind_to_temp bb e | TCall({eexpr = TIdent s},el) when is_really_unbound s -> check_unbound_call s el; bb,e @@ -175,7 +179,7 @@ let rec func ctx bb tf t p = let bb = block_element bb e in bb,mk (TConst TNull) t_dynamic e.epos | TVar _ | TFor _ | TWhile _ -> - Error.error "Cannot use this expression as value" e.epos + Error.typing_error "Cannot use this expression as value" e.epos and value bb e = let bb,e = value' bb e in no_void e.etype e.epos; @@ -201,12 +205,12 @@ let rec func ctx bb tf t p = if bb == g.g_unreachable then bb,acc else begin - let bb,value = if aff || opt then bind_to_temp bb aff e else value bb e in + let bb,value = if aff || opt then bind_to_temp bb e else value bb e in bb,(value :: acc) end ) (bb,[]) el in bb,List.rev values - and bind_to_temp ?(v=None) bb sequential e = + and bind_to_temp ?(v=None) bb e = let is_probably_not_affected e e1 fa = match fa with | FAnon cf | FInstance (_,_,cf) | FStatic (_,cf) | FClosure (_,cf) when cf.cf_kind = Method MethNormal -> true | FStatic(_,{cf_kind = Method MethDynamic}) -> false @@ -309,7 +313,7 @@ let rec func ctx bb tf t p = | _ -> if ExtType.has_variable_semantics t then begin let v = alloc_var VGenerated "tmp" t e.epos in - let bb',e = bind_to_temp ~v:(Some v) !bb false e in + let bb',e = bind_to_temp ~v:(Some v) !bb e in bb := bb'; e end else @@ -321,22 +325,22 @@ let rec func ctx bb tf t p = | e1 :: el -> bb,{e with eexpr = TCall(e1,el)} | _ -> die "" __LOC__ and array_assign_op bb op e ea e1 e2 e3 = - let bb,e1 = bind_to_temp bb false e1 in - let bb,e2 = bind_to_temp bb false e2 in + let bb,e1 = bind_to_temp bb e1 in + let bb,e2 = bind_to_temp bb e2 in let ea = {ea with eexpr = TArray(e1,e2)} in - let bb,e4 = bind_to_temp bb false ea in - let bb,e3 = bind_to_temp bb false e3 in + let bb,e4 = bind_to_temp bb ea in + let bb,e3 = bind_to_temp bb e3 in let eop = {e with eexpr = TBinop(op,e4,e3)} in add_texpr bb {e with eexpr = TBinop(OpAssign,ea,eop)}; bb,ea and field_assign_op bb op e ef e1 fa e2 = let bb,e1 = match fa with | FInstance(c,_,_) | FClosure(Some(c,_),_) when is_stack_allocated c -> bb,e1 - | _ -> bind_to_temp bb false e1 + | _ -> bind_to_temp bb e1 in let ef = {ef with eexpr = TField(e1,fa)} in - let bb,e3 = bind_to_temp bb false ef in - let bb,e2 = bind_to_temp bb false e2 in + let bb,e3 = bind_to_temp bb ef in + let bb,e2 = bind_to_temp bb e2 in let eop = {e with eexpr = TBinop(op,e3,e2)} in add_texpr bb {e with eexpr = TBinop(OpAssign,ef,eop)}; bb,ef @@ -346,8 +350,10 @@ let rec func ctx bb tf t p = add_texpr bb e; bb | TVar(v,Some e1) -> + check_ref v e1; declare_var_and_assign bb v e1 e.epos | TBinop(OpAssign,({eexpr = TLocal v} as e1),e2) -> + check_ref v e2; let assign e = mk (TBinop(OpAssign,e1,e)) e.etype e.epos in @@ -364,6 +370,8 @@ let rec func ctx bb tf t p = (* branching *) | TMeta((Meta.MergeBlock,_,_),{eexpr = TBlock el}) -> block_el bb el + | TBlock [] when (ExtType.is_void (follow e.etype)) -> + bb | TBlock el -> let bb_sub = create_node BKSub e.etype e.epos in add_cfg_edge bb bb_sub CFGGoto; @@ -381,7 +389,7 @@ let rec func ctx bb tf t p = bb_sub_next end | TIf(e1,e2,None) -> - let bb,e1 = bind_to_temp bb false e1 in + let bb,e1 = bind_to_temp bb e1 in if bb == g.g_unreachable then bb else begin @@ -398,7 +406,7 @@ let rec func ctx bb tf t p = bb_next end | TIf(e1,e2,Some e3) -> - let bb,e1 = bind_to_temp bb false e1 in + let bb,e1 = bind_to_temp bb e1 in if bb == g.g_unreachable then bb else begin @@ -424,8 +432,8 @@ let rec func ctx bb tf t p = end end | TSwitch(e1,cases,edef) -> - let is_exhaustive = edef <> None || is_exhaustive e1 in - let bb,e1 = bind_to_temp bb false e1 in + let is_exhaustive = is_exhaustive e1 edef in + let bb,e1 = bind_to_temp bb e1 in bb.bb_terminator <- TermCondBranch e1; let reachable = ref [] in let make_case e = @@ -462,13 +470,11 @@ let rec func ctx bb tf t p = bb_next end | TWhile(e1,e2,NormalWhile) -> - let bb_loop_pre = create_node BKNormal e1.etype e1.epos in + let bb_loop_pre = create_node BKLoopHead e1.etype e1.epos in add_cfg_edge bb bb_loop_pre CFGGoto; set_syntax_edge bb (SEMerge bb_loop_pre); close_node bb; - let bb_loop_head = create_node BKLoopHead e1.etype e1.epos in - add_cfg_edge bb_loop_pre bb_loop_head CFGGoto; - let close = begin_loop bb bb_loop_head in + let close = begin_loop bb bb_loop_pre in let bb_loop_body = create_node BKNormal e2.etype e2.epos in let bb_loop_body_next = block bb_loop_body e2 in let bb_breaks = close() in @@ -481,13 +487,12 @@ let rec func ctx bb tf t p = create_node BKNormal bb.bb_type bb.bb_pos in List.iter (fun bb -> add_cfg_edge bb bb_next CFGGoto) bb_breaks; - set_syntax_edge bb_loop_pre (SEWhile(bb_loop_head,bb_loop_body,bb_next,e.epos)); - close_node bb_loop_pre; + set_syntax_edge bb_loop_pre (SEWhile(bb_loop_body,bb_next,e.epos)); bb_loop_pre.bb_terminator <- TermCondBranch e1; - if bb_loop_body_next != g.g_unreachable then add_cfg_edge bb_loop_body_next bb_loop_head CFGGoto; - add_cfg_edge bb_loop_head bb_loop_body CFGGoto; + if bb_loop_body_next != g.g_unreachable then add_cfg_edge bb_loop_body_next bb_loop_pre CFGGoto; + add_cfg_edge bb_loop_pre bb_loop_body CFGGoto; close_node bb_loop_body_next; - close_node bb_loop_head; + close_node bb_loop_pre; bb_next; | TTry(e1,catches) -> let bb_try = create_node BKNormal e1.etype e1.epos in @@ -679,7 +684,13 @@ let rec block_to_texpr_el ctx bb = [] else begin let block bb = block_to_texpr ctx bb in + let live bb = not ctx.did_optimize || not ctx.config.local_dce || has_block_flag bb BlockDce in + let if_live bb = if live bb then Some bb else None in let rec loop bb se = + let get_terminator() = match bb.bb_terminator with + | TermCondBranch e1 -> e1 + | _ -> die "" __LOC__ + in match se with | SESubBlock(bb_sub,bb_next) -> Some bb_next,Some (block bb_sub) @@ -688,22 +699,16 @@ let rec block_to_texpr_el ctx bb = | SENone -> None,terminator_to_texpr_maybe bb.bb_terminator | SETry(bb_try,_,bbl,bb_next,p) -> - Some bb_next,Some (mk (TTry(block bb_try,List.map (fun (v,bb) -> v,block bb) bbl)) ctx.com.basic.tvoid p) - | se -> - let e1 = match bb.bb_terminator with - | TermCondBranch e1 -> e1 - | _ -> die "" __LOC__ - in - let bb_next,e1_def,t,p = match se with - | SEIfThen(bb_then,bb_next,p) -> Some bb_next,TIf(e1,block bb_then,None),ctx.com.basic.tvoid,p - | SEIfThenElse(bb_then,bb_else,bb_next,t,p) -> Some bb_next,TIf(e1,block bb_then,Some (block bb_else)),t,p - | SESwitch(bbl,bo,bb_next,p) -> Some bb_next,TSwitch(e1,List.map (fun (el,bb) -> el,block bb) bbl,Option.map block bo),ctx.com.basic.tvoid,p - | SEWhile(_,bb_body,bb_next,p) -> - let e2 = block bb_body in - Some bb_next,TWhile(e1,e2,NormalWhile),ctx.com.basic.tvoid,p - | _ -> abort (Printf.sprintf "Invalid node exit: %s" (s_expr_pretty e1)) bb.bb_pos - in - bb_next,Some (mk e1_def t p) + if_live bb_next,Some (mk (TTry(block bb_try,List.map (fun (v,bb) -> v,block bb) bbl)) ctx.com.basic.tvoid p) + | SEIfThen(bb_then,bb_next,p) -> + if_live bb_next,Some (mk (TIf(get_terminator(),block bb_then,None)) ctx.com.basic.tvoid p) + | SEIfThenElse(bb_then,bb_else,bb_next,t,p) -> + if_live bb_next,Some (mk (TIf(get_terminator(),block bb_then,Some (block bb_else))) t p) + | SEWhile(bb_body,bb_next,p) -> + let e2 = block bb_body in + if_live bb_next,Some (mk (TWhile(get_terminator(),e2,NormalWhile)) ctx.com.basic.tvoid p) + | SESwitch(bbl,bo,bb_next,p) -> + Some bb_next,Some (mk (TSwitch(get_terminator(),List.map (fun (el,bb) -> el,block bb) bbl,Option.map block bo)) ctx.com.basic.tvoid p) in let bb_next,e_term = loop bb bb.bb_syntax_edge in let el = DynArray.to_list bb.bb_el in @@ -742,7 +747,7 @@ and func ctx i = | OpAdd | OpMult | OpDiv | OpSub | OpAnd | OpOr | OpXor | OpShl | OpShr | OpUShr | OpMod -> true - | OpAssignOp _ | OpInterval | OpArrow | OpIn | OpAssign | OpEq + | OpAssignOp _ | OpInterval | OpArrow | OpIn | OpNullCoal | OpAssign | OpEq | OpNotEq | OpGt | OpGte | OpLt | OpLte | OpBoolAnd | OpBoolOr -> false in @@ -772,4 +777,4 @@ and func ctx i = mk (TFunction {tf with tf_expr = e}) t p let to_texpr ctx = - func ctx ctx.entry.bb_id \ No newline at end of file + func ctx ctx.entry.bb_id diff --git a/src/optimization/analyzerTypes.ml b/src/optimization/analyzerTypes.ml index c9c3bafc..b5c11cab 100644 --- a/src/optimization/analyzerTypes.ml +++ b/src/optimization/analyzerTypes.ml @@ -43,6 +43,9 @@ module BasicBlock = struct | BKUnreachable (* The unique unreachable block *) | BKCatch of tvar (* A catch block *) + type flag_block = + | BlockDce + type cfg_edge_Flag = | FlagExecutable (* Used by constant propagation to handle live edges *) | FlagDce (* Used by DCE to keep track of handled edges *) @@ -67,7 +70,7 @@ module BasicBlock = struct | SEIfThenElse of t * t * t * Type.t * pos (* `if` with "then", "else" and "next" *) | SESwitch of (texpr list * t) list * t option * t * pos (* `switch` with cases, "default" and "next" *) | SETry of t * t * (tvar * t) list * t * pos (* `try` with "exc", catches and "next" *) - | SEWhile of t * t * t * pos (* `while` with "head", "body" and "next" *) + | SEWhile of t * t * pos (* `while` with "body" and "next" *) | SESubBlock of t * t (* "sub" with "next" *) | SEMerge of t (* Merge to same block *) | SENone (* No syntax exit *) @@ -93,6 +96,7 @@ module BasicBlock = struct bb_pos : pos; (* The block position *) bb_kind : block_kind; (* The block kind *) mutable bb_closed : bool; (* Whether or not the block has been closed *) + mutable bb_flags : int; (* elements *) bb_el : texpr DynArray.t; (* The block expressions *) bb_phi : texpr DynArray.t; (* SSA-phi expressions *) @@ -115,6 +119,18 @@ module BasicBlock = struct | LUEl of int | LUTerm + let int_of_block_flag (flag : flag_block) = + Obj.magic flag + + let add_block_flag bb (flag : flag_block) = + bb.bb_flags <- set_flag bb.bb_flags (int_of_block_flag flag) + + let remove_block_flag bb (flag : flag_block) = + bb.bb_flags <- unset_flag bb.bb_flags (int_of_block_flag flag) + + let has_block_flag bb (flag : flag_block) = + has_flag bb.bb_flags (int_of_block_flag flag) + let s_block_kind = function | BKRoot -> "BKRoot" | BKNormal -> "BKNormal" @@ -177,6 +193,7 @@ module BasicBlock = struct bb_type = t; bb_pos = p; bb_closed = false; + bb_flags = 0; bb_el = DynArray.create(); bb_phi = DynArray.create(); bb_terminator = TermNone; @@ -556,9 +573,8 @@ module Graph = struct loop scopes' bb_exc; List.iter (fun (_,bb_catch) -> loop (next_scope scopes) bb_catch) catches; loop scopes bb_next - | SEWhile(bb_head,bb_body,bb_next,_) -> + | SEWhile(bb_body,bb_next,_) -> let scopes' = next_scope scopes in - loop scopes' bb_head; loop scopes' bb_body; loop scopes bb_next; | SESubBlock(bb_sub,bb_next) -> @@ -578,10 +594,13 @@ type analyzer_context = { config : AnalyzerConfig.t; graph : Graph.t; temp_var_name : string; + with_timer : 'a . string list -> (unit -> 'a) -> 'a; + identifier : string; mutable entry : BasicBlock.t; mutable has_unbound : bool; mutable loop_counter : int; mutable loop_stack : int list; mutable debug_exprs : (string * texpr) list; mutable name_stack : string list; + mutable did_optimize : bool; } diff --git a/src/optimization/dce.ml b/src/optimization/dce.ml index 76fec5bc..09e547b5 100644 --- a/src/optimization/dce.ml +++ b/src/optimization/dce.ml @@ -299,7 +299,7 @@ let rec to_string dce t = match t with | TType(tt,tl) -> if not (List.exists (fun t2 -> Type.fast_eq t t2) dce.ts_stack) then begin dce.ts_stack <- t :: dce.ts_stack; - to_string dce (apply_params tt.t_params tl tt.t_type) + to_string dce (apply_typedef tt tl) end | TAbstract({a_impl = Some c} as a,tl) -> if Meta.has Meta.CoreType a.a_meta then @@ -312,12 +312,9 @@ let rec to_string dce t = match t with | _ -> ()) | TLazy f -> to_string dce (lazy_type f) - | TDynamic t -> - if t == t_dynamic then - () - else - to_string dce t - | TEnum _ | TFun _ | TAnon _ | TAbstract({a_impl = None},_) -> + | TDynamic (Some t) -> + to_string dce t + | TEnum _ | TFun _ | TAnon _ | TAbstract({a_impl = None},_) | TDynamic None -> (* if we to_string these it does not imply that we need all its sub-types *) () @@ -585,11 +582,11 @@ and expr dce e = check_and_add_feature dce "unsafe_string_concat"; expr dce e1; expr dce e2; - | TArray(({etype = TDynamic t} as e1),e2) when t == t_dynamic -> + | TArray(({etype = TDynamic None} as e1),e2) -> check_and_add_feature dce "dynamic_array_read"; expr dce e1; expr dce e2; - | TBinop( (OpAssign | OpAssignOp _), ({eexpr = TArray({etype = TDynamic t},_)} as e1), e2) when t == t_dynamic -> + | TBinop( (OpAssign | OpAssignOp _), ({eexpr = TArray({etype = TDynamic None},_)} as e1), e2) -> check_and_add_feature dce "dynamic_array_write"; expr dce e1; expr dce e2; diff --git a/src/optimization/inline.ml b/src/optimization/inline.ml index cae95334..2115fa29 100644 --- a/src/optimization/inline.ml +++ b/src/optimization/inline.ml @@ -7,7 +7,7 @@ open Typecore open Error let needs_inline ctx is_extern_class cf = - cf.cf_kind = Method MethInline + cf.cf_kind = Method MethInline && ctx.allow_inline && (ctx.g.doinline || is_extern_class || has_class_field_flag cf CfExtern) let mk_untyped_call name p params = @@ -112,7 +112,7 @@ let api_inline2 com c field params p = let api_inline ctx c field params p = let mk_typeexpr path = - let m = (try Hashtbl.find ctx.g.modules path with Not_found -> die "" __LOC__) in + let m = (try ctx.com.module_lut#find path with Not_found -> die "" __LOC__) in add_dependency ctx.m.curmod m; Option.get (ExtList.List.find_map (function | TClassDecl cl when cl.cl_path = path -> Some (make_static_this cl p) @@ -254,7 +254,7 @@ let inline_config cls_opt cf call_args return_type = let t = if cf.cf_name = "_new" then return_type else if call_args = [] then - error "Invalid abstract implementation function" cf.cf_pos + typing_error "Invalid abstract implementation function" cf.cf_pos else follow (List.hd call_args).etype in @@ -308,7 +308,8 @@ class inline_state ctx ethis params cf f p = object(self) try Hashtbl.find locals v.v_id with Not_found -> - let v' = alloc_var (match v.v_kind with VUser _ -> VInlined | k -> k) v.v_name v.v_type v.v_pos in + let name = Option.default v.v_name (get_meta_string v.v_meta Meta.RealPath) in + let v' = alloc_var (match v.v_kind with VUser _ -> VInlined | k -> k) name v.v_type v.v_pos in v'.v_extra <- v.v_extra; let i = { i_var = v; @@ -368,11 +369,23 @@ class inline_state ctx ethis params cf f p = object(self) in try loop e; true with Exit -> false in - let rec is_writable e = + let rec check_write e = match e.eexpr with - | TField _ | TEnumParameter _ | TLocal _ | TArray _ -> true - | TCast(e1,None) -> is_writable e1 - | _ -> false + | TLocal v when has_var_flag v VFinal -> + typing_error "Cannot modify abstract value of final local" p + | TField(_,fa) -> + begin match extract_field fa with + | Some cf when has_class_field_flag cf CfFinal -> + typing_error "Cannot modify abstract value of final field" p + | _ -> + () + end + | TLocal _ | TEnumParameter _ | TArray _ -> + () + | TCast(e1,None) -> + check_write e1 + | _ -> + typing_error "Cannot modify the abstract value, store it into a local first" p; in let vars = List.fold_left (fun acc (i,e) -> let accept vik = @@ -386,7 +399,7 @@ class inline_state ctx ethis params cf f p = object(self) (i.i_subst,Some e) :: acc in if i.i_abstract_this && i.i_write then begin - if not (is_writable e) then error "Cannot modify the abstract value, store it into a local first" p; + check_write e; accept VIInline end else if i.i_force_temp || (i.i_captured && not (is_constant e)) then reject() @@ -397,7 +410,7 @@ class inline_state ctx ethis params cf f p = object(self) | TLocal _ | TConst _ -> if not i.i_write then VIInline else VIDoNotInline | TFunction _ -> - if i.i_write then error "Cannot modify a closure parameter inside inline method" p; + if i.i_write then typing_error "Cannot modify a closure parameter inside inline method" p; if i.i_read <= 1 then VIInline else VIInlineIfCalled | _ -> if not i.i_write && (i.i_read + i.i_called) <= 1 then VIInline else VIDoNotInline @@ -583,14 +596,14 @@ class inline_state ctx ethis params cf f p = object(self) let e = Diagnostics.secure_generated_code ctx e in if has_params then begin let mt = map_type cf.cf_type in - let unify_func () = unify_raise ctx mt (TFun (tl,tret)) p in + let unify_func () = unify_raise mt (TFun (tl,tret)) p in (match follow ethis.etype with | TAnon a -> (match !(a.a_status) with | Statics {cl_kind = KAbstractImpl a } when has_class_field_flag cf CfImpl -> if cf.cf_name <> "_new" then begin (* the first argument must unify with a_this for abstract implementation functions *) let tb = (TFun(("",false,map_type a.a_this) :: (List.tl tl),tret)) in - unify_raise ctx mt tb p + unify_raise mt tb p end | _ -> unify_func()) | _ -> unify_func()); @@ -689,7 +702,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f l.i_read <- l.i_read + (if !in_loop then 2 else 1); { e with eexpr = TLocal l.i_subst } | None -> - error "Could not inline `this` outside of an instance context" po + typing_error "Could not inline `this` outside of an instance context" po ) | TVar (v,eo) -> { e with eexpr = TVar ((state#declare v).i_subst,opt (map false false) eo)} @@ -697,9 +710,9 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f if not term then begin match cf.cf_kind with | Method MethInline -> - error "Cannot inline a not final return" po + typing_error "Cannot inline a not final return" po | _ -> - error ("Function " ^ cf.cf_name ^ " cannot be inlined because of a not final return") p + typing_error ("Function " ^ cf.cf_name ^ " cannot be inlined because of a not final return") p end; (match eo with | None -> mk (TConst TNull) f.tf_type p @@ -722,7 +735,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f in_loop := old; { e with eexpr = TWhile (cond,eloop,flag) } | TSwitch (e1,cases,def) when term -> - let term = term && (def <> None || is_exhaustive e1) in + let term = term && (is_exhaustive e1 def) in let cases = List.map (fun (el,e) -> let el = List.map (map false false) el in el, map term false e @@ -816,9 +829,9 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f | TInst({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some ({eexpr = TFunction tf})} as cf)} as c,_) -> begin match type_inline_ctor ctx c cf tf ethis el po with | Some e -> map term false e - | None -> error "Could not inline super constructor call" po + | None -> typing_error "Could not inline super constructor call" po end - | _ -> error "Cannot inline function containing super" po + | _ -> typing_error "Cannot inline function containing super" po end | TCall(e1,el) -> state#set_side_effect; @@ -826,7 +839,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f let el = List.map (map false false) el in {e with eexpr = TCall(e1,el)} | TConst TSuper -> - error "Cannot inline function containing super" po + typing_error "Cannot inline function containing super" po | TMeta((Meta.Ast,_,_) as m,e1) when term -> (* Special case for @:ast-wrapped TSwitch nodes: If the recursion alters the type of the TSwitch node, we also want to alter the type of the TMeta node. *) @@ -864,7 +877,7 @@ let rec type_inline ctx cf f ethis params tret config p ?(self_calling_closure=f (* Same as type_inline, but modifies the function body to add field inits *) and type_inline_ctor ctx c cf tf ethis el po = let field_inits = - let cparams = List.map snd c.cl_params in + let cparams = extract_param_types c.cl_params in let ethis = mk (TConst TThis) (TInst (c,cparams)) c.cl_pos in let el = List.fold_left (fun acc cf -> match cf.cf_kind,cf.cf_expr with @@ -902,7 +915,7 @@ and inline_rest_params ctx f params map_type p = let cf = try PMap.find "of" c.cl_statics with Not_found -> die ~p:c.cl_name_pos "Can't find haxe.Rest.of function" __LOC__ - and p = punion_el (List.map (fun e -> (),e.epos) params) in + and p = punion_el p params in (* [param1, param2, ...] *) (* If we don't know the type (e.g. the rest argument is a field type parameter) then we need to find a common type via unify_min. *) diff --git a/src/optimization/inlineConstructors.ml b/src/optimization/inlineConstructors.ml index 6db7247d..76ba820e 100644 --- a/src/optimization/inlineConstructors.ml +++ b/src/optimization/inlineConstructors.ml @@ -122,8 +122,8 @@ let inline_constructors ctx original_e = | IOKCtor(ioc) -> List.iter (fun v -> if v.v_id < 0 then cancel_v v p) io.io_dependent_vars; if ioc.ioc_forced then begin - display_error ctx "Forced inline constructor could not be inlined" io.io_pos; - display_error ctx (compl_msg "Cancellation happened here") p; + display_error ctx.com "Forced inline constructor could not be inlined" io.io_pos; + display_error ~depth:1 ctx.com (compl_msg "Cancellation happened here") p; end | _ -> () end @@ -227,7 +227,7 @@ let inline_constructors ctx original_e = let e = Type.map_expr (mark_ctors ~force_inline:is_meta_inline) e in let mark() = incr curr_io_id; - let id_expr = (EConst(Int (string_of_int !curr_io_id)), e.epos) in + let id_expr = (EConst(Int (string_of_int !curr_io_id, None)), e.epos) in let meta = (Meta.InlineObject, [id_expr], e.epos) in mk (TMeta(meta, e)) e.etype e.epos in @@ -316,7 +316,11 @@ let inline_constructors ctx original_e = let iv_is_const iv = match iv.iv_kind with IVKField(_,_,Some(_)) -> true | _ -> false in if is_lvalue && iv_is_const fiv then raise Not_found; if fiv.iv_closed then raise Not_found; - if not captured || (not is_lvalue && fiv.iv_state == IVSUnassigned) then cancel_iv fiv efield.epos; + if not is_lvalue && fiv.iv_state == IVSUnassigned then ( + warning ctx WConstructorInliningCancelled ("Constructor inlining cancelled because of use of uninitialized member field " ^ fname) ethis.epos; + raise Not_found + ); + if not captured then cancel_iv fiv efield.epos; IOFInlineVar(fiv) with Not_found -> cancel_iv iv efield.epos; @@ -395,11 +399,11 @@ let inline_constructors ctx original_e = Some iv | _ -> List.iter (fun v -> cancel_v v v.v_pos) argvs; - if is_extern_ctor c cf then display_error ctx "Extern constructor could not be inlined" e.epos; + if is_extern_ctor c cf then display_error ctx.com "Extern constructor could not be inlined" e.epos; None end | TNew({ cl_constructor = Some ({cf_kind = Method MethInline; cf_expr = Some _} as cf)} as c,_,pl),_ when is_extern_ctor c cf -> - error "Extern constructor could not be inlined" e.epos; + typing_error "Extern constructor could not be inlined" e.epos; | TObjectDecl fl, _ when captured && fl <> [] && List.for_all (fun((s,_,_),_) -> Lexer.is_valid_identifier s) fl -> let v = alloc_var VGenerated "inlobj" e.etype e.epos in let ev = mk (TLocal v) v.v_type e.epos in @@ -435,10 +439,10 @@ let inline_constructors ctx original_e = handle_default_case e in match e.eexpr with - | TMeta((Meta.Inline,_,_),{eexpr = TMeta((Meta.InlineObject, [(EConst(Int (id_str)), _)], _), e)}) -> + | TMeta((Meta.Inline,_,_),{eexpr = TMeta((Meta.InlineObject, [(EConst(Int (id_str, None)), _)], _), e)}) -> let io_id = int_of_string id_str in handle_inline_object_case io_id true e - | TMeta((Meta.InlineObject, [(EConst(Int (id_str)), _)], _), e) -> + | TMeta((Meta.InlineObject, [(EConst(Int (id_str, None)), _)], _), e) -> let io_id = int_of_string id_str in handle_inline_object_case io_id false e | TVar(v,None) -> ignore(add v IVKLocal); None @@ -528,6 +532,8 @@ let inline_constructors ctx original_e = List.iter (fun ca -> ignore(analyze_aliases false ca)) call_args; None end + | TFunction tf -> + analyze_aliases true tf.tf_expr | _ -> handle_default_case e in @@ -588,7 +594,7 @@ let inline_constructors ctx original_e = end in match e.eexpr with - | TMeta((Meta.InlineObject, [(EConst(Int (id_str)), _)], _), e) -> + | TMeta((Meta.InlineObject, [(EConst(Int (id_str, _)), _)], _), e) -> let io_id = int_of_string id_str in begin try let io = get_io io_id in diff --git a/src/optimization/optimizer.ml b/src/optimization/optimizer.ml index 22ad09e3..cda13f04 100644 --- a/src/optimization/optimizer.ml +++ b/src/optimization/optimizer.ml @@ -55,6 +55,7 @@ let standard_precedence op = | OpBoolAnd -> 14, left | OpBoolOr -> 15, left | OpArrow -> 16, left + | OpNullCoal -> 17, right | OpAssignOp OpAssign -> 18, right (* mimics ?: *) | OpAssign | OpAssignOp _ -> 19, right @@ -198,7 +199,7 @@ let reduce_expr com e = List.iter (fun (cl,_) -> List.iter (fun e -> match e.eexpr with - | TCall ({ eexpr = TField (_,FEnum _) },_) -> error "Not-constant enum in switch cannot be matched" e.epos + | TCall ({ eexpr = TField (_,FEnum _) },_) -> typing_error "Not-constant enum in switch cannot be matched" e.epos | _ -> () ) cl ) cases; @@ -237,24 +238,68 @@ let check_enum_construction_args el i = ) (true,0) el in b +let rec extract_constant_value e = match e.eexpr with + | TConst (TInt _ | TFloat _ | TString _ | TBool _ | TNull) -> + Some e + | TConst (TThis | TSuper) -> + None + | TField(_,FStatic(c,({cf_kind = Var {v_write = AccNever}} as cf))) -> + begin match cf.cf_expr with + | Some e -> + (* Don't care about inline, if we know the value it makes no difference. *) + extract_constant_value e + | None -> + None + end + | TField(_,FEnum _) -> + Some e + | TParenthesis e1 -> + extract_constant_value e1 + | _ -> + None + let check_constant_switch e1 cases def = let rec loop e1 cases = match cases with | (el,e) :: cases -> - if List.exists (Texpr.equal e1) el then Some e - else loop e1 cases + (* Map everything first so that we find unknown things eagerly. *) + let el = List.map (fun e2 -> match extract_constant_value e2 with + | Some e2 -> e2 + | None -> raise Exit + ) el in + if List.exists (fun e2 -> + Texpr.equal e1 e2 + ) el then + Some e + else + loop e1 cases | [] -> begin match def with | None -> None | Some e -> Some e end in + let is_empty e = match e.eexpr with + | TBlock [] -> true + | _ -> false + in + let is_empty_def () = match def with + | None -> true + | Some e -> is_empty e +in match Texpr.skip e1 with | {eexpr = TConst ct} as e1 when (match ct with TSuper | TThis -> false | _ -> true) -> - loop e1 cases + begin try + loop e1 cases + with Exit -> + None + end | _ -> - None + if List.for_all (fun (_,e) -> is_empty e) cases && is_empty_def() then + Some e1 + else + None -let reduce_control_flow ctx e = match e.eexpr with +let reduce_control_flow com e = match e.eexpr with | TIf ({ eexpr = TConst (TBool t) },e1,e2) -> (if t then e1 else match e2 with None -> { e with eexpr = TBlock [] } | Some e -> e) | TWhile ({ eexpr = TConst (TBool false) },sub,flag) -> @@ -279,10 +324,10 @@ let reduce_control_flow ctx e = match e.eexpr with (try List.nth el i with Failure _ -> e) | TCast(e1,None) -> (* TODO: figure out what's wrong with these targets *) - let require_cast = match ctx.com.platform with + let require_cast = match com.platform with | Cpp | Flash -> true - | Java -> defined ctx.com Define.Jvm - | Cs -> defined ctx.com Define.EraseGenerics || defined ctx.com Define.FastCast + | Java -> defined com Define.Jvm + | Cs -> defined com Define.EraseGenerics || defined com Define.FastCast | _ -> false in Texpr.reduce_unsafe_casts ~require_cast e e.etype @@ -300,7 +345,7 @@ let rec reduce_loop ctx e = let cf = mk_field "" ef.etype e.epos null_pos in let ethis = mk (TConst TThis) t_dynamic e.epos in let rt = (match follow ef.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in - let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error (Custom _,_) -> None) in + let inl = (try type_inline ctx cf func ethis el rt None e.epos ~self_calling_closure:true false with Error (Custom _,_,_) -> None) in (match inl with | None -> reduce_expr ctx e | Some e -> reduce_loop ctx e) @@ -309,7 +354,7 @@ let rec reduce_loop ctx e = | Some {eexpr = TFunction tf} -> let config = inline_config (Some cl) cf el e.etype in let rt = (match follow e1.etype with TFun (_,rt) -> rt | _ -> die "" __LOC__) in - let inl = (try type_inline ctx cf tf ef el rt config e.epos false with Error (Custom _,_) -> None) in + let inl = (try type_inline ctx cf tf ef el rt config e.epos false with Error (Custom _,_,_) -> None) in (match inl with | None -> reduce_expr ctx e | Some e -> @@ -325,7 +370,7 @@ let rec reduce_loop ctx e = reduce_expr ctx e end | _ -> - reduce_expr ctx (reduce_control_flow ctx e)) + reduce_expr ctx (reduce_control_flow ctx.com e)) let reduce_expression ctx e = if ctx.com.foptimize then @@ -374,4 +419,4 @@ let rec make_constant_expression ctx ?(concat_strings=false) e = | None -> None | Some e -> make_constant_expression ctx e) with Not_found -> None) *) - | _ -> None \ No newline at end of file + | _ -> None diff --git a/src/optimization/optimizerTexpr.ml b/src/optimization/optimizerTexpr.ml index 9ff69ba0..28e457c9 100644 --- a/src/optimization/optimizerTexpr.ml +++ b/src/optimization/optimizerTexpr.ml @@ -21,12 +21,6 @@ let has_side_effect e = with Exit -> true -let rec is_exhaustive e1 = match e1.eexpr with - | TMeta((Meta.Exhaustive,_,_),_) -> true - | TMeta(_, e1) | TParenthesis e1 -> is_exhaustive e1 - | _ -> false - - let is_read_only_field_access e fa = match fa with | FEnum _ -> true @@ -136,9 +130,9 @@ let optimize_binop e op e1 e2 = | OpAnd -> opt Int32.logand | OpOr -> opt Int32.logor | OpXor -> opt Int32.logxor - | OpShl -> opt (fun a b -> Int32.shift_left a (Int32.to_int b)) - | OpShr -> opt (fun a b -> Int32.shift_right a (Int32.to_int b)) - | OpUShr -> opt (fun a b -> Int32.shift_right_logical a (Int32.to_int b)) + | OpShl -> opt (fun a b -> Int32.shift_left a (Int32.to_int (Int32.logand b i32_31))) + | OpShr -> opt (fun a b -> Int32.shift_right a (Int32.to_int (Int32.logand b i32_31))) + | OpUShr -> opt (fun a b -> Int32.shift_right_logical a (Int32.to_int (Int32.logand b i32_31))) | OpEq -> ebool (=) | OpNotEq -> ebool (<>) | OpGt -> ebool (>) @@ -215,7 +209,7 @@ let optimize_binop e op e1 e2 = | OpAssign,_ -> e | _ -> - error "You cannot directly compare enums with arguments. Use either `switch`, `match` or `Type.enumEq`" e.epos + typing_error "You cannot directly compare enums with arguments. Use either `switch`, `match` or `Type.enumEq`" e.epos end | _ -> e) diff --git a/src/syntax/grammar.mly b/src/syntax/grammar.mly index d0315c04..685e0042 100644 --- a/src/syntax/grammar.mly +++ b/src/syntax/grammar.mly @@ -42,6 +42,11 @@ let rec psep sep f = parser | [< r = psep_nonempty sep f >] -> r | [< >] -> [] +let rec psep_trailing sep f = parser + | [< v = f; '(sep2,_) when sep2 = sep; l = psep_trailing sep f >] -> v :: l + | [< v = f >] -> [v] + | [< >] -> [] + let pignore f = try ignore(f()) @@ -108,6 +113,12 @@ let semicolon s = | [< s >] -> syntax_error Missing_semicolon s (next_pos s) +let check_redundant_var p1 = parser + | [< '(Kwd Var),p2; s >] -> + syntax_error (Custom "`final var` is not supported, use `final` instead") ~pos:(Some (punion p1 p2)) s (); + | [< >] -> + () + let parsing_macro_cond = ref false let rec parse_file s = @@ -223,7 +234,7 @@ and parse_type_decl mode s = | [< '(Kwd Using,p1) >] -> parse_using s p1 | [< doc = get_doc; meta = parse_meta; c = parse_common_flags; s >] -> match s with parser - | [< '(Kwd Function,p1); name = dollar_ident; pl = parse_constraint_params; '(POpen,_); args = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> + | [< '(Kwd Function,p1); name = dollar_ident; pl = parse_constraint_params; '(POpen,_); args = psep_trailing Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> let e, p2 = (match s with parser | [< e = expr; s >] -> ignore(semicolon s); @@ -336,7 +347,7 @@ and parse_class doc meta cflags need_name s = d_data = fl; }, punion p1 p2) -and parse_import s p1 = +and parse_import' s p1 = let rec loop pn acc = match s with parser | [< '(Dot,p) >] -> @@ -353,32 +364,43 @@ and parse_import s p1 = loop pn (("extern",p) :: acc) | [< '(Kwd Function,p) >] -> loop pn (("function",p) :: acc) - | [< '(Binop OpMult,_); '(Semicolon,p2) >] -> - p2, List.rev acc, IAll + | [< '(Binop OpMult,_) >] -> + List.rev acc, IAll | [< >] -> ignore(popt semicolon s); - syntax_error (Expected ["identifier"]) s (p,List.rev acc,INormal) + syntax_error (Expected ["identifier"]) s (List.rev acc,INormal) end - | [< '(Semicolon,p2) >] -> - p2, List.rev acc, INormal - | [< '(Kwd In,_); '(Const (Ident name),pname); '(Semicolon,p2) >] -> - p2, List.rev acc, IAsName(name,pname) - | [< '(Const (Ident "as"),_); '(Const (Ident name),pname); '(Semicolon,p2) >] -> - p2, List.rev acc, IAsName(name,pname) + | [< '(Kwd In,_); '(Const (Ident name),pname) >] -> + List.rev acc, IAsName(name,pname) + | [< '(Const (Ident "as"),_); '(Const (Ident name),pname) >] -> + List.rev acc, IAsName(name,pname) | [< >] -> - syntax_error (Expected [".";";";"as"]) s ((last_pos s),List.rev acc,INormal) + List.rev acc,INormal in - let p2, path, mode = (match s with parser + let path, mode = (match s with parser | [< '(Const (Ident name),p) >] -> loop p [name,p] | [< >] -> if would_skip_display_position p1 true s then - (display_position#with_pos p1,[],INormal) + ([],INormal) else - syntax_error (Expected ["identifier"]) s (p1,[],INormal) + syntax_error (Expected ["identifier"]) s ([],INormal) ) in + (path,mode) + +and parse_import s p1 = + let (path,mode) = parse_import' s p1 in + let p2 = match s with parser + | [< '(Semicolon,p2) >] -> + p2 + | [< >] -> + if would_skip_display_position p1 true s then + display_position#with_pos p1 + else + syntax_error (Expected [".";";";"as"]) s (last_pos s) + in (EImport (path,mode),punion p1 p2) -and parse_using s p1 = +and parse_using' s p1 = let rec loop pn acc = match s with parser | [< '(Dot,p) >] -> @@ -393,21 +415,30 @@ and parse_using s p1 = | [< '(Kwd Function,p) >] -> loop pn (("function",p) :: acc) | [< >] -> - syntax_error (Expected ["identifier"]) s (p,List.rev acc); + syntax_error (Expected ["identifier"]) s (List.rev acc); end - | [< '(Semicolon,p2) >] -> - p2,List.rev acc | [< >] -> - syntax_error (Expected [".";";"]) s ((last_pos s),List.rev acc) + List.rev acc in - let p2, path = (match s with parser + match s with parser | [< '(Const (Ident name),p) >] -> loop p [name,p] | [< >] -> if would_skip_display_position p1 true s then - (display_position#with_pos p1,[]) + [] else - syntax_error (Expected ["identifier"]) s (p1,[]) - ) in + syntax_error (Expected ["identifier"]) s [] + +and parse_using s p1 = + let path = parse_using' s p1 in + let p2 = match s with parser + | [< '(Semicolon,p2) >] -> + p2 + | [< >] -> + if would_skip_display_position p1 true s then + display_position#with_pos p1 + else + syntax_error (Expected [".";";"]) s (last_pos s) + in (EUsing path,punion p1 p2) and parse_abstract_relations s = @@ -475,7 +506,7 @@ and resume tdecl fdecl s = | Kwd New :: Kwd Function :: _ when fdecl -> junk_tokens (k - 2); true - | Kwd Macro :: _ | Kwd Public :: _ | Kwd Static :: _ | Kwd Var :: _ | Kwd Override :: _ | Kwd Dynamic :: _ | Kwd Inline :: _ | Kwd Overload :: _ when fdecl -> + | Kwd Macro :: _ | Kwd Public :: _ | Kwd Static :: _ | Kwd Var :: _ | Kwd Final :: _ | Kwd Override :: _ | Kwd Dynamic :: _ | Kwd Inline :: _ | Kwd Overload :: _ when fdecl -> junk_tokens (k - 1); true | BrClose :: _ when tdecl -> @@ -551,14 +582,14 @@ and parse_meta_argument_expr s = end and parse_meta_params pname s = match s with parser - | [< '(POpen,p) when p.pmin = pname.pmax; params = psep Comma parse_meta_argument_expr; >] -> + | [< '(POpen,p) when p.pmin = pname.pmax; params = psep_trailing Comma parse_meta_argument_expr; >] -> ignore(expect_unless_resume_p PClose s); params | [< >] -> [] and parse_meta_entry = parser [< '(At,p1); s >] -> - let meta = check_resume p1 (fun () -> Some (Meta.Last,[],p1)) (fun () -> None) in + let meta = check_resume p1 (fun () -> Some (Meta.HxCompletion,[],p1)) (fun () -> None) in match s with parser | [< name,p = parse_meta_name p1; params = parse_meta_params p >] -> (name,params,punion p1 p) | [< >] -> match meta with None -> serror() | Some meta -> meta @@ -580,7 +611,7 @@ and parse_meta_name_2 p1 acc s = and parse_meta_name p1 = parser | [< '(DblDot,p) when p.pmin = p1.pmax; s >] -> - let meta = check_resume p (fun () -> Some (Meta.Last,p)) (fun() -> None) in + let meta = check_resume p (fun () -> Some (Meta.HxCompletion,p)) (fun() -> None) in begin match s with parser | [< name,p2 = parse_meta_name_2 p [] >] -> (Meta.parse (rev_concat "." name)),p2 | [< >] -> match meta with None -> raise Stream.Failure | Some meta -> meta @@ -620,7 +651,7 @@ and parse_type_opt = parser and parse_complex_type s = parse_complex_type_maybe_named false s and parse_complex_type_maybe_named allow_named = parser - | [< '(POpen,p1); tl = psep Comma (parse_complex_type_maybe_named true); '(PClose,p2); s >] -> + | [< '(POpen,p1); tl = psep_trailing Comma (parse_complex_type_maybe_named true); '(PClose,p2); s >] -> begin match tl with | [] | [(CTNamed _,_)] -> (* it was () or (a:T) - clearly a new function type syntax, proceed with parsing return type *) @@ -849,7 +880,7 @@ and parse_enum s = match s with parser | [< name, p1 = ident; params = parse_constraint_params; s >] -> let args = (match s with parser - | [< '(POpen,_); l = psep Comma parse_enum_param; '(PClose,_) >] -> l + | [< '(POpen,_); l = psep_trailing Comma parse_enum_param; '(PClose,_) >] -> l | [< >] -> [] ) in let t = popt parse_type_hint s in @@ -872,7 +903,7 @@ and parse_enum_param = parser | [< name, _ = ident; t = parse_type_hint >] -> (name,false,t) and parse_function_field doc meta al = parser - | [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); args = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> + | [< '(Kwd Function,p1); name = parse_fun_name; pl = parse_constraint_params; '(POpen,_); args = psep_trailing Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> let e, p2 = (match s with parser | [< e = expr; s >] -> ignore(semicolon s); @@ -930,6 +961,7 @@ and parse_class_field tdecl s = name,punion p1 p2,FVar (t,e),al,meta end | [< '(Kwd Final,p1) >] -> + check_redundant_var p1 s; begin match s with parser | [< opt,name = questionable_dollar_ident; t = popt parse_type_hint; e,p2 = parse_var_field_assignment >] -> let meta = check_optional opt name in @@ -1028,21 +1060,25 @@ and parse_constraint_param s = let meta = parse_meta s in match s with parser | [< name = type_name; s >] -> - let params = (match s with parser - | [< >] -> [] - ) in let cto = (match s with parser | [< '(DblDot,_); s >] -> (match s with parser - | [< '(POpen,p1); t = parse_complex_type; '(PClose,p2) >] -> Some t + | [< t = parse_complex_type >] -> Some t + | [< >] -> serror()) + | [< >] -> None + ) in + let default = (match s with parser + | [< '(Binop OpAssign,_); s >] -> + (match s with parser | [< t = parse_complex_type >] -> Some t | [< >] -> serror()) | [< >] -> None ) in { tp_name = name; - tp_params = params; + tp_params = []; tp_constraints = cto; + tp_default = default; tp_meta = meta; } | [< >] -> @@ -1105,17 +1141,33 @@ and block_with_pos' acc f p s = and block_with_pos acc p s = block_with_pos' acc parse_block_elt p s -and parse_block_elt = parser +and parse_block_var = parser | [< '(Kwd Var,p1); vl = parse_var_decls false p1; p2 = semicolon >] -> - (EVars vl,punion p1 p2) - | [< '(Kwd Final,p1); vl = parse_var_decls true p1; p2 = semicolon >] -> - (EVars vl,punion p1 p2) + (vl,punion p1 p2) + | [< '(Kwd Final,p1); s >] -> + check_redundant_var p1 s; + match s with parser + | [< vl = parse_var_decls true p1; p2 = semicolon >] -> + (vl,punion p1 p2) + | [< >] -> + serror(); + +and parse_block_elt = parser + | [< (vl,p) = parse_block_var >] -> + (EVars vl,p) | [< '(Kwd Inline,p1); s >] -> begin match s with parser | [< '(Kwd Function,_); e = parse_function p1 true; _ = semicolon >] -> e | [< e = secure_expr; _ = semicolon >] -> make_meta Meta.Inline [] e p1 | [< >] -> serror() end + | [< '(Kwd Static,p); s >] -> + begin match s with parser + | [< (vl,p) = parse_block_var >] -> + let vl = List.map (fun ev -> {ev with ev_static = true}) vl in + (EVars vl,p) + | [<>] -> syntax_error (Expected ["var";"final"]) s (mk_null_expr p) + end | [< '(Binop OpLt,p1); s >] -> let e = handle_xml_literal p1 in (* accept but don't expect semicolon *) @@ -1184,6 +1236,14 @@ and parse_var_decl_head final s = let meta = parse_meta s in match s with parser | [< name, p = dollar_ident; t = popt parse_type_hint >] -> (meta,name,final,t,p) + | [< >] -> + (* This nonsense is here for the var @ case in issue #9639 *) + let rec loop meta = match meta with + | (Meta.HxCompletion,_,p) :: _ -> (meta,"",false,None,null_pos) + | _ :: meta -> loop meta + | [] -> no_keyword "variable name" s + in + loop meta and parse_var_assignment = parser | [< '(Binop OpAssign,p1); s >] -> @@ -1221,16 +1281,28 @@ and parse_macro_expr p = parser (ECheckType (t,(CTPath (mk_type_path ~sub:"ComplexType" (["haxe";"macro"],"Expr")),null_pos)),p) | [< '(Kwd Var,p1); vl = psep Comma (parse_var_decl false) >] -> reify_expr (EVars vl,p1) !in_macro - | [< '(Kwd Final,p1); vl = psep Comma (parse_var_decl true) >] -> - reify_expr (EVars vl,p1) !in_macro + | [< '(Kwd Final,p1); s >] -> + check_redundant_var p1 s; + begin match s with parser + | [< vl = psep Comma (parse_var_decl true) >] -> + reify_expr (EVars vl,p1) !in_macro + | [< >] -> + serror() + end | [< d = parse_class None [] [] false >] -> let _,_,to_type = reify !in_macro in (ECheckType (to_type d,(CTPath (mk_type_path ~sub:"TypeDefinition" (["haxe";"macro"],"Expr")),null_pos)),p) | [< e = secure_expr >] -> reify_expr e !in_macro -and parse_function p1 inl = parser - | [< name = popt dollar_ident; pl = parse_constraint_params; '(POpen,_); al = psep Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> +and parse_function p1 inl s = + let name = match s with parser + | [< name = dollar_ident >] -> Some name + | [< >] -> None + in + let pl = parse_constraint_params s in + match s with parser + | [< '(POpen,_); al = psep_trailing Comma parse_fun_param; '(PClose,_); t = popt parse_type_hint; s >] -> let make e = let f = { f_params = pl; @@ -1241,6 +1313,24 @@ and parse_function p1 inl = parser EFunction ((match name with None -> FKAnonymous | Some (name,pn) -> FKNamed ((name,pn),inl)),f), punion p1 (pos e) in make (secure_expr s) + | [< >] -> + (* Generate pseudo function to avoid toplevel-completion (issue #10691). We check against p1 here in order to cover cases + like `function a|b` *) + if would_skip_display_position p1 false s then begin + let null_func = + let f = { + f_params = []; + f_type = None; + f_args = []; + f_expr = None + } in + let p = punion p1 (next_pos s) in + let name = ("_hx_magic",p) in + EFunction(FKNamed(name,inl),f),p + in + null_func + end else + serror() and arrow_expr = parser | [< '(Arrow,_); e = expr >] -> e @@ -1308,14 +1398,22 @@ and expr = parser expr_next (EConst (Ident (s_keyword k)), p) s | [< '(Kwd Macro,p); s >] -> begin match s with parser - | [< '(Dot,pd); e = parse_field (EConst (Ident "macro"),p) pd >] -> e + | [< '(Dot,pd); e = parse_field (EConst (Ident "macro"),p) EFNormal pd >] -> e | [< e = parse_macro_expr p >] -> e | [< >] -> serror() end | [< '(Kwd Var,p1); v = parse_var_decl false >] -> (EVars [v],p1) - | [< '(Kwd Final,p1); v = parse_var_decl true >] -> (EVars [v],p1) + | [< '(Kwd Final,p1); s >] -> + check_redundant_var p1 s; + begin match s with parser + | [< v = parse_var_decl true >] -> + (EVars [v],p1) + | [< >] -> + serror() + end | [< '(Const c,p); s >] -> expr_next (EConst c,p) s | [< '(Kwd This,p); s >] -> expr_next (EConst (Ident "this"),p) s + | [< '(Kwd Abstract,p); s >] -> expr_next (EConst (Ident "abstract"),p) s | [< '(Kwd True,p); s >] -> expr_next (EConst (Ident "true"),p) s | [< '(Kwd False,p); s >] -> expr_next (EConst (Ident "false"),p) s | [< '(Kwd Null,p); s >] -> expr_next (EConst (Ident "null"),p) s @@ -1342,16 +1440,16 @@ and expr = parser | [< '(POpen,p1); s >] -> (match s with parser | [< '(PClose,p2); er = arrow_expr; >] -> arrow_function p1 [] er s - | [< '(Question,p2); al = psep Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> + | [< '(Question,p2); al = psep_trailing Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> let al = (match al with | (np,_,_,topt,e) :: al -> (np,true,[],topt,e) :: al | _ -> die "" __LOC__ ) in arrow_function p1 al er s | [< e = expr; s >] -> (match s with parser | [< '(PClose,p2); s >] -> expr_next (EParenthesis e, punion p1 p2) s - | [< '(Comma,pc); al = psep Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> + | [< '(Comma,pc); al = psep_trailing Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> arrow_function p1 ((arrow_first_param e s) :: al) er s | [< t,pt = parse_type_hint; s >] -> (match s with parser | [< '(PClose,p2); s >] -> expr_next (EParenthesis (ECheckType(e,(t,pt)),punion p1 p2), punion p1 p2) s - | [< '(Comma,pc); al = psep Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> + | [< '(Comma,pc); al = psep_trailing Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> let (np,_) = arrow_ident_checktype e in arrow_function p1 ((np,false,[],(Some(t,pt)),None) :: al) er s | [< '((Binop OpAssign),p2); ea1 = expr; s >] -> @@ -1363,7 +1461,7 @@ and expr = parser (match s with parser | [< '(PClose,p2); er = arrow_expr; >] -> with_args [] er - | [< '(Comma,pc); al = psep Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> + | [< '(Comma,pc); al = psep_trailing Comma parse_fun_param; '(PClose,_); er = arrow_expr; >] -> with_args al er | [< >] -> serror()) | [< >] -> serror()) @@ -1452,7 +1550,7 @@ and expr = parser syntax_error (Expected ["{"]) s (ESwitch(e,[],None),punion p1 (pos e)) end | [< '(Kwd Try,p1); e = secure_expr; cl,p2 = parse_catches e [] (pos e) >] -> (ETry (e,cl),punion p1 p2) - | [< '(IntInterval i,p1); e2 = expr >] -> make_binop OpInterval (EConst (Int i),p1) e2 + | [< '(IntInterval i,p1); e2 = expr >] -> make_binop OpInterval (EConst (Int (i, None)),p1) e2 | [< '(Kwd Untyped,p1); e = secure_expr >] -> (EUntyped e,punion p1 (pos e)) | [< '(Dollar v,p); s >] -> expr_next (EConst (Ident ("$"^v)),p) s | [< '(Kwd Inline,p); e = secure_expr >] -> make_meta Meta.Inline [] e p @@ -1469,14 +1567,15 @@ and expr_next' e1 = parser (match fst e1 with | EConst(Ident n) -> expr_next (EMeta((Meta.from_string n,[],snd e1),eparam), punion p1 p2) s | _ -> die "" __LOC__) - | [< '(Dot,p); e = parse_field e1 p >] -> e + | [< '(Dot,p); e = parse_field e1 EFNormal p >] -> e + | [< '(QuestionDot,p); e = parse_field e1 EFSafe p >] -> e | [< '(POpen,p1); e = parse_call_params (fun el p2 -> (ECall(e1,el)),punion (pos e1) p2) p1; s >] -> expr_next e s | [< '(BkOpen,p1); e2 = secure_expr; s >] -> let p2 = expect_unless_resume_p BkClose s in let e2 = check_signature_mark e2 p1 p2 in expr_next (EArray (e1,e2), punion (pos e1) p2) s | [< '(Arrow,pa); s >] -> - let er = expr s in + let er = secure_expr s in arrow_function (snd e1) [arrow_first_param e1 s] er s | [< '(Binop OpGt,p1); s >] -> (match s with parser @@ -1510,20 +1609,20 @@ and expr_next' e1 = parser expr_next e_is s | [< >] -> e1 -and parse_field e1 p s = +and parse_field e1 efk p s = check_resume p (fun () -> (EDisplay (e1,DKDot),p)) (fun () -> begin match s with parser - | [< '(Kwd Macro,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"macro") , punion (pos e1) p2) s - | [< '(Kwd Extern,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"extern") , punion (pos e1) p2) s - | [< '(Kwd Function,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"function") , punion (pos e1) p2) s - | [< '(Kwd New,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"new") , punion (pos e1) p2) s - | [< '(Kwd k,p2) when !parsing_macro_cond && p.pmax = p2.pmin; s >] -> expr_next (EField (e1,s_keyword k) , punion (pos e1) p2) s - | [< '(Const (Ident f),p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,f) , punion (pos e1) p2) s - | [< '(Dollar v,p2); s >] -> expr_next (EField (e1,"$"^v) , punion (pos e1) p2) s + | [< '(Kwd Macro,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"macro",efk) , punion (pos e1) p2) s + | [< '(Kwd Extern,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"extern",efk) , punion (pos e1) p2) s + | [< '(Kwd Function,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"function",efk) , punion (pos e1) p2) s + | [< '(Kwd New,p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,"new",efk) , punion (pos e1) p2) s + | [< '(Kwd k,p2) when !parsing_macro_cond && p.pmax = p2.pmin; s >] -> expr_next (EField (e1,s_keyword k,efk) , punion (pos e1) p2) s + | [< '(Const (Ident f),p2) when p.pmax = p2.pmin; s >] -> expr_next (EField (e1,f,efk) , punion (pos e1) p2) s + | [< '(Dollar v,p2); s >] -> expr_next (EField (e1,"$"^v,efk) , punion (pos e1) p2) s | [< >] -> (* turn an integer followed by a dot into a float *) match e1 with - | (EConst (Int v),p2) when p2.pmax = p.pmin -> expr_next (EConst (Float (v ^ ".")),punion p p2) s + | (EConst (Int (v, None)),p2) when p2.pmax = p.pmin -> expr_next (EConst (Float (v ^ ".", None)),punion p p2) s | _ -> serror() end ) @@ -1534,7 +1633,14 @@ and parse_guard = parser and expr_or_var = parser | [< '(Kwd Var,p1); np = dollar_ident; >] -> EVars [mk_evar np],punion p1 (snd np) - | [< '(Kwd Final,p1); np = dollar_ident; >] -> EVars [mk_evar ~final:true np],punion p1 (snd np) + | [< '(Kwd Final,p1); s >] -> + check_redundant_var p1 s; + begin match s with parser + | [< np = dollar_ident; >] -> + EVars [mk_evar ~final:true np],punion p1 (snd np) + | [< >] -> + serror() + end | [< e = secure_expr >] -> e and parse_switch_cases eswitch cases = parser @@ -1576,7 +1682,7 @@ and parse_catches etry catches pmax = parser and parse_call_params f p1 s = if not !in_display_file then begin - let el = psep Comma expr s in + let el = psep_trailing Comma expr s in match s with parser | [< '(PClose,p2) >] -> f el p2 | [< >] -> @@ -1599,6 +1705,9 @@ and parse_call_params f p1 s = | [< '(PClose,p2) >] -> let e = check_signature_mark e p1 p2 in f (List.rev (e :: acc)) p2 + | [< '(Comma,p2); '(PClose,p3) >] -> + let e = check_signature_mark e p1 p3 in + f (List.rev (e :: acc)) p3 | [< '(Comma,p2) >] -> let e = check_signature_mark e p1 p2 in parse_next_param (e :: acc) p2 @@ -1633,13 +1742,13 @@ and secure_expr = parser let rec validate_macro_cond s e = match fst e with | EConst (Ident _) | EConst (String _) - | EConst (Int _) - | EConst (Float _) + | EConst (Int (_, _)) + | EConst (Float (_, _)) -> e | EUnop (op,p,e1) -> (EUnop (op, p, validate_macro_cond s e1), snd e) | EBinop (op,e1,e2) -> (EBinop(op, (validate_macro_cond s e1), (validate_macro_cond s e2)), snd e) | EParenthesis (e1) -> (EParenthesis (validate_macro_cond s e1), snd e) - | EField(e1,name) -> (EField(validate_macro_cond s e1,name), snd e) + | EField(e1,name,efk) -> (EField(validate_macro_cond s e1,name,efk), snd e) | ECall ((EConst (Ident _),_) as i, args) -> (ECall (i,List.map (validate_macro_cond s) args),snd e) | _ -> syntax_error (Custom ("Invalid conditional expression")) ~pos:(Some (pos e)) s ((EConst (Ident "false"),(pos e))) @@ -1656,10 +1765,10 @@ let rec parse_macro_cond s = parse_macro_ident t p s | [< '(Const (String(s,qs)),p) >] -> None, (EConst (String(s,qs)),p) - | [< '(Const (Int i),p) >] -> - None, (EConst (Int i),p) - | [< '(Const (Float f),p) >] -> - None, (EConst (Float f),p) + | [< '(Const (Int (i, s)),p) >] -> + None, (EConst (Int (i, s)),p) + | [< '(Const (Float (f, s)),p) >] -> + None, (EConst (Float (f, s)),p) | [< '(Kwd k,p) >] -> parse_macro_ident (s_keyword k) p s | [< '(Unop op,p); tk, e = parse_macro_cond >] -> @@ -1670,4 +1779,4 @@ let rec parse_macro_cond s = cond with e -> parsing_macro_cond := false; - raise e \ No newline at end of file + raise e diff --git a/src/syntax/lexer.ml b/src/syntax/lexer.ml index 6a56ecc1..abcfe854 100644 --- a/src/syntax/lexer.ml +++ b/src/syntax/lexer.ml @@ -73,6 +73,18 @@ let make_file file = llastindex = 0; } +let copy_file source dest = + dest.lline <- source.lline; + dest.lmaxline <- source.lmaxline; + dest.llines <- source.llines; + dest.lalines <- source.lalines; + dest.llast <- source.llast; + dest.llastindex <- source.llastindex + +let print_file file = + let sllines = String.concat ";" (List.map (fun (i1,i2) -> Printf.sprintf "(%i,%i)" i1 i2) file.llines) in + let slalines = String.concat ";" (Array.to_list (Array.map (fun (i1,i2) -> Printf.sprintf "(%i,%i)" i1 i2) file.lalines)) in + Printf.sprintf "lfile: %s\nlline: %i\nlmaxline: %i\nllines: [%s]\nlalines: [%s]\nllast: %i\nllastindex: %i" file.lfile file.lline file.lmaxline sllines slalines file.llast file.llastindex let cur = ref (make_file "") @@ -111,6 +123,40 @@ let is_valid_identifier s = with Exit -> false +let split_suffix s is_int = + let len = String.length s in + let rec loop i pivot = + if i = len then begin + match pivot with + | None -> + (s,None) + | Some pivot -> + (* There might be a _ at the end of the literal because we allow _f64 and such *) + let literal_length = if String.unsafe_get s (pivot - 1) = '_' then pivot - 1 else pivot in + let literal = String.sub s 0 literal_length in + let suffix = String.sub s pivot (len - pivot) in + (literal, Some suffix) + end else begin + let c = String.unsafe_get s i in + match c with + | 'i' | 'u' -> + loop (i + 1) (Some i) + | 'f' when not is_int -> + loop (i + 1) (Some i) + | _ -> + loop (i + 1) pivot + end + in + loop 0 None + +let split_int_suffix s = + let (literal,suffix) = split_suffix s true in + Const (Int (literal,suffix)) + +let split_float_suffix s = + let (literal,suffix) = split_suffix s false in + Const (Float (literal,suffix)) + let init file = let f = make_file file in cur := f; @@ -119,6 +165,15 @@ let init file = let save() = !cur +let reinit file = + let old_file = try Some (Hashtbl.find all_files file) with Not_found -> None in + let old_cur = !cur in + init file; + (fun () -> + cur := old_cur; + Option.may (Hashtbl.replace all_files file) old_file; + ) + let restore c = cur := c @@ -299,7 +354,17 @@ let string_is_whitespace s = let idtype = [%sedlex.regexp? Star '_', 'A'..'Z', Star ('_' | 'a'..'z' | 'A'..'Z' | '0'..'9')] -let integer = [%sedlex.regexp? ('1'..'9', Star ('0'..'9')) | '0'] +let digit = [%sedlex.regexp? '0'..'9'] +let sep_digit = [%sedlex.regexp? Opt '_', digit] +let integer_digits = [%sedlex.regexp? (digit, Star sep_digit)] +let hex_digit = [%sedlex.regexp? '0'..'9'|'a'..'f'|'A'..'F'] +let sep_hex_digit = [%sedlex.regexp? Opt '_', hex_digit] +let hex_digits = [%sedlex.regexp? (hex_digit, Star sep_hex_digit)] +let integer = [%sedlex.regexp? ('1'..'9', Star sep_digit) | '0'] + +let integer_suffix = [%sedlex.regexp? Opt '_', ('i'|'u'), Plus integer] + +let float_suffix = [%sedlex.regexp? Opt '_', 'f', Plus integer] (* https://www.w3.org/TR/xml/#sec-common-syn plus '$' for JSX *) let xml_name_start_char = [%sedlex.regexp? '$' | ':' | 'A'..'Z' | '_' | 'a'..'z' | 0xC0 .. 0xD6 | 0xD8 .. 0xF6 | 0xF8 .. 0x2FF | 0x370 .. 0x37D | 0x37F .. 0x1FFF | 0x200C .. 0x200D | 0x2070 .. 0x218F | 0x2C00 .. 0x2FEF | 0x3001 .. 0xD7FF | 0xF900 .. 0xFDCF | 0xFDF0 .. 0xFFFD | 0x10000 .. 0xEFFFF] @@ -319,12 +384,16 @@ let rec token lexbuf = | Plus (Chars " \t") -> token lexbuf | "\r\n" -> newline lexbuf; token lexbuf | '\n' | '\r' -> newline lexbuf; token lexbuf - | "0x", Plus ('0'..'9'|'a'..'f'|'A'..'F') -> mk lexbuf (Const (Int (lexeme lexbuf))) - | integer -> mk lexbuf (Const (Int (lexeme lexbuf))) - | integer, '.', Plus '0'..'9' -> mk lexbuf (Const (Float (lexeme lexbuf))) - | '.', Plus '0'..'9' -> mk lexbuf (Const (Float (lexeme lexbuf))) - | integer, ('e'|'E'), Opt ('+'|'-'), Plus '0'..'9' -> mk lexbuf (Const (Float (lexeme lexbuf))) - | integer, '.', Star '0'..'9', ('e'|'E'), Opt ('+'|'-'), Plus '0'..'9' -> mk lexbuf (Const (Float (lexeme lexbuf))) + | "0x", Plus hex_digits, Opt integer_suffix -> + mk lexbuf (split_int_suffix (lexeme lexbuf)) + | integer, Opt integer_suffix -> + mk lexbuf (split_int_suffix (lexeme lexbuf)) + | integer, float_suffix -> + mk lexbuf (split_float_suffix (lexeme lexbuf)) + | integer, '.', Plus integer_digits, Opt float_suffix -> mk lexbuf (split_float_suffix (lexeme lexbuf)) + | '.', Plus integer_digits, Opt float_suffix -> mk lexbuf (split_float_suffix (lexeme lexbuf)) + | integer, ('e'|'E'), Opt ('+'|'-'), Plus integer_digits, Opt float_suffix -> mk lexbuf (split_float_suffix (lexeme lexbuf)) + | integer, '.', Star digit, ('e'|'E'), Opt ('+'|'-'), Plus integer_digits, Opt float_suffix -> mk lexbuf (split_float_suffix (lexeme lexbuf)) | integer, "..." -> let s = lexeme lexbuf in mk lexbuf (IntInterval (String.sub s 0 (String.length s - 3))) @@ -345,6 +414,7 @@ let rec token lexbuf = | "<<=" -> mk lexbuf (Binop (OpAssignOp OpShl)) | "||=" -> mk lexbuf (Binop (OpAssignOp OpBoolOr)) | "&&=" -> mk lexbuf (Binop (OpAssignOp OpBoolAnd)) + | "??=" -> mk lexbuf (Binop (OpAssignOp OpNullCoal)) (*//| ">>=" -> mk lexbuf (Binop (OpAssignOp OpShr)) *) (*//| ">>>=" -> mk lexbuf (Binop (OpAssignOp OpUShr)) *) | "==" -> mk lexbuf (Binop OpEq) @@ -364,6 +434,7 @@ let rec token lexbuf = | ":" -> mk lexbuf DblDot | "," -> mk lexbuf Comma | "." -> mk lexbuf Dot + | "?." -> mk lexbuf QuestionDot | "%" -> mk lexbuf (Binop OpMod) | "&" -> mk lexbuf (Binop OpAnd) | "|" -> mk lexbuf (Binop OpOr) @@ -379,6 +450,7 @@ let rec token lexbuf = | "}" -> mk lexbuf BrClose | "(" -> mk lexbuf POpen | ")" -> mk lexbuf PClose + | "??" -> mk lexbuf (Binop OpNullCoal) | "?" -> mk lexbuf Question | "@" -> mk lexbuf At @@ -636,4 +708,4 @@ let lex_xml p lexbuf = try not_xml ctx 0 (name <> "") (* don't allow self-closing fragments *) with Exit -> - error Unterminated_markup p \ No newline at end of file + error Unterminated_markup p diff --git a/src/syntax/parser.ml b/src/syntax/parser.ml index 7942c232..d0686b28 100644 --- a/src/syntax/parser.ml +++ b/src/syntax/parser.ml @@ -67,6 +67,7 @@ exception TypePath of string list * (string * bool) option * bool (* in import * exception SyntaxCompletion of syntax_completion * DisplayTypes.completion_subject let error_msg = function + | Unexpected (Kwd k) -> "Unexpected keyword \""^(s_keyword k)^"\"" | Unexpected t -> "Unexpected "^(s_token t) | Duplicate_default -> "Duplicate default" | Missing_semicolon -> "Missing ;" @@ -191,7 +192,7 @@ let get_doc s = | None -> None | Some (d,pos) -> last_doc := None; - if pos = p.pmin then Some d else None + Some d let unsupported_decl_flag decl flag pos = let msg = (s_decl_flag flag) ^ " modifier is not supported for " ^ decl in @@ -270,11 +271,11 @@ let precedence op = | OpInterval -> 7, left | OpBoolAnd -> 8, left | OpBoolOr -> 9, left - | OpArrow -> 10, right + | OpArrow | OpNullCoal -> 10, right | OpAssign | OpAssignOp _ -> 11, right -let is_not_assign = function - | OpAssign | OpAssignOp _ -> false +let is_higher_than_ternary = function + | OpAssign | OpAssignOp _ | OpArrow -> false | _ -> true let swap op1 op2 = @@ -287,7 +288,7 @@ let rec make_binop op e ((v,p2) as e2) = | EBinop (_op,_e,_e2) when swap op _op -> let _e = make_binop op e _e in EBinop (_op,_e,_e2) , punion (pos _e) (pos _e2) - | ETernary (e1,e2,e3) when is_not_assign op -> + | ETernary (e1,e2,e3) when is_higher_than_ternary op -> let e = make_binop op e e1 in ETernary (e,e2,e3) , punion (pos e) (pos e3) | _ -> @@ -301,8 +302,8 @@ let rec make_unop op ((v,p2) as e) p1 = | EBinop (bop,e,e2) -> EBinop (bop, make_unop op e p1 , e2) , (punion p1 p2) | ETernary (e1,e2,e3) -> ETernary (make_unop op e1 p1 , e2, e3), punion p1 p2 | EIs (e, t) -> EIs (make_unop op e p1, t), punion p1 p2 - | EConst (Int i) when op = Neg -> EConst (Int (neg i)),punion p1 p2 - | EConst (Float j) when op = Neg -> EConst (Float (neg j)),punion p1 p2 + | EConst (Int (i, suffix)) when op = Neg -> EConst (Int (neg i, suffix)),punion p1 p2 + | EConst (Float (j, suffix)) when op = Neg -> EConst (Float (neg j, suffix)),punion p1 p2 | _ -> EUnop (op,Prefix,e), punion p1 p2 let rec make_meta name params ((v,p2) as e) p1 = @@ -398,7 +399,7 @@ let check_type_decl_completion mode pmax s = if pmax <= p.pmin && pmin >= p.pmax then begin let so,p = match Stream.peek s with | Some((Const(Ident name),p)) when display_position#enclosed_in p -> (Some name),p - | Some(e,p) -> print_endline (s_token e); None,p + | Some(e,p) -> None,p | _ -> None,p in delay_syntax_completion (SCTypeDecl mode) so p @@ -419,4 +420,9 @@ let check_signature_mark e p1 p2 = end let convert_abstract_flags flags = - ExtList.List.filter_map decl_flag_to_abstract_flag flags \ No newline at end of file + ExtList.List.filter_map decl_flag_to_abstract_flag flags + +let no_keyword what s = + match Stream.peek s with + | Some (Kwd kwd,p) -> error (Custom ("Keyword " ^ (s_keyword kwd) ^ " cannot be used as " ^ what)) p + | _ -> raise Stream.Failure diff --git a/src/syntax/parserEntry.ml b/src/syntax/parserEntry.ml index e3188616..ebbc0d8a 100644 --- a/src/syntax/parserEntry.ml +++ b/src/syntax/parserEntry.ml @@ -72,8 +72,8 @@ let rec eval ctx (e,p) = | EConst (Ident i) -> (try TString (Define.raw_defined_value ctx i) with Not_found -> TNull) | EConst (String(s,_)) -> TString s - | EConst (Int i) -> TFloat (float_of_string i) - | EConst (Float f) -> TFloat (float_of_string f) + | EConst (Int (i, _)) -> TFloat (float_of_string i) + | EConst (Float (f, _)) -> TFloat (float_of_string f) | ECall ((EConst (Ident "version"),_),[(EConst (String(s,_)), p)]) -> parse_version s p | EBinop (OpBoolAnd, e1, e2) -> TBool (is_true (eval ctx e1) && is_true (eval ctx e2)) | EBinop (OpBoolOr, e1, e2) -> TBool (is_true (eval ctx e1) || is_true(eval ctx e2)) @@ -87,7 +87,7 @@ let rec eval ctx (e,p) = in (match op with | OpEq -> compare (=) - | OpNotEq -> compare (<>) + | OpNotEq -> TBool (not (is_true (compare (=)))) | OpGt -> compare (>) | OpGte -> compare (>=) | OpLt -> compare (<) @@ -243,16 +243,17 @@ let parse entry ctx code file = match fst tk with | Comment s -> (* if encloses_resume (pos tk) then syntax_completion SCComment (pos tk); *) - let tk = next_token() in let l = String.length s in if l > 0 && s.[0] = '*' then last_doc := Some (String.sub s 1 (l - (if l > 1 && s.[l-1] = '*' then 2 else 1)), (snd tk).pmin); + let tk = next_token() in tk | CommentLine s -> if !in_display_file then begin let p = pos tk in (* Completion at the / should not pick up the comment (issue #9133) *) let p = if is_completion() then {p with pmin = p.pmin + 1} else p in - if display_position#enclosed_in p then syntax_completion SCComment None (pos tk); + (* The > 0 check is to deal with the special case of line comments at the beginning of the file (issue #10322) *) + if display_position#enclosed_in p && p.pmin > 0 then syntax_completion SCComment None (pos tk); end; next_token() | Sharp "end" -> @@ -289,7 +290,7 @@ let parse entry ctx code file = | _ -> error Unimplemented (snd tk)) | Sharp "line" -> let line = (match next_token() with - | (Const (Int s),p) -> (try int_of_string s with _ -> error (Custom ("Could not parse ridiculous line number " ^ s)) p) + | (Const (Int (s, _)),p) -> (try int_of_string s with _ -> error (Custom ("Could not parse ridiculous line number " ^ s)) p) | (t,p) -> error (Unexpected t) p ) in !(Lexer.cur).Lexer.lline <- line - 1; @@ -387,6 +388,13 @@ let parse entry ctx code file = let parse_string entry com s p error inlined = let old = Lexer.save() in let old_file = (try Some (Hashtbl.find Lexer.all_files p.pfile) with Not_found -> None) in + let restore_file_data = + let f = Lexer.make_file old.lfile in + Lexer.copy_file old f; + (fun () -> + Lexer.copy_file f old + ) + in let old_display = display_position#get in let old_in_display_file = !in_display_file in let old_syntax_errors = !syntax_errors in @@ -400,7 +408,10 @@ let parse_string entry com s p error inlined = in_display_file := old_in_display_file; end; syntax_errors := old_syntax_errors; - Lexer.restore old + Lexer.restore old; + (* String parsing might mutate lexer_file information, e.g. from newline() calls. Here we + restore the actual file data (issue #10763). *) + restore_file_data() in if inlined then Lexer.init p.pfile diff --git a/src/syntax/reification.ml b/src/syntax/reification.ml index af283b38..963c9133 100644 --- a/src/syntax/reification.ml +++ b/src/syntax/reification.ml @@ -30,14 +30,17 @@ let reify in_macro = | _ -> (ECall (constr,vl),pmin) in let to_const c p = - let cst n v = mk_enum "Constant" n [EConst (String(v,SDoubleQuotes)),p] p in match c with - | Int i -> cst "CInt" i + | Int (i, suffix) -> + let suffix = match suffix with None -> (EConst (Ident "null"),p) | Some s -> (EConst (String (s, SDoubleQuotes)),p) in + mk_enum "Constant" "CInt" [(EConst (String(i,SDoubleQuotes)),p);suffix] p | String(s,qs) -> let qs = mk_enum "StringLiteralKind" (match qs with SDoubleQuotes -> "DoubleQuotes" | SSingleQuotes -> "SingleQuotes") [] p in mk_enum "Constant" "CString" [(EConst (String(s,SDoubleQuotes)),p);qs] p - | Float s -> cst "CFloat" s - | Ident s -> cst "CIdent" s + | Float (f, suffix) -> + let suffix = match suffix with None -> (EConst (Ident "null"),p) | Some s -> (EConst (String (s, SDoubleQuotes)),p) in + mk_enum "Constant" "CFloat" [(EConst (String(f,SDoubleQuotes)),p);suffix] p + | Ident s -> mk_enum "Constant" "CIdent" [EConst (String(s,SDoubleQuotes)),p] p | Regexp (r,o) -> mk_enum "Constant" "CRegexp" [(EConst (String(r,SDoubleQuotes)),p);(EConst (String(o,SDoubleQuotes)),p)] p in let rec to_binop o p = @@ -67,6 +70,7 @@ let reify in_macro = | OpInterval -> op "OpInterval" | OpArrow -> op "OpArrow" | OpIn -> op "OpIn" + | OpNullCoal -> op "OpNullCoal" in let to_string s p = let len = String.length s in @@ -113,9 +117,9 @@ let reify in_macro = type parameters. *) let ea = to_array to_tparam t.tparams p in let fields = [ - ("pack", (EField(ei,"pack"),p)); - ("name", (EField(ei,"name"),p)); - ("sub", (EField(ei,"sub"),p)); + ("pack", (efield(ei,"pack"),p)); + ("name", (efield(ei,"name"),p)); + ("sub", (efield(ei,"sub"),p)); ("params", ea); ] in to_obj fields p @@ -220,8 +224,8 @@ let reify in_macro = p | None -> let file = (EConst (String(p.pfile,SDoubleQuotes)),p) in - let pmin = (EConst (Int (string_of_int p.pmin)),p) in - let pmax = (EConst (Int (string_of_int p.pmax)),p) in + let pmin = (EConst (Int ((string_of_int p.pmin), None)),p) in + let pmax = (EConst (Int ((string_of_int p.pmax), None)),p) in if in_macro then (EUntyped (ECall ((EConst (Ident "$__mk_pos__"),p),[file;pmin;pmax]),p),p) else @@ -230,7 +234,7 @@ let reify in_macro = match !cur_pos with | Some p -> p | None when in_macro -> to_pos p - | None -> (ECall ((EField ((EField ((EField ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makePosition"),p),[to_pos p]),p) + | None -> (ECall ((efield ((efield ((efield ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makePosition"),p),[to_pos p]),p) and to_expr_array a p = match a with | [EMeta ((Meta.Dollar "a",[],_),e1),_] -> (match fst e1 with EArrayDecl el -> to_expr_array el p | _ -> e1) | _ -> to_array to_expr a p @@ -250,9 +254,13 @@ let reify in_macro = expr "EArray" [loop e1;loop e2] | EBinop (op,e1,e2) -> expr "EBinop" [to_binop op p; loop e1; loop e2] - | EField (e,s) -> - let p = {p with pmin = p.pmax - String.length s} in - expr "EField" [loop e; to_string s p] + | EField (e,s,efk) -> + let p = patch_string_pos p s in + let efk = match efk with + | EFNormal -> "Normal" + | EFSafe -> "Safe" + in + expr "EField" [loop e; to_string s p; mk_enum "EFieldKind" efk [] p] | EParenthesis e -> expr "EParenthesis" [loop e] | EObjectDecl fl -> @@ -287,6 +295,7 @@ let reify in_macro = "type", to_opt to_type_hint v.ev_type p; "expr", to_opt to_expr v.ev_expr p; "isFinal",to_bool v.ev_final p; + "isStatic",to_bool v.ev_static p; "meta",to_meta v.ev_meta p; ] in to_obj fields p @@ -333,8 +342,6 @@ let reify in_macro = expr "EIs" [loop e; to_type_hint ct p] | EDisplay (e,dk) -> expr "EDisplay" [loop e; to_display_kind dk p] - | EDisplayNew t -> - expr "EDisplayNew" [to_tpath t p] | ETernary (e1,e2,e3) -> expr "ETernary" [loop e1;loop e2;loop e3] | ECheckType (e1,ct) -> @@ -353,12 +360,15 @@ let reify in_macro = | EParenthesis (ECheckType (e2, (CTPath{tname="String";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CString" [e2] (pos e2)] | EParenthesis (ECheckType (e2, (CTPath{tname="Int";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CInt" [e2] (pos e2)] | EParenthesis (ECheckType (e2, (CTPath{tname="Float";tpackage=[]},_)),_) -> expr "EConst" [mk_enum "Constant" "CFloat" [e2] (pos e2)] - | _ -> (ECall ((EField ((EField ((EField ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makeExpr"),p),[e; to_enc_pos (pos e)]),p) + | EConst (Int (s, Some "i64")) -> + expr "EConst" [mk_enum "Constant" "CInt" [ (EConst(String (s, SDoubleQuotes)),(pos e1)); (EConst(String ("i64", SDoubleQuotes)),(pos e1)) ] (pos e1)] + | _ -> + (ECall ((efield ((efield ((efield ((EConst (Ident "haxe"),p),"macro"),p),"Context"),p),"makeExpr"),p),[e1; to_enc_pos (pos e1)]),p) end | Meta.Dollar "i", _ -> expr "EConst" [mk_enum "Constant" "CIdent" [e1] (pos e1)] | Meta.Dollar "p", _ -> - (ECall ((EField ((EField ((EField ((EConst (Ident "haxe"),p),"macro"),p),"MacroStringTools"),p),"toFieldExpr"),p),[e]),p) + (ECall ((efield ((efield ((efield ((EConst (Ident "haxe"),p),"macro"),p),"MacroStringTools"),p),"toFieldExpr"),p),[e1]),p) | Meta.Pos, [pexpr] -> let old = !cur_pos in cur_pos := Some pexpr; @@ -372,7 +382,8 @@ let reify in_macro = "name", to_placed_name t.tp_name; "params", (EArrayDecl (List.map (to_tparam_decl p) t.tp_params),p); "meta", to_meta t.tp_meta p; - "constraints", (EArrayDecl (match t.tp_constraints with None -> [] | Some th -> [to_ctype th p]),p) + "constraints", (EArrayDecl (match t.tp_constraints with None -> [] | Some th -> [to_ctype th p]),p); + "defaultType", to_opt to_ctype t.tp_default p; ] p and to_type_def (t,p) = match t with diff --git a/src/typing/callUnification.ml b/src/typing/callUnification.ml index 9d71697c..b9ca1291 100644 --- a/src/typing/callUnification.ml +++ b/src/typing/callUnification.ml @@ -1,6 +1,7 @@ open Globals open Ast open Type +open Common open Typecore open Error open FieldAccess @@ -12,54 +13,18 @@ let is_forced_inline c cf = | _ when has_class_field_flag cf CfExtern -> true | _ -> false -let relative_path ctx file = - let slashes path = String.concat "/" (ExtString.String.nsplit path "\\") in - let fpath = slashes (Path.get_full_path file) in - let fpath_lower = String.lowercase fpath in - let flen = String.length fpath_lower in - let rec loop = function - | [] -> file - | path :: l -> - let spath = String.lowercase (slashes path) in - let slen = String.length spath in - if slen > 0 && slen < flen && String.sub fpath_lower 0 slen = spath then String.sub fpath slen (flen - slen) else loop l - in - loop ctx.com.Common.class_path - -let mk_infos ctx p params = - let file = if ctx.in_macro then p.pfile else if Common.defined ctx.com Define.AbsolutePath then Path.get_full_path p.pfile else relative_path ctx p.pfile in - (EObjectDecl ( - (("fileName",null_pos,NoQuotes) , (EConst (String(file,SDoubleQuotes)) , p)) :: - (("lineNumber",null_pos,NoQuotes) , (EConst (Int (string_of_int (Lexer.get_error_line p))),p)) :: - (("className",null_pos,NoQuotes) , (EConst (String (s_type_path ctx.curclass.cl_path,SDoubleQuotes)),p)) :: - if ctx.curfield.cf_name = "" then - params - else - (("methodName",null_pos,NoQuotes), (EConst (String (ctx.curfield.cf_name,SDoubleQuotes)),p)) :: params - ) ,p) - -let rec is_pos_infos = function - | TMono r -> - (match r.tm_type with - | Some t -> is_pos_infos t - | _ -> false) - | TLazy f -> - is_pos_infos (lazy_type f) - | TType ({ t_path = ["haxe"] , "PosInfos" },[]) -> - true - | TType (t,tl) -> - is_pos_infos (apply_params t.t_params tl t.t_type) - | TAbstract({a_path=[],"Null"},[t]) -> - is_pos_infos t - | _ -> - false - let rec unify_call_args ctx el args r callp inline force_inline in_overload = let call_error err p = - raise (Error (Call_error err,p)) + raise (Error (Call_error err,p,0)) in let arg_error ul name opt p = - let err = Stack (ul,Custom ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'")) in + let msg = ("For " ^ (if opt then "optional " else "") ^ "function argument '" ^ name ^ "'") in + let err = match ul with + | Stack s -> Stack (s @ [(Custom msg,p)]) + | Unify l -> Unify (l @ [(Unify_custom msg)]) + | Custom parent -> Custom (parent ^ "\n" ^ msg) + | _ -> Stack [(ul,p); (Custom (compl_msg msg), p)] + in call_error (Could_not_unify err) p in let mk_pos_infos t = @@ -83,8 +48,8 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = let handle_errors fn = try fn() - with Error(l,p) when (match l with Call_error _ | Module_not_found _ -> false | _ -> true) -> - raise (WithTypeError (l,p)) + with Error(l,p,nl) when (match l with Call_error _ | Module_not_found _ -> false | _ -> true) -> + raise (WithTypeError (l,p,nl)) in (* let force_inline, is_extern = match cf with Some(TInst(c,_),f) -> is_forced_inline (Some c) f, (has_class_flag c CExtern) | _ -> false, false in *) let type_against name t e = @@ -102,7 +67,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = [] | _,[name,false,TAbstract({ a_path = ["cpp"],"Rest" },[t])] -> (try List.map (fun e -> type_against name t e) el - with WithTypeError(ul,p) -> arg_error ul name false p) + with WithTypeError(ul,p,_) -> arg_error ul name false p) | _,[name,false,t] when ExtType.is_rest (follow t) -> begin match follow t with | TAbstract({a_path=(["haxe"],"Rest")},[arg_t]) -> @@ -121,28 +86,28 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = match el with | [(EUnop (Spread,Prefix,e),p)] -> (try [mk (TUnop (Spread, Prefix, type_against name t e)) t p] - with WithTypeError(ul,p) -> arg_error ul name false p) + with WithTypeError(ul,p,_) -> arg_error ul name false p) | _ when ExtType.is_mono (follow arg_t) -> (try let el = type_rest mk_mono in - unify ctx (unify_min ctx el) arg_t (punion_el (List.map (fun e -> ((),e.epos)) el)); + unify ctx (unify_min ctx el) arg_t (punion_el callp el); el - with WithTypeError(ul,p) -> + with WithTypeError(ul,p,_) -> arg_error ul name false p) | _ -> (try type_rest (fun() -> arg_t) - with WithTypeError(ul,p) -> + with WithTypeError(ul,p,_) -> arg_error ul name false p) (* for other platforms make sure rest arguments are wrapped in an array *) else begin match el with | [(EUnop (Spread,Prefix,e),p)] -> (try [type_against name t e] - with WithTypeError(ul,p) -> arg_error ul name false p) + with WithTypeError(ul,p,_) -> arg_error ul name false p) | [] -> (try [type_against name t (EArrayDecl [],callp)] - with WithTypeError(ul,p) -> arg_error ul name false p) + with WithTypeError(ul,p,_) -> arg_error ul name false p) | (_,p1) :: _ -> let p = List.fold_left (fun p (e1,p2) -> @@ -162,7 +127,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = do_type (ECheckType(e,(CTPath tp, p)),p) (* ([arg1, arg2...]:Array) *) end else do_type e - with WithTypeError(ul,p) -> + with WithTypeError(ul,p,_) -> arg_error ul name false p ) end @@ -172,7 +137,10 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = | [],(_,false,_) :: _ -> call_error (Not_enough_arguments args) callp | [],(name,true,t) :: args -> - begin match loop [] args with + if not ctx.allow_transform then begin + ignore(loop [] args); + [] + end else begin match loop [] args with | [] when not (inline && (ctx.g.doinline || force_inline)) && not ctx.com.config.pf_pad_nulls -> if is_pos_infos t then [mk_pos_infos t] else [] @@ -183,7 +151,7 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = | (e,p) :: el, [] -> begin match List.rev !skipped with | [] -> - if ctx.is_display_file && not (Diagnostics.is_diagnostics_run ctx.com p) then begin + if ctx.is_display_file && not (Diagnostics.error_in_diagnostics_run ctx.com p) then begin ignore(type_expr ctx (e,p) WithType.value); ignore(loop el []) end; @@ -191,12 +159,13 @@ let rec unify_call_args ctx el args r callp inline force_inline in_overload = | (s,ul,p) :: _ -> arg_error ul s true p end | e :: el,(name,opt,t) :: args -> + let might_skip = List.length el < List.length args in begin try let e = type_against name t e in e :: loop el args with - WithTypeError (ul,p)-> - if opt && List.length el < List.length args then + WithTypeError (ul,p,_)-> + if opt && might_skip then let e_def = skip name ul t p in e_def :: loop (e :: el) args else @@ -236,15 +205,15 @@ let unify_typed_args ctx tmap args el_typed call_pos = match args,el with | [], _ :: _ -> let call_error = Call_error(Too_many_arguments) in - raise(Error(call_error,call_pos)) + raise(Error(call_error,call_pos,0)) | _, [] -> List.rev acc_args,args | ((_,opt,t0) as arg) :: args,e :: el -> begin try - unify_raise ctx (tmap e.etype) t0 e.epos; - with Error(Unify _ as msg,p) -> + unify_raise (tmap e.etype) t0 e.epos; + with Error(Unify _ as msg,p,nl) -> let call_error = Call_error(Could_not_unify msg) in - raise(Error(call_error,p)) + raise(Error(call_error,p,nl)) end; loop (arg :: acc_args) (fun t -> t) args el in @@ -298,7 +267,7 @@ let unify_field_call ctx fa el_typed el p inline = | Some s -> new Javadoc.javadoc s in match de with - | DisplayHover (Some hover) -> + | DisplayHover hover -> begin match hover.hexpected with | Some (WithType(t,Some si)) -> let si = match si with @@ -344,16 +313,16 @@ let unify_field_call ctx fa el_typed el p inline = in make_field_call_candidate el ret monos tf cf (mk_call,extract_delayed_display()) | t -> - error (s_type (print_context()) t ^ " cannot be called") p + typing_error (s_type (print_context()) t ^ " cannot be called") p in let maybe_raise_unknown_ident cerr p = - let rec loop err = + let rec loop err p = match err with - | Unknown_ident _ -> error (error_msg err) p - | Stack (e1,e2) -> (loop e1; loop e2) + | Unknown_ident _ -> located_typing_error (error_msg p err) + | Stack stack -> List.iter (fun (e,p) -> loop e p) stack | _ -> () in - match cerr with Could_not_unify err -> loop err | _ -> () + match cerr with Could_not_unify err -> loop err p | _ -> () in let attempt_calls candidates = let rec loop candidates = match candidates with @@ -371,7 +340,7 @@ let unify_field_call ctx fa el_typed el p inline = candidate :: candidates,failures end else [candidate],[] - with Error ((Call_error cerr as err),p) -> + with Error ((Call_error cerr as err),p,_) -> List.iter (fun (m,t,constr) -> if t != m.tm_type then m.tm_type <- t; if constr != m.tm_down_constraints then m.tm_down_constraints <- constr; @@ -406,7 +375,7 @@ let unify_field_call ctx fa el_typed el p inline = if overload_kind = OverloadProper then maybe_check_access cf; begin try commit_delayed_display (attempt_call cf false) - with Error _ when ctx.com.display.dms_error_policy = EPIgnore -> + with Error _ when Common.ignore_error ctx.com -> fail_fun(); end | _ -> @@ -417,19 +386,19 @@ let unify_field_call ctx fa el_typed el p inline = Option.may (fun de -> raise_augmented_display_exception cf de; ) delayed_display; - cf,error_msg err,p + cf,error_msg p err ) failures in - let failures = remove_duplicates (fun (_,msg1,_) (_,msg2,_) -> msg1 <> msg2) failures in + let failures = remove_duplicates (fun (_,msg1) (_,msg2) -> msg1 <> msg2) failures in begin match failures with - | [_,msg,p] -> - error msg p + | [_,msg] -> + located_typing_error msg | _ -> - display_error ctx "Could not find a suitable overload, reasons follow" p; - List.iter (fun (cf,msg,p2) -> - display_error ctx ("Overload resolution failed for " ^ (s_type (print_context()) cf.cf_type)) p; - display_error ctx msg p2; + display_error ctx.com "Could not find a suitable overload, reasons follow" p; + List.iter (fun (cf,msg) -> + display_error ~depth:1 ctx.com ("Overload resolution failed for " ^ (s_type (print_context()) cf.cf_type)) p; + located_display_error ~depth:2 ctx.com msg; ) failures; - error "End of overload failure reasons" p + typing_error ~depth:1 "End of overload failure reasons" p end in if overload_kind = OverloadProper then begin match Overloads.Resolution.reduce_compatible candidates with @@ -438,10 +407,10 @@ let unify_field_call ctx fa el_typed el p inline = maybe_check_access fcc.fc_field; commit_delayed_display fcc | fcc :: l -> - display_error ctx "Ambiguous overload, candidates follow" p; + display_error ctx.com "Ambiguous overload, candidates follow" p; let st = s_type (print_context()) in List.iter (fun fcc -> - display_error ctx (Printf.sprintf "... %s" (st fcc.fc_type)) fcc.fc_field.cf_name_pos; + display_error ~depth:1 ctx.com (compl_msg (st fcc.fc_type)) fcc.fc_field.cf_name_pos; ) (fcc :: l); commit_delayed_display fcc end else begin match List.rev candidates with @@ -456,20 +425,24 @@ class call_dispatcher (p : pos) = let is_set = match mode with MSet _ -> true | _ -> false in - let check_assign () = if is_set && ctx.com.display.dms_error_policy <> EPIgnore then invalid_assign p in + let check_assign () = if is_set && not (Common.ignore_error ctx.com) then invalid_assign p in object(self) method private make_field_call (fa : field_access) (el_typed : texpr list) (el : expr list) = let fcc = unify_field_call ctx fa el_typed el p fa.fa_inline in - if has_class_field_flag fcc.fc_field CfAbstract then begin match fa.fa_on.eexpr with - | TConst TSuper -> display_error ctx (Printf.sprintf "abstract method %s cannot be accessed directly" fcc.fc_field.cf_name) p; - | _ -> () - end; - fcc.fc_data() + if has_class_field_flag fcc.fc_field CfGeneric then begin + !type_generic_function_ref ctx fa fcc with_type p + end else begin + if has_class_field_flag fcc.fc_field CfAbstract then begin match fa.fa_on.eexpr with + | TConst TSuper -> display_error ctx.com (Printf.sprintf "abstract method %s cannot be accessed directly" fcc.fc_field.cf_name) p; + | _ -> () + end; + fcc.fc_data() + end method private macro_call (ethis : texpr) (cf : tclass_field) (el : expr list) = - if ctx.macro_depth > 300 then error "Stack overflow" p; + if ctx.macro_depth > 300 then typing_error "Stack overflow" p; ctx.macro_depth <- ctx.macro_depth + 1; ctx.with_type_stack <- with_type :: ctx.with_type_stack; let ethis_f = ref (fun () -> ()) in @@ -503,26 +476,27 @@ object(self) in ctx.macro_depth <- ctx.macro_depth - 1; ctx.with_type_stack <- List.tl ctx.with_type_stack; - let old = ctx.on_error in - ctx.on_error <- (fun ctx msg ep -> + let old = ctx.com.located_error in + ctx.com.located_error <- (fun ?(depth = 0) msg -> + let ep = extract_located_pos msg in (* display additional info in the case the error is not part of our original call *) if ep.pfile <> p.pfile || ep.pmax < p.pmin || ep.pmin > p.pmax then begin locate_macro_error := false; - old ctx msg ep; + old ~depth (if ep = null_pos then (relocate msg p) else msg); locate_macro_error := true; - ctx.com.error (compl_msg "Called from macro here") p; + if ep <> null_pos then old ~depth:(depth+1) (located (compl_msg "Called from macro here") p); end else - old ctx msg ep; + old ~depth msg; ); let e = try f() with exc -> - ctx.on_error <- old; + ctx.com.located_error <- old; !ethis_f(); raise exc in let e = Diagnostics.secure_generated_code ctx e in - ctx.on_error <- old; + ctx.com.located_error <- old; !ethis_f(); e @@ -530,6 +504,17 @@ object(self) used with actual expression calls and not with something like field calls. *) method expr_call (e : texpr) (el_typed : texpr list) (el : expr list) = check_assign(); + let default t = + let el = el_typed @ List.map (fun e -> type_expr ctx e WithType.value) el in + let t = if t == t_dynamic then + t_dynamic + else if ctx.untyped then + mk_mono() + else + typing_error (s_type (print_context()) e.etype ^ " cannot be called") e.epos + in + mk (TCall (e,el)) t p + in let rec loop t = match follow t with | TFun (args,r) -> let args_typed,args_left = unify_typed_args ctx (fun t -> t) args el_typed p in @@ -537,23 +522,28 @@ object(self) let el = el_typed @ el in let r = match tfunc with TFun(_,r) -> r | _ -> die "" __LOC__ in mk (TCall (e,el)) r p - | TAbstract(a,tl) when Meta.has Meta.Callable a.a_meta -> - loop (Abstract.get_underlying_type a tl) + | TAbstract(a,tl) as t -> + let check_callable () = + if Meta.has Meta.Callable a.a_meta then + loop (Abstract.get_underlying_type a tl) + else + default t + in + begin match a.a_call,a.a_impl with + | Some cf,Some c -> + let e_static = Builder.make_static_this c e.epos in + let fa = FieldAccess.create e_static cf (FHAbstract(a,tl,c)) false e.epos in + self#field_call fa (e :: el_typed) el + | _ -> + check_callable(); + end | TMono _ -> let t = mk_mono() in let el = el_typed @ List.map (fun e -> type_expr ctx e WithType.value) el in unify ctx (tfun (List.map (fun e -> e.etype) el) t) e.etype e.epos; mk (TCall (e,el)) t p | t -> - let el = el_typed @ List.map (fun e -> type_expr ctx e WithType.value) el in - let t = if t == t_dynamic then - t_dynamic - else if ctx.untyped then - mk_mono() - else - error (s_type (print_context()) e.etype ^ " cannot be called") e.epos - in - mk (TCall (e,el)) t p + default t in loop e.etype @@ -582,13 +572,12 @@ object(self) let name = Printf.sprintf "%s_%s" (if is_set then "set" else "get") fa.fa_field.cf_name in make_call ctx (mk (TField (e,quick_field_dynamic e.etype name)) tf p) el_typed t p | _ -> - error "Could not resolve accessor" p + typing_error "Could not resolve accessor" p (* Calls the field represented by `fa` with the typed arguments `el_typed` and the syntactic arguments `el`. This function inspects the nature of the field being called and dispatches the call accordingly: - * If the field is `@:generic`, call `type_generic_function`. * If the field is a non-macro method, call it via `make_field_call`. * If the field is a property, resolve the accessor (depending on `mode`) and recurse onto it. * Otherwise, call the field as a normal expression via `expr_call`. @@ -597,19 +586,15 @@ object(self) match fa.fa_field.cf_kind with | Method (MethNormal | MethInline | MethDynamic) -> check_assign(); - if has_class_field_flag fa.fa_field CfGeneric then begin - !type_generic_function_ref ctx fa el_typed el with_type p - end else - self#make_field_call fa el_typed el + self#make_field_call fa el_typed el | Method MethMacro -> begin match el_typed with | [] -> self#macro_call fa.fa_on fa.fa_field el | el_typed -> - let cur = ctx.this_stack in - let el' = List.map (fun e -> fst (push_this ctx e)) el_typed in + let el',fl = List.split (List.map (fun e -> push_this ctx e) el_typed) in let e = self#macro_call fa.fa_on fa.fa_field (el' @ el) in - ctx.this_stack <- cur; + List.iter (fun f -> f()) fl; e end; | Var v -> @@ -641,4 +626,4 @@ let maybe_reapply_overload_call ctx e = e end | _ -> - e \ No newline at end of file + e diff --git a/src/typing/calls.ml b/src/typing/calls.ml index e6a21e3b..6c6739a6 100644 --- a/src/typing/calls.ml +++ b/src/typing/calls.ml @@ -10,6 +10,17 @@ open Error open CallUnification let make_call ctx e params t ?(force_inline=false) p = + let params = + match follow e.etype with + | TFun (expected_args,_) -> + (match List.rev expected_args with + | (_,true,t) :: rest when is_pos_infos t && List.length rest = List.length params -> + let infos = mk_infos ctx p [] in + params @ [type_expr ctx infos (WithType.with_type t)] + | _ -> params + ) + | _ -> params + in try let ethis,cl,f = match e.eexpr with | TField (ethis,fa) -> @@ -36,7 +47,7 @@ let make_call ctx e params t ?(force_inline=false) p = PMap.mem f.cf_name c.cl_fields || List.exists has_override c.cl_descendants in - if List.exists has_override c.cl_descendants then error (Printf.sprintf "Cannot force inline-call to %s because it is overridden" f.cf_name) p + if List.exists has_override c.cl_descendants then typing_error (Printf.sprintf "Cannot force inline-call to %s because it is overridden" f.cf_name) p ) end; let config = Inline.inline_config cl f params t in @@ -52,17 +63,17 @@ let make_call ctx e params t ?(force_inline=false) p = (* Current method needs to infer CfModifiesThis flag, since we are calling a method, which modifies `this` *) add_class_field_flag ctx.curfield CfModifiesThis else - error ("Abstract 'this' value can only be modified inside an inline function. '" ^ f.cf_name ^ "' modifies 'this'") p; + typing_error ("Abstract 'this' value can only be modified inside an inline function. '" ^ f.cf_name ^ "' modifies 'this'") p; | _ -> () ); - let params = List.map (ctx.g.do_optimize ctx) params in + let params = List.map (Optimizer.reduce_expression ctx) params in let force_inline = is_forced_inline cl f in (match f.cf_expr_unoptimized,f.cf_expr with - | Some fd,_ + | Some {eexpr = TFunction fd},_ | None,Some { eexpr = TFunction fd } -> (match Inline.type_inline ctx f fd ethis params t config p force_inline with | None -> - if force_inline then error "Inline could not be done" p; + if force_inline then typing_error "Inline could not be done" p; raise Exit; | Some e -> e) | _ -> @@ -75,20 +86,19 @@ let make_call ctx e params t ?(force_inline=false) p = with Exit -> mk (TCall (e,params)) t p -let mk_array_get_call ctx (cf,tf,r,e1,e2o) c ebase p = match cf.cf_expr with +let mk_array_get_call ctx (cf,tf,r,e1) c ebase p = match cf.cf_expr with | None when not (has_class_field_flag cf CfExtern) -> - if not (Meta.has Meta.NoExpr cf.cf_meta) then display_error ctx "Recursive array get method" p; + if not (Meta.has Meta.NoExpr cf.cf_meta) then display_error ctx.com "Recursive array get method" p; mk (TArray(ebase,e1)) r p | _ -> let et = type_module_type ctx (TClassDecl c) None p in let ef = mk (TField(et,(FStatic(c,cf)))) tf p in make_call ctx ef [ebase;e1] r p -let mk_array_set_call ctx (cf,tf,r,e1,e2o) c ebase p = - let evalue = match e2o with None -> die "" __LOC__ | Some e -> e in +let mk_array_set_call ctx (cf,tf,r,e1,evalue) c ebase p = match cf.cf_expr with | None when not (has_class_field_flag cf CfExtern) -> - if not (Meta.has Meta.NoExpr cf.cf_meta) then display_error ctx "Recursive array set method" p; + if not (Meta.has Meta.NoExpr cf.cf_meta) then display_error ctx.com "Recursive array set method" p; let ea = mk (TArray(ebase,e1)) r p in mk (TBinop(OpAssign,ea,evalue)) r p | _ -> @@ -100,9 +110,10 @@ let abstract_using_param_type sea = match follow sea.se_this.etype with | TAbstract(a,tl) when has_class_field_flag sea.se_access.fa_field CfImpl -> apply_params a.a_params tl a.a_this | _ -> sea.se_this.etype -let rec acc_get ctx g p = +let rec acc_get ctx g = let inline_read fa = let cf = fa.fa_field in + let p = fa.fa_pos in (* do not create a closure for static calls *) let apply_params = match fa.fa_host with | FHStatic c -> @@ -155,12 +166,12 @@ let rec acc_get ctx g p = let e_def = FieldAccess.get_field_expr fa FRead in begin match follow fa.fa_on.etype with | TInst (c,_) when chk_class c -> - display_error ctx "Can't create closure on an extern inline member method" p; + display_error ctx.com "Can't create closure on an extern inline member method" p; e_def | TAnon a -> begin match !(a.a_status) with | Statics c when has_class_field_flag cf CfExtern -> - display_error ctx "Cannot create closure on @:extern inline method" p; + display_error ctx.com "Cannot create closure on @:extern inline method" p; e_def | Statics c when chk_class c -> wrap_extern c | _ -> e_def @@ -174,19 +185,34 @@ let rec acc_get ctx g p = let tf = apply_params cf.cf_type in if not (type_iseq tf e.etype) then mk (TCast(e,None)) tf e.epos else e - | Var _,None when ctx.com.display.dms_display -> - FieldAccess.get_field_expr fa FRead | Var _,None -> - error "Recursive inline is not supported" p + typing_error "Recursive inline is not supported" p end in - let dispatcher () = new call_dispatcher ctx MGet WithType.value p in + let dispatcher p = new call_dispatcher ctx MGet WithType.value p in match g with - | AKNo f -> error ("Field " ^ f ^ " cannot be accessed for reading") p + | AKNo(_,p) -> typing_error ("This expression cannot be accessed for reading") p | AKExpr e -> e + | AKSafeNav sn -> + (* generate null-check branching for the safe navigation chain *) + let eobj = sn.sn_base in + let enull = Builder.make_null eobj.etype sn.sn_pos in + let eneq = Builder.binop OpNotEq eobj enull ctx.t.tbool sn.sn_pos in + let ethen = acc_get ctx sn.sn_access in + let tnull = ctx.t.tnull ethen.etype in + let ethen = if not (is_nullable ethen.etype) then + mk (TCast(ethen,None)) tnull ethen.epos + else + ethen + in + let eelse = Builder.make_null tnull sn.sn_pos in + let eif = mk (TIf(eneq,ethen,Some eelse)) tnull sn.sn_pos in + (match sn.sn_temp_var with + | None -> eif + | Some evar -> { eif with eexpr = TBlock [evar; eif] }) | AKAccess _ -> die "" __LOC__ | AKResolve(sea,name) -> - (dispatcher ())#resolve_call sea name + (dispatcher sea.se_access.fa_pos)#resolve_call sea name | AKUsingAccessor sea | AKUsingField sea when ctx.in_display -> (* Generate a TField node so we can easily match it for position/usage completion (issue #1968) *) let e_field = FieldAccess.get_field_expr sea.se_access FGet in @@ -204,7 +230,7 @@ let rec acc_get ctx g p = if ctx.in_display then FieldAccess.get_field_expr fa FRead else - error "Invalid macro access" p + typing_error "Invalid macro access" fa.fa_pos | _ -> if fa.fa_inline then inline_read fa @@ -212,15 +238,16 @@ let rec acc_get ctx g p = FieldAccess.get_field_expr fa FRead end | AKAccessor fa -> - (dispatcher())#field_call fa [] [] + (dispatcher fa.fa_pos)#field_call fa [] [] | AKUsingAccessor sea -> - (dispatcher())#field_call sea.se_access [sea.se_this] [] + (dispatcher sea.se_access.fa_pos)#field_call sea.se_access [sea.se_this] [] | AKUsingField sea -> let e = sea.se_this in let e_field = FieldAccess.get_field_expr sea.se_access FGet in (* build a closure with first parameter applied *) (match follow e_field.etype with | TFun ((_,_,t0) :: args,ret) -> + let p = sea.se_access.fa_pos in let te = abstract_using_param_type sea in unify ctx te t0 e.epos; let tcallb = TFun (args,ret) in @@ -245,27 +272,56 @@ let rec acc_get ctx g p = make_call ctx ewrap [e] tcallb p | _ -> die "" __LOC__) -let build_call ?(mode=MGet) ctx acc el (with_type:WithType.t) p = +let check_dynamic_super_method_call ctx fa p = + match fa with + | { fa_on = { eexpr = TConst TSuper } ; fa_field = { cf_kind = Method MethDynamic; cf_name = name } } -> + ctx.com.error ("Cannot call super." ^ name ^ " since it's a dynamic method") p + | _ -> + () + +let rec build_call_access ctx acc el mode with_type p = + let get_accessor_to_call fa args = + let dispatch = new call_dispatcher ctx MGet WithType.value fa.fa_pos in + dispatch#field_call fa args [] + in let dispatch = new call_dispatcher ctx mode with_type p in match acc with | AKField fa -> - dispatch#field_call fa [] el + check_dynamic_super_method_call ctx fa p; + AKExpr (dispatch#field_call fa [] el) | AKUsingField sea -> let eparam = sea.se_this in - dispatch#field_call sea.se_access [eparam] el + let e = dispatch#field_call sea.se_access [eparam] el in + let e = match sea.se_access.fa_host with + | FHAbstract _ when not ctx.allow_transform -> + (* transform XXXImpl.field(this,args) back into this.field(args) *) + (match e.eexpr with + | TCall ({ eexpr = TField(_,name) } as f, abs :: el) -> { e with eexpr = TCall(mk (TField(abs,name)) t_dynamic f.epos, el) } + | _ -> assert false) + | _ -> + e + in + AKExpr e | AKResolve(sea,name) -> - dispatch#expr_call (dispatch#resolve_call sea name) [] el - | AKNo _ | AKAccess _ -> - ignore(acc_get ctx acc p); - error ("Unexpected access mode, please report this: " ^ (s_access_kind acc)) p + AKExpr (dispatch#expr_call (dispatch#resolve_call sea name) [] el) + | AKNo(_,p) -> + typing_error "This expression cannot be called" p + | AKAccess _ -> + typing_error "This expression cannot be called" p | AKAccessor fa -> - let e = dispatch#field_call fa [] [] in - dispatch#expr_call e [] el + let e = get_accessor_to_call fa [] in + AKExpr (dispatch#expr_call e [] el) | AKUsingAccessor sea -> - let e = dispatch#field_call sea.se_access [sea.se_this] [] in - dispatch#expr_call e [] el + let e = get_accessor_to_call sea.se_access [sea.se_this] in + AKExpr (dispatch#expr_call e [] el) | AKExpr e -> - dispatch#expr_call e [] el + AKExpr (dispatch#expr_call e [] el) + | AKSafeNav sn -> + (* pack the call inside the safe navigation chain *) + AKSafeNav { sn with sn_access = build_call_access ctx sn.sn_access el mode with_type p } + +let build_call ?(mode=MGet) ctx acc el (with_type:WithType.t) p = + acc_get ctx (build_call_access ctx acc el mode with_type p) let rec needs_temp_var e = match e.eexpr with @@ -274,6 +330,9 @@ let rec needs_temp_var e = | _ -> true let call_to_string ctx ?(resume=false) e = + if not ctx.allow_transform then + { e with etype = ctx.t.tstring } + else let gen_to_string e = (* Ignore visibility of the toString field. *) ctx.meta <- (Meta.PrivateAccess,[],e.epos) :: ctx.meta; @@ -315,19 +374,19 @@ let type_bind ctx (e : texpr) (args,ret) params p = in let rec loop args params given_args missing_args ordered_args = match args, params with | [], [] -> given_args,missing_args,ordered_args - | [], _ -> error "Too many callback arguments" p + | [], _ -> typing_error "Too many callback arguments" p | (n,o,t) :: args , [] when o -> let a = if is_pos_infos t then let infos = mk_infos ctx p [] in ordered_args @ [type_expr ctx infos (WithType.with_argument t n)] - else if ctx.com.config.pf_pad_nulls then + else if ctx.com.config.pf_pad_nulls && ctx.allow_transform then (ordered_args @ [(mk (TConst TNull) t_dynamic p)]) else ordered_args in loop args [] given_args missing_args a | (n,o,t) :: _ , (EConst(Ident "_"),p) :: _ when not ctx.com.config.pf_can_skip_non_nullable_argument && o && not (is_nullable t) -> - error "Usage of _ is not supported for optional non-nullable arguments" p + typing_error "Usage of _ is not supported for optional non-nullable arguments" p | (n,o,t) :: args , ([] as params) | (n,o,t) :: args , (EConst(Ident "_"),_) :: params -> let v = alloc_var VGenerated (alloc_name n) (if o then ctx.t.tnull t else t) p in @@ -349,6 +408,16 @@ let type_bind ctx (e : texpr) (args,ret) params p = e,var_decls | TField(_,(FStatic(_,cf) | FInstance(_,_,cf))) when is_immutable_method cf -> e,var_decls + | TField(eobj,FClosure(Some (cl,tp), cf)) when is_immutable_method cf -> + (* + if we're binding an instance method, we don't really need to create a closure for it, + since we'll create a closure for the binding anyway, instead store the instance and + call its method inside a bind-generated closure + *) + let vobj = alloc_var VGenerated "`" eobj.etype eobj.epos in + let var_decl = mk (TVar(vobj, Some eobj)) ctx.t.tvoid eobj.epos in + let eobj = { eobj with eexpr = TLocal vobj } in + { e with eexpr = TField(eobj, FInstance (cl, tp, cf)) }, var_decl :: var_decls | _ -> let e_var = alloc_var VGenerated "`" e.etype e.epos in (mk (TLocal e_var) e.etype e.epos), (mk (TVar(e_var,Some e)) ctx.t.tvoid e.epos) :: var_decls @@ -385,8 +454,14 @@ let array_access ctx e1 e2 mode p = AKAccess (a,pl,c,e1,e2) | _ -> has_abstract_array_access := true; - let e = mk_array_get_call ctx (AbstractCast.find_array_access ctx a pl e2 None p) c e1 p in - AKExpr e + let f = AbstractCast.find_array_read_access ctx a pl e2 p in + if not ctx.allow_transform then + let _,_,r,_ = f in + AKExpr { eexpr = TArray(e1,e2); epos = p; etype = r } + else begin + let e = mk_array_get_call ctx f c e1 p in + AKExpr e + end end | _ -> raise Not_found) with Not_found -> @@ -409,8 +484,8 @@ let array_access ctx e1 e2 mode p = let pt = spawn_monomorph ctx p in let t = ctx.t.tarray pt in begin try - unify_raise ctx et t p - with Error(Unify _,_) -> + unify_raise et t p + with Error(Unify _,_,_) -> if not ctx.untyped then begin let msg = if !has_abstract_array_access then "No @:arrayAccess function accepts an argument of " ^ (s_type (print_context()) e2.etype) @@ -435,12 +510,16 @@ let field_chain ctx path access mode with_type = let rec loop access path = match path with | [] -> access - | [(name,_,p)] -> - let e = acc_get ctx access p in - type_field_default_cfg ctx e name p mode with_type - | (name,_,p) :: path -> - let e = acc_get ctx access p in - let access = type_field_default_cfg ctx e name p MGet WithType.value in + | part :: path -> + let e = acc_get ctx access in + let mode, with_type = + if path <> [] then + (* intermediate field access are just reading the value *) + MGet, WithType.value + else + mode, with_type + in + let access = type_field_default_cfg ctx e part.name part.pos mode with_type in loop access path in - loop access path \ No newline at end of file + loop access path diff --git a/src/typing/fieldAccess.ml b/src/typing/fieldAccess.ml index 145e14bb..786a58c2 100644 --- a/src/typing/fieldAccess.ml +++ b/src/typing/fieldAccess.ml @@ -12,9 +12,9 @@ type field_host = (* Used as lhs, no semantic difference to FGet. *) | FWrite -type accessor_resolution = +type 'a accessor_resolution = (* Accessor was found. *) - | AccessorFound of field_access + | AccessorFound of 'a (* Accessor was not found, but access was made on anonymous structure. *) | AccessorAnon (* Accessor was not found. *) @@ -37,6 +37,21 @@ let apply_fa cf = function | FHAbstract(a,tl,c) -> FStatic(c,cf) | FHAnon -> FAnon cf +let get_host c cf = + if has_class_field_flag cf CfStatic then + FHStatic c + else match c.cl_kind with + | KAbstractImpl a -> + FHAbstract(a,extract_param_types a.a_params,c) + | _ -> + FHInstance(c,extract_param_types c.cl_params) + +let get_host_class_raise = function + | FHStatic c -> c + | FHInstance(c,_) -> c + | FHAbstract(_,_,c) -> c + | FHAnon -> raise Not_found + (* Returns the mapping function to apply type parameters. *) let get_map_function fa = match fa.fa_host with | FHStatic _ | FHAnon -> (fun t -> t) @@ -75,19 +90,18 @@ let get_field_expr fa mode = in mk (TField(fa.fa_on,fa')) t fa.fa_pos -(* Resolves the accessor on the field access, using the provided `mode`. *) -let resolve_accessor fa mode = match fa.fa_field.cf_kind with +let find_accessor_for_field host cf t mode = match cf.cf_kind with | Var v -> begin match (match mode with MSet _ -> v.v_write | _ -> v.v_read) with | AccCall -> - let name = (match mode with MSet _ -> "set_" | _ -> "get_") ^ fa.fa_field.cf_name in + let name = (match mode with MSet _ -> "set_" | _ -> "get_") ^ cf.cf_name in let forward cf_acc new_host = - create fa.fa_on cf_acc new_host fa.fa_inline fa.fa_pos + (cf_acc,new_host) in - begin match fa.fa_host with + begin match host with | FHStatic c -> begin try - AccessorFound (forward (PMap.find name c.cl_statics) fa.fa_host) + AccessorFound (forward (PMap.find name c.cl_statics) host) with Not_found -> (* TODO: Check if this is correct, there's a case in hxcpp's VirtualArray *) AccessorAnon @@ -95,7 +109,7 @@ let resolve_accessor fa mode = match fa.fa_field.cf_kind with | FHInstance(c,tl) -> begin try (* Accessors can be overridden, so we have to check the actual type. *) - let c,tl = match follow fa.fa_on.etype with + let c,tl = match follow t with | TInst(c,tl) -> c,tl | _ -> c,tl in @@ -110,7 +124,7 @@ let resolve_accessor fa mode = match fa.fa_field.cf_kind with end | FHAbstract(a,tl,c) -> begin try - AccessorFound (forward (PMap.find name c.cl_statics) fa.fa_host) + AccessorFound (forward (PMap.find name c.cl_statics) host) with Not_found -> AccessorAnon end @@ -123,6 +137,21 @@ let resolve_accessor fa mode = match fa.fa_field.cf_kind with | _ -> AccessorInvalid +(* Resolves the accessor on the field access, using the provided `mode`. *) +let resolve_accessor fa mode = + let forward cf_acc new_host = + create fa.fa_on cf_acc new_host fa.fa_inline fa.fa_pos + in + match find_accessor_for_field fa.fa_host fa.fa_field fa.fa_on.etype mode with + | AccessorFound(cf_acc,new_host) -> + AccessorFound (forward cf_acc new_host) + | AccessorInvalid -> + AccessorInvalid + | AccessorAnon -> + AccessorAnon + | AccessorNotFound -> + AccessorNotFound + let get_constructor_access c tl p = try let e_static = Builder.make_static_this c p in @@ -139,7 +168,7 @@ let get_constructor_access c tl p = in create e_static cf fh false p with Not_found -> - raise_error (No_constructor (match c.cl_kind with + raise_typing_error (No_constructor (match c.cl_kind with | KAbstractImpl a -> TAbstractDecl a | _ -> TClassDecl c )) p diff --git a/src/typing/fields.ml b/src/typing/fields.ml index 0363b174..77ad56eb 100644 --- a/src/typing/fields.ml +++ b/src/typing/fields.ml @@ -73,11 +73,11 @@ let field_type ctx c pl f p = apply_params l monos f.cf_type let no_abstract_constructor c p = - if has_class_flag c CAbstract then raise_error (Abstract_class (TClassDecl c)) p + if has_class_flag c CAbstract then raise_typing_error (Abstract_class (TClassDecl c)) p let check_constructor_access ctx c f p = - if (Meta.has Meta.CompilerGenerated f.cf_meta) then display_error ctx (error_msg (No_constructor (TClassDecl c))) p; - if not (can_access ctx c f true || extends ctx.curclass c) && not ctx.untyped then display_error ctx (Printf.sprintf "Cannot access private constructor of %s" (s_class_path c)) p + if (Meta.has Meta.CompilerGenerated f.cf_meta) then located_display_error ctx.com (error_msg p (No_constructor (TClassDecl c))); + if not (can_access ctx c f true || extends ctx.curclass c) && not ctx.untyped then display_error ctx.com (Printf.sprintf "Cannot access private constructor of %s" (s_class_path c)) p let check_no_closure_meta ctx cf fa mode p = match mode with @@ -89,7 +89,7 @@ let check_no_closure_meta ctx cf fa mode p = Meta.has Meta.NoClosure cl_meta || Meta.has Meta.NoClosure f.cf_meta then - error ("Method " ^ f.cf_name ^ " cannot be used as a value") p + typing_error ("Method " ^ f.cf_name ^ " cannot be used as a value") p | _ -> () in begin match cf.cf_kind with @@ -105,17 +105,16 @@ let check_no_closure_meta ctx cf fa mode p = | _ -> () -(* Note: `p` is the position of the field access expression. *) let field_access ctx mode f fh e pfield = let pfull = punion e.epos pfield in let is_set = match mode with MSet _ -> true | _ -> false in check_no_closure_meta ctx f fh mode pfield; let bypass_accessor = if ctx.bypass_accessor > 0 then (ctx.bypass_accessor <- ctx.bypass_accessor - 1; true) else false in - let make_access inline = FieldAccess.create e f fh inline pfull in + let make_access inline = FieldAccess.create e f fh (inline && ctx.allow_inline) pfull in match f.cf_kind with | Method m -> let normal () = AKField(make_access false) in - if is_set && m <> MethDynamic && not ctx.untyped then error "Cannot rebind this method : please use 'dynamic' before method declaration" pfield; + if is_set && m <> MethDynamic && not ctx.untyped then typing_error "Cannot rebind this method : please use 'dynamic' before method declaration" pfield; let maybe_check_visibility c static = (* For overloads we have to resolve the actual field before we can check accessibility. *) begin match mode with @@ -127,34 +126,24 @@ let field_access ctx mode f fh e pfield = in let default () = match m, mode with - | MethInline, _ when ctx.g.doinline -> + | MethInline, _ when ctx.g.doinline && ctx.allow_inline -> AKField (make_access true) | MethMacro, MGet -> - display_error ctx "Macro functions must be called immediately" pfield; normal() + display_error ctx.com "Macro functions must be called immediately" pfield; normal() | _ , MGet -> - if has_class_field_flag f CfGeneric then display_error ctx "Cannot create closure on generic function" pfield; + if has_class_field_flag f CfGeneric then display_error ctx.com "Cannot create closure on generic function" pfield; normal() | _ -> normal() in begin match fh with | FHInstance(c,tl) -> - if e.eexpr = TConst TSuper then (match mode,f.cf_kind with - | MGet,Var {v_read = AccCall } - | MSet _,Var {v_write = AccCall } - | MCall _,Var {v_read = AccCall } -> + if e.eexpr = TConst TSuper then begin match mode with + | MSet _ | MGet -> + display_error ctx.com "Cannot create closure on super method" pfield + | MCall _ -> () - | MCall _, Var _ -> - display_error ctx "Cannot access superclass variable for calling: needs to be a proper method" pfield - | MCall _, _ -> - () - | MGet,Var _ - | MSet _,Var _ when ctx.com.platform = Flash && has_class_flag c CExtern -> - () - | _, Method _ -> - display_error ctx "Cannot create closure on super method" pfield - | _ -> - display_error ctx "Normal variables cannot be accessed with 'super', use 'this' instead" pfield); + end; (* We need the actual class type (i.e. a potential child class) for visibility checks. *) begin match follow e.etype with | TInst(c,_) -> @@ -184,12 +173,23 @@ let field_access ctx mode f fh e pfield = | _ -> () end; + if e.eexpr = TConst TSuper then begin match mode with + | MGet | MCall _ when v.v_read = AccCall -> + () + | MSet _ when v.v_write = AccCall -> + () + | _ -> + display_error ctx.com "Normal variables cannot be accessed with 'super', use 'this' instead" pfield; + end; | FHAnon -> () end; let normal inline = AKField (make_access inline) in + let normal_failure ()= + AKNo((normal false),pfield) + in match (match mode with MGet | MCall _ -> v.v_read | MSet _ -> v.v_write) with | AccNo when not (Meta.has Meta.PrivateAccess ctx.meta) -> (match follow e.etype with @@ -198,12 +198,12 @@ let field_access ctx mode f fh e pfield = | TAnon a -> (match !(a.a_status) with | Statics c2 when ctx.curclass == c2 || can_access ctx c2 { f with cf_flags = unset_flag f.cf_flags (int_of_class_field_flag CfPublic) } true -> normal false - | _ -> if ctx.untyped then normal false else AKNo f.cf_name) + | _ -> if ctx.untyped then normal false else normal_failure()) | _ -> - if ctx.untyped then normal false else AKNo f.cf_name) + if ctx.untyped then normal false else normal_failure()) | AccNormal | AccNo -> normal false - | AccCall when ctx.in_display && DisplayPosition.display_position#enclosed_in pfull -> + | AccCall when (not ctx.allow_transform) || (ctx.in_display && DisplayPosition.display_position#enclosed_in pfull) -> normal false | AccCall -> let m = (match mode with MSet _ -> "set_" | _ -> "get_") ^ f.cf_name in @@ -221,10 +221,10 @@ let field_access ctx mode f fh e pfield = ) in if bypass_accessor then ( - (match e.eexpr with TLocal _ when Common.defined ctx.com Define.Haxe3Compat -> ctx.com.warning "Field set has changed here in Haxe 4: call setter explicitly to keep Haxe 3.x behaviour" pfield | _ -> ()); + (match e.eexpr with TLocal _ when Common.defined ctx.com Define.Haxe3Compat -> warning ctx WTemp "Field set has changed here in Haxe 4: call setter explicitly to keep Haxe 3.x behaviour" pfield | _ -> ()); if not (is_physical_field f) then begin - display_error ctx "This field cannot be accessed because it is not a real variable" pfield; - display_error ctx "Add @:isVar here to enable it" f.cf_pos; + display_error ctx.com "This field cannot be accessed because it is not a real variable" pfield; + display_error ctx.com "Add @:isVar here to enable it" f.cf_pos; end; normal false ) @@ -236,18 +236,21 @@ let field_access ctx mode f fh e pfield = AKAccessor (make_access false) end | AccNever -> - if ctx.untyped then normal false else AKNo f.cf_name + if ctx.untyped then normal false else normal_failure() | AccInline -> normal true | AccCtor -> + let is_child_of_abstract c = + has_class_flag c CAbstract && extends ctx.curclass c + in (match ctx.curfun, fh with - | FunConstructor, FHInstance(c,_) when c == ctx.curclass -> normal false - | _ -> AKNo f.cf_name + | FunConstructor, FHInstance(c,_) when c == ctx.curclass || is_child_of_abstract c -> normal false + | _ -> normal_failure() ) | AccRequire (r,msg) -> match msg with | None -> error_require r pfield - | Some msg -> error msg pfield + | Some msg -> typing_error msg pfield let class_field ctx c tl name p = raw_class_field (fun f -> field_type ctx c tl f p) c tl name @@ -282,7 +285,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = let _,el,_ = Meta.get meta a.a_meta in if el <> [] && not (List.exists (fun e -> match fst e with | EConst (Ident i' | String (i',_)) -> i' = i - | _ -> error "Identifier or string expected as argument to @:forward" (pos e) + | _ -> typing_error "Identifier or string expected as argument to @:forward" (pos e) ) el) then raise Not_found; f() in @@ -294,7 +297,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = type_field_by_forward f Meta.Forward a in let type_field_by_typedef f e td tl = - f e (follow_without_type (apply_params td.t_params tl td.t_type)) + f e (follow_without_type (apply_typedef td tl)) in let type_field_by_interfaces e c = (* For extern lib types we didn't go through check_interfaces and check_abstract_class, which handles some field @@ -328,7 +331,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = | TAnon a -> (try let f = PMap.find i a.a_fields in - if has_class_field_flag f CfImpl && not (has_class_field_flag f CfEnum) then display_error ctx "Cannot access non-static abstract field statically" pfield; + if has_class_field_flag f CfImpl && not (has_class_field_flag f CfEnum) then display_error ctx.com "Cannot access non-static abstract field statically" pfield; match !(a.a_status) with | EnumStatics en -> let c = try PMap.find f.cf_name en.e_constrs with Not_found -> die "" __LOC__ in @@ -435,7 +438,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = end | _ -> check() - with Unify_error el | Error (Unify el,_) -> + with Unify_error el | Error (Unify el,_,_) -> check_constant_struct := !check_constant_struct || List.exists (function | Has_extra_field _ -> true | _ -> false @@ -512,10 +515,10 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = in loop c tl with Not_found when PMap.mem i c.cl_statics -> - error ("Cannot access static field " ^ i ^ " from a class instance") pfield; + typing_error ("Cannot access static field " ^ i ^ " from a class instance") pfield; ) | TDynamic t -> - AKExpr (mk (TField (e,FDynamic i)) t p) + AKExpr (mk (TField (e,FDynamic i)) (match t with None -> t_dynamic | Some t -> t) p) | TAbstract (a,tl) -> (try if not (TypeFieldConfig.allow_resolve cfg) then raise Not_found; @@ -526,7 +529,7 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = with Not_found -> try type_field_by_forward_member type_field_by_fallback e a tl with Not_found when not (has_class_field_flag (PMap.find i (find_some a.a_impl).cl_statics) CfImpl) -> - error ("Invalid call to static function " ^ i ^ " through abstract instance") pfield + typing_error ("Invalid call to static function " ^ i ^ " through abstract instance") pfield ) | _ -> raise Not_found in @@ -551,16 +554,16 @@ let type_field cfg ctx e i p mode (with_type : WithType.t) = | TInst ({ cl_kind = KAbstractImpl a },_) | TAbstract (a,_) when has_special_field a -> (* the abstract field is not part of the field list, which is only true when it has no expression (issue #2344) *) - display_error ctx ("Field " ^ i ^ " cannot be called directly because it has no expression") pfield; + display_error ctx.com ("Field " ^ i ^ " cannot be called directly because it has no expression") pfield; | TAnon { a_status = { contents = Statics c } } when PMap.mem i c.cl_fields -> - display_error ctx ("Static access to instance field " ^ i ^ " is not allowed") pfield; + display_error ctx.com ("Static access to instance field " ^ i ^ " is not allowed") pfield; | _ -> let tthis = e.etype in try - if not (Diagnostics.is_diagnostics_run ctx.com pfield) then raise Exit; + if not (Diagnostics.error_in_diagnostics_run ctx.com pfield) then raise Exit; DisplayFields.handle_missing_field_raise ctx tthis i mode with_type pfield with Exit -> - display_error ctx (StringError.string_error i (string_source tthis) (s_type (print_context()) tthis ^ " has no field " ^ i)) pfield + display_error ctx.com (StringError.string_error i (string_source tthis) (s_type (print_context()) tthis ^ " has no field " ^ i)) pfield end; AKExpr (mk (TField (e,FDynamic i)) (spawn_monomorph ctx p) p) @@ -626,4 +629,4 @@ let get_struct_init_anon_fields c tl = PMap.add cf.cf_name cf fields | _ -> fields - ) c.cl_fields PMap.empty \ No newline at end of file + ) c.cl_fields PMap.empty diff --git a/src/typing/finalization.ml b/src/typing/finalization.ml index 2c9f60da..0545697c 100644 --- a/src/typing/finalization.ml +++ b/src/typing/finalization.ml @@ -23,26 +23,26 @@ let get_main ctx types = | None -> raise Not_found | Some c -> - p := c.cl_pos; + p := c.cl_name_pos; c, PMap.find "main" c.cl_statics with Not_found -> try let t = Typeload.find_type_in_module_raise ctx m name null_pos in match t with | TEnumDecl _ | TTypeDecl _ | TAbstractDecl _ -> - error ("Invalid -main : " ^ s_type_path path ^ " is not a class") null_pos + typing_error ("Invalid -main : " ^ s_type_path path ^ " is not a class") null_pos | TClassDecl c -> - p := c.cl_pos; + p := c.cl_name_pos; c, PMap.find "main" c.cl_statics with Not_found -> - error ("Invalid -main : " ^ s_type_path path ^ " does not have static function main") !p + typing_error ("Invalid -main : " ^ s_type_path path ^ " does not have static function main") !p in let ft = Type.field_type f in let fmode, r = match follow ft with | TFun ([],r) -> FStatic (c,f), r - | _ -> error ("Invalid -main : " ^ s_type_path path ^ " has invalid main function") c.cl_pos + | _ -> typing_error ("Invalid -main : " ^ s_type_path path ^ " has invalid main function") c.cl_pos in - if not (ExtType.is_void (follow r)) then error (Printf.sprintf "Return type of main function should be Void (found %s)" (s_type (print_context()) r)) f.cf_name_pos; + if not (ExtType.is_void (follow r)) then typing_error (Printf.sprintf "Return type of main function should be Void (found %s)" (s_type (print_context()) r)) f.cf_name_pos; f.cf_meta <- (Dce.mk_keep_meta f.cf_pos) :: f.cf_meta; let emain = type_module_type ctx (TClassDecl c) None null_pos in let main = mk (TCall (mk (TField (emain,fmode)) ft null_pos,[])) r null_pos in @@ -86,7 +86,7 @@ let finalize ctx = () | fl -> let rec loop handled_types = - let all_types = Hashtbl.fold (fun _ m acc -> m.m_types @ acc) ctx.g.modules [] in + let all_types = ctx.com.module_lut#fold (fun _ m acc -> m.m_types @ acc) [] in match (List.filter (fun mt -> not (List.memq mt handled_types)) all_types) with | [] -> () @@ -102,7 +102,7 @@ type state = | Done | NotYet -let sort_types com modules = +let sort_types com (modules : (path,module_def) lookup) = let types = ref [] in let states = Hashtbl.create 0 in let state p = try Hashtbl.find states p with Not_found -> NotYet in @@ -113,7 +113,7 @@ let sort_types com modules = match state p with | Done -> () | Generating -> - com.warning ("Warning : maybe loop in static generation of " ^ s_type_path p) (t_infos t).mt_pos; + com.warning WStaticInitOrder [] ("Warning : maybe loop in static generation of " ^ s_type_path p) (t_infos t).mt_pos; | NotYet -> Hashtbl.add states p Generating; let t = (match t with @@ -193,10 +193,10 @@ let sort_types com modules = ) c.cl_statics in - let sorted_modules = List.sort (fun m1 m2 -> compare m1.m_path m2.m_path) (Hashtbl.fold (fun _ m acc -> m :: acc) modules []) in + let sorted_modules = List.sort (fun m1 m2 -> compare m1.m_path m2.m_path) (modules#fold (fun _ m acc -> m :: acc) []) in List.iter (fun m -> List.iter loop m.m_types) sorted_modules; List.rev !types, sorted_modules let generate ctx = - let types,modules = sort_types ctx.com ctx.g.modules in + let types,modules = sort_types ctx.com ctx.com.module_lut in get_main ctx types,types,modules diff --git a/src/typing/forLoop.ml b/src/typing/forLoop.ml index d0da20f0..56d528ce 100644 --- a/src/typing/forLoop.ml +++ b/src/typing/forLoop.ml @@ -92,9 +92,9 @@ module IterationKind = struct | TDynamic _ | TMono _ -> (* try to find something better than a dynamic value to iterate on *) dynamic_iterator := Some e; - raise (Error (Unify [Unify_custom "Avoid iterating on a dynamic value"], p)) + raise (Error (Unify [Unify_custom "Avoid iterating on a dynamic value"], p, 0)) | _ -> e - with Error (Unify _,_) -> + with Error (Unify _,_,depth) -> let try_last_resort after = try match last_resort with @@ -106,16 +106,16 @@ module IterationKind = struct let try_acc acc = let acc_expr = build_call ctx acc [] WithType.value e.epos in try - unify_raise ctx acc_expr.etype t acc_expr.epos; + unify_raise acc_expr.etype t acc_expr.epos; acc_expr - with Error (Unify(l),p) -> + with Error (Unify(l),p,n) -> try_last_resort (fun () -> match !dynamic_iterator with | Some e -> e | None -> if resume then raise Not_found; - display_error ctx "Field iterator has an invalid type" acc_expr.epos; - display_error ctx (error_msg (Unify l)) p; + display_error ~depth ctx.com "Field iterator has an invalid type" acc_expr.epos; + located_display_error ~depth:(depth+1) ctx.com (error_msg p (Unify l)); mk (TConst TNull) t_dynamic p ) in @@ -154,7 +154,7 @@ module IterationKind = struct (try (* first try: do we have an @:arrayAccess getter field? *) let todo = mk (TConst TNull) ctx.t.tint p in - let cf,_,r,_,_ = AbstractCast.find_array_access_raise ctx a tl todo None p in + let cf,_,r,_ = AbstractCast.find_array_read_access_raise ctx a tl todo p in let get_next e_base e_index t p = make_static_call ctx c cf (apply_params a.a_params tl) [e_base;e_index] r p in @@ -172,7 +172,7 @@ module IterationKind = struct let of_texpr ?(resume=false) ctx e unroll p = let dynamic_iterator e = - display_error ctx "You can't iterate on a Dynamic value, please specify Iterator or Iterable" e.epos; + display_error ctx.com "You can't iterate on a Dynamic value, please specify Iterator or Iterable" e.epos; IteratorDynamic,e,t_dynamic in let check_iterator () = @@ -260,7 +260,7 @@ module IterationKind = struct { it_kind = it; it_type = pt; - it_expr = e1; + it_expr = if not ctx.allow_transform then e else e1; } let to_texpr ctx v iterator e2 p = @@ -280,7 +280,7 @@ module IterationKind = struct | TBinop (OpAssignOp _,{ eexpr = TLocal l },_) | TUnop (Increment,_,{ eexpr = TLocal l }) | TUnop (Decrement,_,{ eexpr = TLocal l }) when List.memq l vl -> - error "Loop variable cannot be modified" e.epos + typing_error "Loop variable cannot be modified" e.epos | _ -> Type.iter loop e in @@ -315,9 +315,11 @@ module IterationKind = struct mk (TBlock el) t_void p in match iterator.it_kind with + | _ when not ctx.allow_transform -> + mk (TFor(v,e1,e2)) t_void p | IteratorIntUnroll(offset,length,ascending) -> check_loop_var_modification [v] e2; - if not ascending then error "Cannot iterate backwards" p; + if not ascending then typing_error "Cannot iterate backwards" p; let el = ExtList.List.init length (fun i -> let ei = make_int ctx.t (if ascending then i + offset else offset - i) p in let rec loop e = match e.eexpr with @@ -330,7 +332,7 @@ module IterationKind = struct mk (TBlock el) t_void p | IteratorIntConst(a,b,ascending) -> check_loop_var_modification [v] e2; - if not ascending then error "Cannot iterate backwards" p; + if not ascending then typing_error "Cannot iterate backwards" p; let v_index = gen_local ctx t_int a.epos in let evar_index = mk (TVar(v_index,Some a)) t_void a.epos in let ev_index = make_local v_index v_index.v_pos in @@ -460,7 +462,7 @@ let type_for_loop ctx handle_display it e2 p = let rec loop_ident dko e1 = match e1 with | EConst(Ident i),p -> i,p,dko | EDisplay(e1,dk),_ -> loop_ident (Some dk) e1 - | _ -> error "Identifier expected" (pos e1) + | _ -> typing_error "Identifier expected" (pos e1) in let rec loop dko e1 = match fst e1 with | EBinop(OpIn,e1,e2) -> @@ -475,7 +477,7 @@ let type_for_loop ctx handle_display it e2 p = | Some dk -> ignore(handle_display ctx e1 dk MGet WithType.value); | None -> () end; - error "For expression should be 'v in expr'" (snd it) + typing_error "For expression should be 'v in expr'" (snd it) in let ik,e1 = loop None it in let e1 = type_expr ctx e1 WithType.value in @@ -503,7 +505,7 @@ let type_for_loop ctx handle_display it e2 p = | IKKeyValue((ikey,pkey,dkokey),(ivalue,pvalue,dkovalue)) -> (match follow e1.etype with | TDynamic _ | TMono _ -> - display_error ctx "You can't iterate on a Dynamic value, please specify KeyValueIterator or KeyValueIterable" e1.epos; + display_error ctx.com "You can't iterate on a Dynamic value, please specify KeyValueIterator or KeyValueIterable" e1.epos; | _ -> () ); let e1,pt = IterationKind.check_iterator ctx "keyValueIterator" e1 e1.epos in @@ -513,8 +515,8 @@ let type_for_loop ctx handle_display it e2 p = let enext = build_call ctx (type_field_default_cfg ctx etmp "next" etmp.epos (MCall []) WithType.value (* WITHTYPETODO *)) [] WithType.value etmp.epos in let v = gen_local ctx pt e1.epos in let ev = make_local v v.v_pos in - let ekey = Calls.acc_get ctx (type_field_default_cfg ctx ev "key" ev.epos MGet WithType.value) ev.epos in - let evalue = Calls.acc_get ctx (type_field_default_cfg ctx ev "value" ev.epos MGet WithType.value) ev.epos in + let ekey = Calls.acc_get ctx (type_field_default_cfg ctx ev "key" ev.epos MGet WithType.value) in + let evalue = Calls.acc_get ctx (type_field_default_cfg ctx ev "value" ev.epos MGet WithType.value) in let vkey = add_local_with_origin ctx TVOForVariable ikey ekey.etype pkey in let vvalue = add_local_with_origin ctx TVOForVariable ivalue evalue.etype pvalue in let e2 = type_expr ctx e2 NoValue in diff --git a/src/typing/functionArguments.ml b/src/typing/functionArguments.ml index 5de946ec..1ad58015 100644 --- a/src/typing/functionArguments.ml +++ b/src/typing/functionArguments.ml @@ -4,21 +4,6 @@ open Type open Typecore open Error -let type_opt ctx is_core_api is_abstract_method p t = - let c = ctx.curclass in - match t with - | None when (has_class_flag c CExtern) || (has_class_flag c CInterface) -> - display_error ctx "Type required for extern classes and interfaces" p; - t_dynamic - | None when is_core_api -> - display_error ctx "Type required for core api classes" p; - t_dynamic - | None when is_abstract_method -> - display_error ctx "Type required for abstract functions" p; - t_dynamic - | _ -> - Typeload.load_type_hint ctx p t - let type_function_arg ctx t e opt p = (* TODO https://github.com/HaxeFoundation/haxe/issues/8461 *) (* delay ctx PTypeField (fun() -> @@ -38,7 +23,7 @@ let type_function_arg_value ctx t c do_display = | Some e -> let p = pos e in let e = if do_display then Display.ExprPreprocessing.process_expr ctx.com e else e in - let e = ctx.g.do_optimize ctx (type_expr ctx e (WithType.with_type t)) in + let e = Optimizer.reduce_expression ctx (type_expr ctx e (WithType.with_type t)) in unify ctx e.etype t p; let rec loop e = match e.eexpr with | TConst _ -> Some e @@ -46,23 +31,23 @@ let type_function_arg_value ctx t c do_display = | TField({eexpr = TTypeExpr _},FStatic({cl_kind = KAbstractImpl a},cf)) when a.a_enum && has_class_field_flag cf CfEnum -> Some e | TCast(e,None) -> loop e | _ -> - if ctx.com.display.dms_kind = DMNone || ctx.com.display.dms_inline && ctx.com.display.dms_error_policy = EPCollect then - display_error ctx "Parameter default value should be constant" p; + if ctx.com.display.dms_kind = DMNone || Common.is_diagnostics ctx.com then + Common.display_error ctx.com "Default argument value should be constant" p; None in loop e class function_arguments (ctx : typer) - (type_arg : bool -> type_hint option -> pos -> Type.t) + (type_arg : int -> bool -> type_hint option -> pos -> Type.t) (is_extern : bool) (do_display : bool) (abstract_this : Type.t option) (syntax : (placed_name * bool * metadata * type_hint option * expr option) list) = let with_default = - let l = List.map (fun ((name,pn),opt,m,t,eo) -> - let t = type_arg opt t pn in + let l = List.mapi (fun i ((name,pn),opt,_,t,eo) -> + let t = type_arg i opt t pn in let t,eo = type_function_arg ctx t eo opt pn in (name,eo,t) ) syntax in @@ -91,9 +76,9 @@ object(self) method private check_rest (is_last : bool) (eo : expr option) (opt : bool) (t : Type.t) (pn : pos) = if ExtType.is_rest (follow t) then begin - if opt then error "Rest argument cannot be optional" pn; - begin match eo with None -> () | Some (_,p) -> error "Rest argument cannot have default value" p end; - if not is_last then error "Rest should only be used for the last function argument" pn; + if opt then typing_error "Rest argument cannot be optional" pn; + begin match eo with None -> () | Some (_,p) -> typing_error "Rest argument cannot have default value" p end; + if not is_last then typing_error "Rest should only be used for the last function argument" pn; end (* Returns the `(tvar * texpr option) list` for `tf_args`. Also checks the validity of argument names and whether or not @@ -102,21 +87,21 @@ object(self) | Some l -> l | None -> - let make_local name t meta pn = - let v = alloc_var (VUser TVOArgument) name t pn in + let make_local name kind t meta pn = + let v = alloc_var kind name t pn in v.v_meta <- v.v_meta @ meta; v in let rec loop acc is_abstract_this syntax typed = match syntax,typed with | syntax,(name,_,t) :: typed when is_abstract_this -> - let v = make_local name t [] null_pos in + let v = make_local name VAbstractThis t [] null_pos in v.v_meta <- (Meta.This,[],null_pos) :: v.v_meta; loop ((v,None) :: acc) false syntax typed | ((_,pn),opt,m,_,_) :: syntax,(name,eo,t) :: typed -> delay ctx PTypeField (fun() -> self#check_rest (typed = []) eo opt t pn); if not is_extern then check_local_variable_name ctx name TVOArgument pn; let eo = type_function_arg_value ctx t eo do_display in - let v = make_local name t m pn in + let v = make_local name (VUser TVOArgument) t m pn in if do_display && DisplayPosition.display_position#enclosed_in pn then DisplayEmitter.display_variable ctx v pn; loop ((v,eo) :: acc) false syntax typed @@ -150,4 +135,4 @@ object(self) List.iter (fun (v,_) -> ctx.locals <- PMap.add v.v_name v ctx.locals ) self#for_expr -end \ No newline at end of file +end diff --git a/src/typing/generic.ml b/src/typing/generic.ml index 330878bd..ba94b7bb 100644 --- a/src/typing/generic.ml +++ b/src/typing/generic.ml @@ -27,14 +27,14 @@ let make_generic ctx ps pt p = let rec loop l1 l2 = match l1, l2 with | [] , [] -> [] - | (x,TLazy f) :: l1, _ -> loop ((x,lazy_type f) :: l1) l2 - | (_,t1) :: l1 , t2 :: l2 -> + | ({ttp_type=TLazy f} as tp) :: l1, _ -> loop ({tp with ttp_type=lazy_type f} :: l1) l2 + | tp1 :: l1 , t2 :: l2 -> let t,eo = generic_check_const_expr ctx t2 in - (t1,(t,eo)) :: loop l1 l2 + (tp1.ttp_type,(t,eo)) :: loop l1 l2 | _ -> die "" __LOC__ in let name = - String.concat "_" (List.map2 (fun (s,_) t -> + String.concat "_" (List.map2 (fun {ttp_name=s} t -> let rec subst s = "_" ^ string_of_int (Char.code (String.get (Str.matched_string s) 0)) ^ "_" in let ident_safe = Str.global_substitute (Str.regexp "[^a-zA-Z0-9_]") subst in let s_type_path_underscore (p,s) = match p with [] -> s | _ -> String.concat "_" p ^ "_" ^ s in @@ -111,7 +111,7 @@ let generic_substitute_expr gctx e = let fa = try quick_field t cf.cf_name with Not_found -> - error (Printf.sprintf "Type %s has no field %s (possible typing order issue)" (s_type (print_context()) t) cf.cf_name) e.epos + typing_error (Printf.sprintf "Type %s has no field %s (possible typing order issue)" (s_type (print_context()) t) cf.cf_name) e.epos in build_expr {e with eexpr = TField(e1,fa)} end; @@ -128,7 +128,7 @@ let generic_substitute_expr gctx e = let eo = loop gctx.subst in begin match eo with | Some e -> e - | None -> error "Only Const type parameters can be used as value" e.epos + | None -> typing_error "Only Const type parameters can be used as value" e.epos end with Not_found -> e @@ -155,9 +155,9 @@ let static_method_container gctx c cf p = let t = Typeload.load_instance ctx (mk_type_path (pack,name),p) true in match t with | TInst(cg,_) -> cg - | _ -> error ("Cannot specialize @:generic static method because the generated type name is already used: " ^ name) p - with Error(Module_not_found path,_) when path = (pack,name) -> - let m = (try Hashtbl.find ctx.g.modules (Hashtbl.find ctx.g.types_module c.cl_path) with Not_found -> die "" __LOC__) in + | _ -> typing_error ("Cannot specialize @:generic static method because the generated type name is already used: " ^ name) p + with Error(Module_not_found path,_,_) when path = (pack,name) -> + let m = (try ctx.com.module_lut#find (ctx.com.type_to_module#find c.cl_path) with Not_found -> die "" __LOC__) in let mg = { m_id = alloc_mid(); m_path = (pack,name); @@ -168,12 +168,45 @@ let static_method_container gctx c cf p = gctx.mg <- Some mg; let cg = mk_class mg (pack,name) c.cl_pos c.cl_name_pos in mg.m_types <- [TClassDecl cg]; - Hashtbl.add ctx.g.modules mg.m_path mg; + ctx.com.module_lut#add mg.m_path mg; add_dependency mg m; add_dependency ctx.m.curmod mg; cg -let rec build_generic ctx c p tl = +let set_type_parameter_dependencies mg tl = + (* ensure that type parameters are set in dependencies *) + let dep_stack = ref [] in + let rec loop t = + if not (List.memq t !dep_stack) then begin + dep_stack := t :: !dep_stack; + match t with + | TInst (c,tl) -> add_dep c.cl_module tl + | TEnum (e,tl) -> add_dep e.e_module tl + | TType (t,tl) -> add_dep t.t_module tl + | TAbstract (a,tl) -> add_dep a.a_module tl + | TMono r -> + (match r.tm_type with + | None -> () + | Some t -> loop t) + | TLazy f -> + loop (lazy_type f); + | TDynamic None -> + () + | TDynamic (Some t2) -> + loop t2 + | TAnon a -> + PMap.iter (fun _ f -> loop f.cf_type) a.a_fields + | TFun (args,ret) -> + List.iter (fun (_,_,t) -> loop t) args; + loop ret + end + and add_dep m tl = + add_dependency mg m; + List.iter loop tl + in + List.iter loop tl + +let rec build_generic_class ctx c p tl = let pack = fst c.cl_path in let recurse = ref false in let rec check_recursive t = @@ -182,7 +215,7 @@ let rec build_generic ctx c p tl = (match c2.cl_kind with | KTypeParameter tl -> if not (TypeloadCheck.is_generic_parameter ctx c2) && has_ctor_constraint c2 then - error "Type parameters with a constructor cannot be used non-generically" p; + typing_error "Type parameters with a constructor cannot be used non-generically" p; recurse := true | _ -> ()); List.iter check_recursive tl; @@ -199,10 +232,9 @@ let rec build_generic ctx c p tl = let t = Typeload.load_instance ctx (mk_type_path (pack,name),p) false in match t with | TInst({ cl_kind = KGenericInstance (csup,_) },_) when c == csup -> t - | _ -> error ("Cannot specialize @:generic because the generated type name is already used: " ^ name) p - with Error(Module_not_found path,_) when path = (pack,name) -> - let m = (try Hashtbl.find ctx.g.modules (Hashtbl.find ctx.g.types_module c.cl_path) with Not_found -> die "" __LOC__) in - (* let ctx = { ctx with m = { ctx.m with module_types = m.m_types @ ctx.m.module_types } } in *) + | _ -> typing_error ("Cannot specialize @:generic because the generated type name is already used: " ^ name) p + with Error(Module_not_found path,_,_) when path = (pack,name) -> + let m = (try ctx.com.module_lut#find (ctx.com.type_to_module#find c.cl_path) with Not_found -> die "" __LOC__) in ignore(c.cl_build()); (* make sure the super class is already setup *) let mg = { m_id = alloc_mid(); @@ -213,56 +245,44 @@ let rec build_generic ctx c p tl = } in gctx.mg <- Some mg; let cg = mk_class mg (pack,name) c.cl_pos c.cl_name_pos in + cg.cl_meta <- List.filter (fun (m,_,_) -> match m with + | Meta.Access | Allow + | Final + | Hack + | Internal + | Keep + | NoClosure | NullSafety + | Pure + | Struct | StructInit + | Using -> + true + | _ -> + false + ) c.cl_meta; + cg.cl_meta <- (Meta.NoDoc,[],null_pos) :: cg.cl_meta; mg.m_types <- [TClassDecl cg]; - Hashtbl.add ctx.g.modules mg.m_path mg; + ctx.com.module_lut#add mg.m_path mg; add_dependency mg m; add_dependency ctx.m.curmod mg; - (* ensure that type parameters are set in dependencies *) - let dep_stack = ref [] in - let rec loop t = - if not (List.memq t !dep_stack) then begin - dep_stack := t :: !dep_stack; - match t with - | TInst (c,tl) -> add_dep c.cl_module tl - | TEnum (e,tl) -> add_dep e.e_module tl - | TType (t,tl) -> add_dep t.t_module tl - | TAbstract (a,tl) -> add_dep a.a_module tl - | TMono r -> - (match r.tm_type with - | None -> () - | Some t -> loop t) - | TLazy f -> - loop (lazy_type f); - | TDynamic t2 -> - if t == t2 then () else loop t2 - | TAnon a -> - PMap.iter (fun _ f -> loop f.cf_type) a.a_fields - | TFun (args,ret) -> - List.iter (fun (_,_,t) -> loop t) args; - loop ret - end - and add_dep m tl = - add_dependency mg m; - List.iter loop tl - in - List.iter loop tl; + set_type_parameter_dependencies mg tl; let build_field cf_old = (* We have to clone the type parameters (issue #4672). We cannot substitute the constraints immediately because we need the full substitution list first. *) - let param_subst,params = List.fold_left (fun (subst,params) (s,t) -> match follow t with + let param_subst,params = List.fold_left (fun (subst,params) tp -> match follow tp.ttp_type with | TInst(c,tl) as t -> let t2 = TInst({c with cl_module = mg;},tl) in - (t,(t2,None)) :: subst,(s,t2) :: params + (t,(t2,None)) :: subst,({tp with ttp_type=t2}) :: params | _ -> die "" __LOC__ ) ([],[]) cf_old.cf_params in let gctx = {gctx with subst = param_subst @ gctx.subst} in let cf_new = {cf_old with cf_pos = cf_old.cf_pos} in (* copy *) + remove_class_field_flag cf_new CfPostProcessed; (* Type parameter constraints are substituted here. *) - cf_new.cf_params <- List.rev_map (fun (s,t) -> match follow t with + cf_new.cf_params <- List.rev_map (fun tp -> match follow tp.ttp_type with | TInst({cl_kind = KTypeParameter tl1} as c,_) -> let tl1 = List.map (generic_substitute_type gctx) tl1 in c.cl_kind <- KTypeParameter tl1; - s,t + tp (* TPTODO: weird mapping *) | _ -> die "" __LOC__ ) params; let f () = @@ -271,16 +291,16 @@ let rec build_generic ctx c p tl = begin try (match cf_old.cf_expr with | None -> begin match cf_old.cf_kind with - | Method _ when not (has_class_flag c CInterface) && not (has_class_flag c CExtern) -> - display_error ctx (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name) cf_new.cf_pos; - display_error ctx (Printf.sprintf "While building %s" (s_type_path cg.cl_path)) p; + | Method _ when not (has_class_flag c CInterface) && not (has_class_flag c CExtern) && not (has_class_field_flag cf_old CfAbstract) -> + display_error ctx.com (Printf.sprintf "Field %s has no expression (possible typing order issue)" cf_new.cf_name) cf_new.cf_pos; + display_error ctx.com (Printf.sprintf "While building %s" (s_type_path cg.cl_path)) p; | _ -> () end | Some e -> cf_new.cf_expr <- Some (generic_substitute_expr gctx e) ) with Unify_error l -> - error (error_msg (Unify l)) cf_new.cf_pos + located_typing_error (error_msg cf_new.cf_pos (Unify l)) end; t in @@ -288,17 +308,17 @@ let rec build_generic ctx c p tl = let t = spawn_monomorph ctx p in r := lazy_processing (fun() -> t); let t0 = f() in - unify_raise ctx t0 t p; + unify_raise t0 t p; link_dynamic t0 t; t - ) "build_generic" in + ) "build_generic_class" in cf_new.cf_type <- TLazy r; cf_new in - if c.cl_init <> None then error "This class can't be generic" p; + if c.cl_init <> None then typing_error "This class can't be generic" p; List.iter (fun cf -> match cf.cf_kind with - | Method MethMacro when not ctx.in_macro -> () - | _ -> error "A generic class can't have static fields" cf.cf_pos + | Method MethMacro when not ctx.com.is_macro_context -> () + | _ -> typing_error "A generic class can't have static fields" cf.cf_pos ) c.cl_ordered_statics; cg.cl_super <- (match c.cl_super with | None -> None @@ -307,21 +327,20 @@ let rec build_generic ctx c p tl = let cs,pl = TypeloadCheck.Inheritance.check_extends ctx c ts p in match cs.cl_kind with | KGeneric -> - (match build_generic ctx cs p pl with + (match build_generic_class ctx cs p pl with | TInst (cs,pl) -> Some (cs,pl) | _ -> die "" __LOC__) | _ -> Some(cs,pl) ); TypeloadFunction.add_constructor ctx cg false p; cg.cl_kind <- KGenericInstance (c,tl); - cg.cl_meta <- (Meta.NoDoc,[],null_pos) :: cg.cl_meta; - if has_meta Meta.Keep c.cl_meta then cg.cl_meta <- (Meta.Keep,[],null_pos) :: cg.cl_meta; if (has_class_flag c CInterface) then add_class_flag cg CInterface; + if (has_class_flag c CAbstract) then add_class_flag cg CAbstract; cg.cl_constructor <- (match cg.cl_constructor, c.cl_constructor, c.cl_super with | _, Some cf, _ -> Some (build_field cf) | Some ctor, _, _ -> Some ctor | None, None, None -> None - | _ -> error "Please define a constructor for this class in order to use it as generic" c.cl_pos + | _ -> typing_error "Please define a constructor for this class in order to use it as generic" c.cl_pos ); cg.cl_implements <- List.map (fun (i,tl) -> (match follow (generic_substitute_type gctx (TInst (i, List.map (generic_substitute_type gctx) tl))) with @@ -341,16 +360,15 @@ let rec build_generic ctx c p tl = TInst (cg,[]) end -let type_generic_function ctx fa el_typed el with_type p = +let type_generic_function ctx fa fcc with_type p = let c,stat = match fa.fa_host with | FHInstance(c,tl) -> c,false | FHStatic c -> c,true | FHAbstract(a,tl,c) -> c,true | _ -> die "" __LOC__ in - let cf = fa.fa_field in - if cf.cf_params = [] then error "Function has no type parameters and cannot be generic" p; - let fcc = CallUnification.unify_field_call ctx fa el_typed el p false in + let cf = fcc.fc_field in + if cf.cf_params = [] then typing_error "Function has no type parameters and cannot be generic" p; begin match with_type with | WithType.WithType(t,_) -> unify ctx fcc.fc_ret t p | _ -> () @@ -365,10 +383,10 @@ let type_generic_function ctx fa el_typed el with_type p = let gctx = make_generic ctx cf.cf_params monos p in let name = cf.cf_name ^ "_" ^ gctx.name in let unify_existing_field tcf pcf = try - unify_raise ctx tcf fcc.fc_type p - with Error(Unify _,_) as err -> - display_error ctx ("Cannot create field " ^ name ^ " due to type mismatch") p; - display_error ctx (compl_msg "Conflicting field was defined here") pcf; + unify_raise tcf fcc.fc_type p + with Error(Unify _,_,depth) as err -> + display_error ~depth ctx.com ("Cannot create field " ^ name ^ " due to type mismatch") p; + display_error ~depth:(depth+1) ctx.com (compl_msg "Conflicting field was defined here") pcf; raise err in let fa = try @@ -395,14 +413,14 @@ let type_generic_function ctx fa el_typed el with_type p = ignore(follow cf.cf_type); let rec check e = match e.eexpr with | TNew({cl_kind = KTypeParameter _} as c,_,_) when not (TypeloadCheck.is_generic_parameter ctx c) -> - display_error ctx "Only generic type parameters can be constructed" e.epos; - display_error ctx "While specializing this call" p; + display_error ctx.com "Only generic type parameters can be constructed" e.epos; + display_error ctx.com "While specializing this call" p; | _ -> Type.iter check e in cf2.cf_expr <- (match cf.cf_expr with | None -> - display_error ctx "Recursive @:generic function" p; None; + display_error ctx.com "Recursive @:generic function" p; None; | Some e -> let e = generic_substitute_expr gctx e in check e; @@ -418,6 +436,7 @@ let type_generic_function ctx fa el_typed el with_type p = if stat then begin if Meta.has Meta.GenericClassPerMethod c.cl_meta then begin let c = static_method_container gctx c cf p in + set_type_parameter_dependencies c.cl_module monos; let cf2 = try let cf2 = PMap.find cf.cf_name c.cl_statics in unify_existing_field cf2.cf_type cf2.cf_pos; @@ -431,6 +450,7 @@ let type_generic_function ctx fa el_typed el with_type p = in {fa with fa_host = FHStatic c;fa_field = cf2;fa_on = Builder.make_static_this c p} end else begin + set_type_parameter_dependencies c.cl_module monos; let cf2 = mk_cf2 name in c.cl_statics <- PMap.add cf2.cf_name cf2 c.cl_statics; c.cl_ordered_statics <- cf2 :: c.cl_ordered_statics; @@ -438,6 +458,7 @@ let type_generic_function ctx fa el_typed el with_type p = {fa with fa_field = cf2} end end else begin + set_type_parameter_dependencies c.cl_module monos; let cf2 = mk_cf2 name in if has_class_field_flag cf CfOverride then add_class_field_flag cf2 CfOverride; c.cl_fields <- PMap.add cf2.cf_name cf2 c.cl_fields; @@ -446,10 +467,10 @@ let type_generic_function ctx fa el_typed el with_type p = {fa with fa_field = cf2} end in - let e = FieldAccess.get_field_expr fa FCall in - make_call ctx e el fcc.fc_ret p + let dispatch = new CallUnification.call_dispatcher ctx (MCall []) with_type p in + dispatch#field_call fa el [] with Generic_Exception (msg,p) -> - error msg p) + typing_error msg p) ;; -Typecore.type_generic_function_ref := type_generic_function \ No newline at end of file +Typecore.type_generic_function_ref := type_generic_function diff --git a/src/typing/instanceBuilder.ml b/src/typing/instanceBuilder.ml index f2f11dd9..2594f1fd 100644 --- a/src/typing/instanceBuilder.ml +++ b/src/typing/instanceBuilder.ml @@ -7,9 +7,9 @@ open Error let get_macro_path ctx e args p = let rec loop e = match fst e with - | EField (e,f) -> f :: loop e + | EField (e,f,_) -> f :: loop e | EConst (Ident i) -> [i] - | _ -> error "Invalid macro call" p + | _ -> typing_error "Invalid macro call" p in let path = match e with | (EConst(Ident i)),_ -> @@ -19,7 +19,7 @@ let get_macro_path ctx e args p = with Not_found -> try (t_infos (let path,_,_ = PMap.find i ctx.m.module_globals in path)).mt_path with Not_found -> - error "Invalid macro call" p + typing_error "Invalid macro call" p in i :: (snd path) :: (fst path) | _ -> @@ -27,7 +27,7 @@ let get_macro_path ctx e args p = in (match path with | meth :: cl :: path -> (List.rev path,cl), meth, args - | _ -> error "Invalid macro call" p) + | _ -> typing_error "Invalid macro call" p) let build_macro_type ctx pl p = let path, field, args = (match pl with @@ -35,7 +35,7 @@ let build_macro_type ctx pl p = | [TInst ({ cl_kind = KExpr (EArrayDecl [ECall (e,args),_],_) },_)] -> get_macro_path ctx e args p | _ -> - error "MacroType requires a single expression call parameter" p + typing_error "MacroType requires a single expression call parameter" p ) in let old = ctx.ret in let t = (match ctx.g.do_macro ctx MMacroType path field args p with @@ -49,11 +49,11 @@ let build_macro_build ctx c pl cfl p = let path, field, args = let build_expr = try Meta.get Meta.GenericBuild c.cl_meta - with Not_found -> error ((s_type_path c.cl_path) ^ " is missing @:genericBuild meta. Was it removed by a macro?") p + with Not_found -> typing_error ((s_type_path c.cl_path) ^ " is missing @:genericBuild meta. Was it removed by a macro?") p in match build_expr with | _,[ECall(e,args),_],_ -> get_macro_path ctx e args p - | _ -> error "genericBuild requires a single expression call parameter" p + | _ -> typing_error "genericBuild requires a single expression call parameter" p in let old = ctx.ret,ctx.get_build_infos in ctx.get_build_infos <- (fun() -> Some (TClassDecl c, pl, cfl)); @@ -77,7 +77,7 @@ let build_instance ctx mtype p = let t = spawn_monomorph ctx p in r := lazy_processing (fun() -> t); let tf = (f()) in - unify_raise ctx tf t p; + unify_raise tf t p; link_dynamic t tf; (match tf with | TInst (c, _) -> ignore(c.cl_build()) @@ -91,7 +91,7 @@ let build_instance ctx mtype p = let ft = (fun pl -> match c.cl_kind with | KGeneric -> - build (fun () -> Generic.build_generic ctx c p pl) "build_generic" + build (fun () -> Generic.build_generic_class ctx c p pl) "build_generic" | KMacroType -> build (fun () -> build_macro_type ctx pl p) "macro_type" | KGenericBuild cfl -> diff --git a/src/typing/macroContext.ml b/src/typing/macroContext.ml index fa438420..a1e2f6e3 100644 --- a/src/typing/macroContext.ml +++ b/src/typing/macroContext.ml @@ -53,15 +53,7 @@ let safe_decode ctx v expected t p f = let errors = Interp.handle_decoding_error (output_string ch) v t in List.iter (fun (s,i) -> Printf.fprintf ch "\nline %i: %s" i s) (List.rev errors); close_out ch; - error (Printf.sprintf "Expected %s but got %s (see %s.txt for details)" expected (Interp.value_string v) (String.concat "/" path)) p - -let get_next_stored_typed_expr_id = - let uid = ref 0 in - (fun() -> incr uid; !uid) - -let get_stored_typed_expr com id = - let e = PMap.find id com.stored_typed_exprs in - Texpr.duplicate_tvars e + typing_error (Printf.sprintf "Expected %s but got %s (see %s.txt for details)" expected (Interp.value_string v) (String.concat "/" path)) p let get_type_patch ctx t sub = let new_patch() = @@ -91,32 +83,42 @@ let macro_timer ctx l = let typing_timer ctx need_type f = let t = Timer.timer ["typing"] in - let old = ctx.com.error and oldp = ctx.pass and oldlocals = ctx.locals in + let old = ctx.com.located_error and oldp = ctx.pass and oldlocals = ctx.locals in + let restore_report_mode = disable_report_mode ctx.com in (* disable resumable errors... unless we are in display mode (we want to reach point of completion) *) - (*if ctx.com.display = DMNone then ctx.com.error <- (fun e p -> raise (Error(Custom e,p)));*) (* TODO: review this... *) - ctx.com.error <- (fun e p -> raise (Error(Custom e,p))); + (*if ctx.com.display = DMNone then ctx.com.located_error <- (fun e p -> raise (Error(Custom e,p)));*) (* TODO: review this... *) + let rec located_to_error = function + | Message (e,p) -> (Custom e,p) + | Stack stack -> (Stack (List.map located_to_error stack),null_pos) + in + + ctx.com.located_error <- (fun ?(depth=0) msg -> + let (e,p) = located_to_error msg in + raise (Error (e,p,depth))); + if need_type && ctx.pass < PTypeField then begin ctx.pass <- PTypeField; flush_pass ctx PBuildClass "typing_timer"; end; let exit() = t(); - ctx.com.error <- old; + ctx.com.located_error <- old; ctx.pass <- oldp; ctx.locals <- oldlocals; + restore_report_mode (); in try let r = f() in exit(); r - with Error (ekind,p) -> + with Error (ekind,p,_) -> exit(); - Interp.compiler_error (error_msg ekind) p - | WithTypeError (l,p) -> + Interp.compiler_error (error_msg p ekind) + | WithTypeError (l,p,_) -> exit(); - Interp.compiler_error (error_msg l) p + Interp.compiler_error (error_msg p l) | e -> exit(); raise e @@ -126,22 +128,31 @@ let load_macro_ref : (typer -> bool -> path -> string -> pos -> (typer * ((strin let make_macro_api ctx p = let parse_expr_string s p inl = typing_timer ctx false (fun() -> - match ParserEntry.parse_expr_string ctx.com.defines s p error inl with + match ParserEntry.parse_expr_string ctx.com.defines s p typing_error inl with | ParseSuccess(data,true,_) when inl -> data (* ignore errors when inline-parsing in display file *) | ParseSuccess(data,_,_) -> data | ParseError _ -> raise MacroApi.Invalid_expr) in let parse_metadata s p = try - match ParserEntry.parse_string Grammar.parse_meta ctx.com.defines s null_pos error false with + match ParserEntry.parse_string Grammar.parse_meta ctx.com.defines s null_pos typing_error false with | ParseSuccess(meta,_,_) -> meta - | ParseError(_,_,_) -> error "Malformed metadata string" p + | ParseError(_,_,_) -> typing_error "Malformed metadata string" p with _ -> - error "Malformed metadata string" p + typing_error "Malformed metadata string" p in { MacroApi.pos = p; MacroApi.get_com = (fun() -> ctx.com); + MacroApi.get_macro_stack = (fun () -> + let envs = Interp.call_stack (Interp.get_eval (Interp.get_ctx ())) in + let envs = match envs with + | _ :: envs -> envs (* Skip call to getMacroStack() *) + | _ -> envs + in + List.map (fun (env:Interp.env) -> {pfile = EvalHash.rev_hash env.env_info.pfile;pmin = env.env_leave_pmin; pmax = env.env_leave_pmax}) envs + ); + MacroApi.init_macros_done = (fun () -> ctx.com.stage >= CInitMacrosDone); MacroApi.get_type = (fun s -> typing_timer ctx false (fun() -> let path = parse_path s in @@ -154,7 +165,7 @@ let make_macro_api ctx p = try let m = Some (Typeload.load_instance ctx (tp,p) true) in m - with Error (Module_not_found _,p2) when p == p2 -> + with Error (Module_not_found _,p2,_) when p == p2 -> None ) ); @@ -168,6 +179,13 @@ let make_macro_api ctx p = m ) ); + MacroApi.after_init_macros = (fun f -> + ctx.com.callbacks#add_after_init_macros (fun () -> + let t = macro_timer ctx ["afterInitMacros"] in + f (); + t() + ) + ); MacroApi.after_typing = (fun f -> ctx.com.callbacks#add_after_typing (fun tl -> let t = macro_timer ctx ["afterTyping"] in @@ -200,33 +218,26 @@ let make_macro_api ctx p = ]; ); MacroApi.parse_string = parse_expr_string; + MacroApi.parse = (fun entry s -> + match ParserEntry.parse_string entry ctx.com.defines s null_pos typing_error false with + | ParseSuccess(r,_,_) -> r + | ParseError(_,(msg,p),_) -> Parser.error msg p + ); MacroApi.type_expr = (fun e -> typing_timer ctx true (fun() -> type_expr ctx e WithType.value) ); - MacroApi.type_macro_expr = (fun e -> - let e = typing_timer ctx true (fun() -> type_expr ctx e WithType.value) in - let rec loop e = match e.eexpr with - | TField(_,FStatic(c,({cf_kind = Method _} as cf))) -> ignore(!load_macro_ref ctx false c.cl_path cf.cf_name e.epos) - | _ -> Type.iter loop e - in - loop e; - e - ); MacroApi.flush_context = (fun f -> typing_timer ctx true f ); MacroApi.store_typed_expr = (fun te -> let p = te.epos in - let id = get_next_stored_typed_expr_id() in - ctx.com.stored_typed_exprs <- PMap.add id te ctx.com.stored_typed_exprs; - let eid = (EConst (Int (string_of_int id))), p in - (EMeta ((Meta.StoredTypedExpr,[],p), eid)), p + snd (Typecore.store_typed_expr ctx.com te p) ); MacroApi.allow_package = (fun v -> Common.allow_package ctx.com v); MacroApi.type_patch = (fun t f s v -> typing_timer ctx false (fun() -> let v = (match v with None -> None | Some s -> - match ParserEntry.parse_string Grammar.parse_complex_type ctx.com.defines s null_pos error false with + match ParserEntry.parse_string Grammar.parse_complex_type ctx.com.defines s null_pos typing_error false with | ParseSuccess((ct,_),_,_) -> Some ct | ParseError(_,(msg,p),_) -> Parser.error msg p (* p is null_pos, but we don't have anything else here... *) ) in @@ -282,7 +293,7 @@ let make_macro_api ctx p = List.map fst ctx.m.module_using; ); MacroApi.get_local_imports = (fun() -> - ctx.m.module_imports; + ctx.m.import_statements; ); MacroApi.get_local_vars = (fun () -> ctx.locals; @@ -310,10 +321,11 @@ let make_macro_api ctx p = let mnew = TypeloadModule.type_module ctx ~dont_check_path:(has_native_meta) m (Path.UniqueKey.lazy_path mdep.m_extra.m_file) [tdef,pos] pos in mnew.m_extra.m_kind <- if is_macro then MMacro else MFake; add_dependency mnew mdep; + ctx.com.module_nonexistent_lut#clear; in add false ctx; (* if we are adding a class which has a macro field, we also have to add it to the macro context (issue #1497) *) - if not ctx.in_macro then match tdef with + if not ctx.com.is_macro_context then match tdef with | EClass c when List.exists (fun cff -> (Meta.has Meta.Macro cff.cff_meta || List.mem_assoc AMacro cff.cff_access)) c.d_data -> add true mctx | _ -> @@ -333,12 +345,13 @@ let make_macro_api ctx p = let types = imports @ usings @ types in let mpath = Ast.parse_path m in begin try - let m = Hashtbl.find ctx.g.modules mpath in + let m = ctx.com.module_lut#find mpath in ignore(TypeloadModule.type_types_into_module ctx m types pos) with Not_found -> let mnew = TypeloadModule.type_module ctx mpath (Path.UniqueKey.lazy_path ctx.m.curmod.m_extra.m_file) types pos in mnew.m_extra.m_kind <- MFake; add_dependency mnew ctx.m.curmod; + ctx.com.module_nonexistent_lut#clear; end ); MacroApi.module_dependency = (fun mpath file -> @@ -365,7 +378,7 @@ let make_macro_api ctx p = try ignore(AbstractCast.cast_or_unify_raise ctx t e p); true - with Error (Unify _,_) -> + with Error (Unify _,_,_) -> false ) ); @@ -379,28 +392,74 @@ let make_macro_api ctx p = MacroApi.add_module_check_policy = (fun sl il b i -> let add ctx = ctx.g.module_check_policies <- (List.fold_left (fun acc s -> (ExtString.String.nsplit s ".",List.map Obj.magic il,b) :: acc) ctx.g.module_check_policies sl); - Hashtbl.iter (fun _ m -> m.m_extra.m_check_policy <- TypeloadModule.get_policy ctx m.m_path) ctx.g.modules; + ctx.com.module_lut#iter (fun _ m -> m.m_extra.m_check_policy <- TypeloadModule.get_policy ctx.g m.m_path); in let add_macro ctx = match ctx.g.macros with | None -> () | Some(_,mctx) -> add mctx; in + let open CompilationCache in match Obj.magic i with - | CompilationServer.NormalContext -> add ctx - | CompilationServer.MacroContext -> add_macro ctx - | CompilationServer.NormalAndMacroContext -> add ctx; add_macro ctx; + | NormalContext -> add ctx + | MacroContext -> add_macro ctx + | NormalAndMacroContext -> add ctx; add_macro ctx; ); + MacroApi.register_define = (fun s data -> Define.register_user_define ctx.com.user_defines s data); + MacroApi.register_metadata = (fun s data -> Meta.register_user_meta ctx.com.user_metas s data); MacroApi.decode_expr = Interp.decode_expr; MacroApi.encode_expr = Interp.encode_expr; MacroApi.encode_ctype = Interp.encode_ctype; MacroApi.decode_type = Interp.decode_type; + MacroApi.display_error = display_error ctx.com; + MacroApi.with_imports = (fun imports usings f -> + let old_globals = ctx.m.module_globals in + let old_imports = ctx.m.module_imports in + let old_using = ctx.m.module_using in + let run () = + let context_init = new TypeloadFields.context_init in + List.iter (fun (path,mode) -> + ImportHandling.init_import ctx context_init path mode null_pos + ) imports; + List.iter (fun path -> + ImportHandling.init_using ctx context_init path null_pos + ) usings; + context_init#run; + f() + in + let restore () = + ctx.m.module_globals <- old_globals; + ctx.m.module_imports <- old_imports; + ctx.m.module_using <- old_using; + in + Std.finally restore run () + ); + MacroApi.with_options = (fun opts f -> + let old_inline = ctx.allow_inline in + let old_transform = ctx.allow_transform in + (match opts.opt_inlining with + | None -> () + | Some v -> ctx.allow_inline <- v); + (match opts.opt_transform with + | None -> () + | Some v -> ctx.allow_transform <- v); + let restore() = + ctx.allow_inline <- old_inline; + ctx.allow_transform <- old_transform; + in + Std.finally restore f () + ); + MacroApi.info = (fun ?(depth=0) msg p -> + ctx.com.info ~depth msg p + ); + MacroApi.warning = (fun ?(depth=0) w msg p -> + warning ~depth ctx w msg p + ); } let rec init_macro_interp ctx mctx mint = let p = null_pos in ignore(TypeloadModule.load_module mctx (["haxe";"macro"],"Expr") p); ignore(TypeloadModule.load_module mctx (["haxe";"macro"],"Type") p); - flush_macro_context mint ctx; Interp.init mint; if !macro_enable_cache && not (Common.defined mctx.com Define.NoMacroCache) then begin macro_interp_cache := Some mint; @@ -409,8 +468,8 @@ let rec init_macro_interp ctx mctx mint = and flush_macro_context mint ctx = let t = macro_timer ctx ["flush"] in let mctx = (match ctx.g.macros with None -> die "" __LOC__ | Some (_,mctx) -> mctx) in - ctx.g.do_finalize mctx; - let _, types, modules = ctx.g.do_generate mctx in + Finalization.finalize mctx; + let _, types, modules = Finalization.generate mctx in mctx.com.types <- types; mctx.com.Common.modules <- modules; (* we should maybe ensure that all filters in Main are applied. Not urgent atm *) @@ -425,6 +484,9 @@ and flush_macro_context mint ctx = let's save the minimal amount of information we need *) let minimal_restore t = + if (t_infos t).mt_module.m_extra.m_processed = 0 then + (t_infos t).mt_module.m_extra.m_processed <- mctx.com.compilation_step; + match t with | TClassDecl c -> let mk_field_restore f = @@ -449,20 +511,19 @@ and flush_macro_context mint ctx = () in let type_filters = [ - Filters.remove_generic_base mctx; + Filters.remove_generic_base; Exceptions.patch_constructors mctx; - Filters.add_field_inits (RenameVars.init mctx.com) mctx; + (fun mt -> Filters.add_field_inits mctx.curclass.cl_path (RenameVars.init mctx.com) mctx.com mt); minimal_restore; - Filters.apply_native_paths mctx + Filters.apply_native_paths ] in let ready = fun t -> Filters.apply_filters_once mctx expr_filters t; List.iter (fun f -> f t) type_filters in (try Interp.add_types mint types ready - with Error (e,p) -> t(); raise (Fatal_error(error_msg e,p))); - t(); - Filters.next_compilation() + with Error (e,p,n) -> t(); raise (Fatal_error(error_msg p e,n))); + t() let create_macro_interp ctx mctx = let com2 = mctx.com in @@ -475,11 +536,11 @@ let create_macro_interp ctx mctx = Interp.do_reuse mint (make_macro_api ctx null_pos); mint, (fun() -> ()) ) in - let on_error = com2.error in - com2.error <- (fun e p -> + let on_error = com2.located_error in + com2.located_error <- (fun ?(depth = 0) msg -> Interp.set_error (Interp.get_ctx()) true; macro_interp_cache := None; - on_error e p + on_error ~depth msg ); let macro = ((fun() -> Interp.select mint), mctx) in ctx.g.macros <- Some macro; @@ -494,12 +555,12 @@ let get_macro_context ctx p = select(); api, ctx | None -> - let com2 = Common.clone ctx.com in + let com2 = Common.clone ctx.com true in ctx.com.get_macros <- (fun() -> Some com2); com2.package_rules <- PMap.empty; com2.main_class <- None; (* Inherit most display settings, but require normal typing. *) - com2.display <- {ctx.com.display with dms_kind = DMNone; dms_display = false; dms_full_typing = true; dms_force_macro_typing = true; dms_inline = true; }; + com2.display <- {ctx.com.display with dms_kind = DMNone; dms_full_typing = true; dms_force_macro_typing = true; dms_inline = true; }; com2.class_path <- List.filter (fun s -> not (ExtString.String.exists s "/_std/")) com2.class_path; let name = platform_name !Globals.macro_platform in com2.class_path <- List.map (fun p -> p ^ name ^ "/_std/") com2.std_path @ com2.class_path; @@ -515,7 +576,7 @@ let get_macro_context ctx p = let load_macro_module ctx cpath display p = let api, mctx = get_macro_context ctx p in - let m = (try Hashtbl.find ctx.g.types_module cpath with Not_found -> cpath) in + let m = (try ctx.com.type_to_module#find cpath with Not_found -> cpath) in (* Temporarily enter display mode while typing the macro. *) let old = mctx.com.display in if display then mctx.com.display <- ctx.com.display; @@ -523,18 +584,18 @@ let load_macro_module ctx cpath display p = api.MacroApi.current_macro_module <- (fun() -> mloaded); mctx.m <- { curmod = mloaded; - module_types = []; + module_imports = []; module_using = []; module_globals = PMap.empty; wildcard_packages = []; - module_imports = []; + import_statements = []; }; mloaded,(fun () -> mctx.com.display <- old) let load_macro' ctx display cpath f p = let api, mctx = get_macro_context ctx p in let mint = Interp.get_ctx() in - let (meth,mloaded) = try Hashtbl.find mctx.com.cached_macros (cpath,f) with Not_found -> + let (meth,mloaded) = try mctx.com.cached_macros#find (cpath,f) with Not_found -> let t = macro_timer ctx ["typing";s_type_path cpath ^ "." ^ f] in let mpath, sub = (match List.rev (fst cpath) with | name :: pack when name.[0] >= 'A' && name.[0] <= 'Z' -> (List.rev pack,name), Some (snd cpath) @@ -543,34 +604,34 @@ let load_macro' ctx display cpath f p = let mloaded,restore = load_macro_module ctx mpath display p in let cl, meth = try - if sub <> None then raise Not_found; + if sub <> None || mloaded.m_path <> cpath then raise Not_found; match mloaded.m_statics with | None -> raise Not_found | Some c -> - mctx.g.do_finalize mctx; + Finalization.finalize mctx; c, PMap.find f c.cl_statics with Not_found -> let name = Option.default (snd mpath) sub in let path = fst mpath, name in - let mt = try List.find (fun t2 -> (t_infos t2).mt_path = path) mloaded.m_types with Not_found -> raise_error (Type_not_found (mloaded.m_path,name,Not_defined)) p in + let mt = try List.find (fun t2 -> (t_infos t2).mt_path = path) mloaded.m_types with Not_found -> raise_typing_error (Type_not_found (mloaded.m_path,name,Not_defined)) p in match mt with | TClassDecl c -> - mctx.g.do_finalize mctx; - c, (try PMap.find f c.cl_statics with Not_found -> error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p) - | _ -> error "Macro should be called on a class" p + Finalization.finalize mctx; + c, (try PMap.find f c.cl_statics with Not_found -> typing_error ("Method " ^ f ^ " not found on class " ^ s_type_path cpath) p) + | _ -> typing_error "Macro should be called on a class" p in api.MacroApi.current_macro_module <- (fun() -> mloaded); - let meth = (match follow meth.cf_type with TFun (args,ret) -> (args,ret,cl,meth),mloaded | _ -> error "Macro call should be a method" p) in + let meth = (match follow meth.cf_type with TFun (args,ret) -> (args,ret,cl,meth),mloaded | _ -> typing_error "Macro call should be a method" p) in restore(); - if not ctx.in_macro then flush_macro_context mint ctx; - Hashtbl.add mctx.com.cached_macros (cpath,f) meth; + if not ctx.com.is_macro_context then flush_macro_context mint ctx; + mctx.com.cached_macros#add (cpath,f) meth; mctx.m <- { curmod = null_module; - module_types = []; + module_imports = []; module_using = []; module_globals = PMap.empty; wildcard_packages = []; - module_imports = []; + import_statements = []; }; t(); meth @@ -629,13 +690,13 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = let cttype = mk_type_path (["haxe";"macro"],"Type") in let ttype = Typeload.load_instance mctx (cttype,p) false in try - unify_raise mctx mret ttype mpos; + unify_raise mret ttype mpos; (* TODO: enable this again in the future *) - (* ctx.com.warning "Returning Type from @:genericBuild macros is deprecated, consider returning ComplexType instead" p; *) - with Error (Unify _,_) -> + (* warning ctx WDeprecated "Returning Type from @:genericBuild macros is deprecated, consider returning ComplexType instead" p; *) + with Error (Unify _,_,_) -> let cttype = mk_type_path ~sub:"ComplexType" (["haxe";"macro"],"Expr") in let ttype = Typeload.load_instance mctx (cttype,p) false in - unify_raise mctx mret ttype mpos; + unify_raise mret ttype mpos; ); (* if the function's last argument is of Array, split the argument list and use [] for unify_call_args @@ -666,10 +727,10 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = force default parameter types to haxe.macro.Expr, and if success allow to pass any value type since it will be encoded *) let eargs = List.map (fun (n,o,t) -> - try unify_raise mctx t expr p; (n, o, t_dynamic), MAExpr - with Error (Unify _,_) -> match follow t with + try unify_raise t expr p; (n, o, t_dynamic), MAExpr + with Error (Unify _,_,_) -> match follow t with | TFun _ -> - (n,o,t_dynamic), MAFunction + (n,o,t), MAFunction | _ -> (n,o,t), MAOther ) margs in @@ -681,6 +742,11 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = let constants = List.map (fun e -> let p = snd e in let e = + let rec is_function e = match fst e with + | EFunction _ -> true + | EParenthesis e1 | ECast(e1,_) | EMeta(_,e1) -> is_function e1 + | _ -> false + in if Texpr.is_constant_value ctx.com.basic e then (* temporarily disable format strings processing for macro call argument typing since we want to pass raw constants *) let rec loop e = @@ -689,16 +755,20 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = | _ -> Ast.map_expr loop e in loop e + else if is_function e then + (* If we pass a function expression we don't want to type it as part of `unify_call_args` because that result just gets + discarded. Use null here so it passes, then do the actual typing in the MAFunction part below. *) + (EConst (Ident "null"),p) else (* if it's not a constant, let's make something that is typed as haxe.macro.Expr - for nice error reporting *) (ECheckType ((EConst (Ident "null"),p), (CTPath ctexpr,p)), p) in (* let's track the index by doing [e][index] (we will keep the expression type this way) *) incr index; - (EArray ((EArrayDecl [e],p),(EConst (Int (string_of_int (!index))),p)),p) + (EArray ((EArrayDecl [e],p),(EConst (Int (string_of_int (!index), None)),p)),p) ) el in let elt = fst (CallUnification.unify_call_args mctx constants (List.map fst eargs) t_dynamic p false false false) in - List.map2 (fun (_,mct) e -> + List.map2 (fun ((n,_,t),mct) e -> let e, et = (match e.eexpr with (* get back our index and real expression *) | TArray ({ eexpr = TArrayDecl [e] }, { eexpr = TConst (TInt index) }) -> List.nth el (Int32.to_int index), e @@ -711,7 +781,8 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = | MAExpr -> Interp.encode_expr e | MAFunction -> - let e = ictx.Interp.curapi.MacroApi.type_macro_expr e in + let e = type_expr mctx e (WithType.with_argument t n) in + unify mctx e.etype t e.epos; begin match Interp.eval_expr ictx e with | Some v -> v | None -> Interp.vnull @@ -759,7 +830,7 @@ let type_macro ctx mode cpath f (el:Ast.expr list) p = in safe_decode ctx v expected mret p process in - let e = if ctx.in_macro then + let e = if ctx.com.is_macro_context then Some (EThrow((EConst(String("macro-in-macro",SDoubleQuotes))),p),p) else call() @@ -770,36 +841,36 @@ let call_macro ctx path meth args p = let mctx, (margs,_,mclass,mfield), call = load_macro ctx false path meth p in mctx.curclass <- null_class; let el, _ = CallUnification.unify_call_args mctx args margs t_dynamic p false false false in - call (List.map (fun e -> try Interp.make_const e with Exit -> error "Parameter should be a constant" e.epos) el) + call (List.map (fun e -> try Interp.make_const e with Exit -> typing_error "Argument should be a constant" e.epos) el) let call_init_macro ctx e = let p = { pfile = "--macro " ^ e; pmin = -1; pmax = -1 } in let e = try - if String.get e (String.length e - 1) = ';' then error "Unexpected ;" p; - begin match ParserEntry.parse_expr_string ctx.com.defines e p error false with + if String.get e (String.length e - 1) = ';' then typing_error "Unexpected ;" p; + begin match ParserEntry.parse_expr_string ctx.com.defines e p typing_error false with | ParseSuccess(data,_,_) -> data | ParseError(_,(msg,p),_) -> (Parser.error msg p) end with err -> - display_error ctx ("Could not parse `" ^ e ^ "`") p; + display_error ctx.com ("Could not parse `" ^ e ^ "`") p; raise err in match fst e with | ECall (e,args) -> let rec loop e = match fst e with - | EField (e,f) -> f :: loop e + | EField (e,f,_) -> f :: loop e | EConst (Ident i) -> [i] - | _ -> error "Invalid macro call" p + | _ -> typing_error "Invalid macro call" p in let path, meth = (match loop e with | [meth] -> (["haxe";"macro"],"Compiler"), meth | [meth;"server"] -> (["haxe";"macro"],"CompilationServer"), meth | meth :: cl :: path -> (List.rev path,cl), meth - | _ -> error "Invalid macro call" p) in + | _ -> typing_error "Invalid macro call" p) in ignore(call_macro ctx path meth args p); | _ -> - error "Invalid macro call" p + typing_error "Invalid macro call" p let interpret ctx = let mctx = Interp.create ctx.com (make_macro_api ctx null_pos) false in @@ -812,7 +883,7 @@ let setup() = Interp.setup Interp.macro_api let type_stored_expr ctx e1 = - let id = match e1 with (EConst (Int s),_) -> int_of_string s | _ -> die "" __LOC__ in + let id = match e1 with (EConst (Int (s, _)),_) -> int_of_string s | _ -> die "" __LOC__ in get_stored_typed_expr ctx.com id ;; diff --git a/src/typing/magicTypes.ml b/src/typing/magicTypes.ml index c604f4fc..863a8c47 100644 --- a/src/typing/magicTypes.ml +++ b/src/typing/magicTypes.ml @@ -11,7 +11,7 @@ open Error (* REMOTING PROXYS *) let extend_remoting ctx c t p async prot = - if c.cl_super <> None then error "Cannot extend several classes" p; + if c.cl_super <> None then typing_error "Cannot extend several classes" p; (* remove forbidden packages *) let rules = ctx.com.package_rules in ctx.com.package_rules <- PMap.foldi (fun key r acc -> match r with Forbidden -> acc | _ -> PMap.add key r acc) rules PMap.empty; @@ -22,13 +22,13 @@ let extend_remoting ctx c t p async prot = let t = (try load_type_def ctx p (mk_type_path (fst path,new_name)) with - Error (Module_not_found _,p2) when p == p2 -> + Error (Module_not_found _,p2,_) when p == p2 -> (* build it *) Common.log ctx.com ("Building proxy for " ^ s_type_path path); let file, decls = (try TypeloadParse.parse_module ctx path p with - | Not_found -> ctx.com.package_rules <- rules; error ("Could not load proxy module " ^ s_type_path path ^ (if fst path = [] then " (try using absolute path)" else "")) p + | Not_found -> ctx.com.package_rules <- rules; typing_error ("Could not load proxy module " ^ s_type_path path ^ (if fst path = [] then " (try using absolute path)" else "")) p | e -> ctx.com.package_rules <- rules; raise e) in ctx.com.package_rules <- rules; let base_fields = [ @@ -41,7 +41,7 @@ let extend_remoting ctx c t p async prot = acc else match f.cff_kind with | FFun fd when (is_public || List.mem_assoc APublic f.cff_access) && not (List.mem_assoc AStatic f.cff_access) -> - if List.exists (fun (_,_,_,t,_) -> t = None) fd.f_args then error ("Field " ^ fst f.cff_name ^ " type is not complete and cannot be used by RemotingProxy") p; + if List.exists (fun (_,_,_,t,_) -> t = None) fd.f_args then typing_error ("Field " ^ fst f.cff_name ^ " type is not complete and cannot be used by RemotingProxy") p; let eargs = [EArrayDecl (List.map (fun ((a,_),_,_,_,_) -> (EConst (Ident a),p)) fd.f_args),p] in let ftype = (match fd.f_type with Some (CTPath { tpackage = []; tname = "Void" },_) -> None | _ -> fd.f_type) in let fargs, eargs = if async then match ftype with @@ -53,8 +53,8 @@ let extend_remoting ctx c t p async prot = let id = (EConst (String (fst f.cff_name,SDoubleQuotes)), p) in let id = if prot then id else ECall ((EConst (Ident "__unprotect__"),p),[id]),p in let expr = ECall ( - (EField ( - (ECall ((EField ((EConst (Ident "__cnx"),p),"resolve"),p),[id]),p), + (efield ( + (ECall ((efield ((EConst (Ident "__cnx"),p),"resolve"),p),[id]),p), "call") ,p),eargs),p in @@ -81,11 +81,11 @@ let extend_remoting ctx c t p async prot = try List.find (fun tdecl -> snd (t_path tdecl) = new_name) m.m_types with Not_found -> - error ("Module " ^ s_type_path path ^ " does not define type " ^ t.tname) p + typing_error ("Module " ^ s_type_path path ^ " does not define type " ^ t.tname) p ) in match t with | TClassDecl c2 when c2.cl_params = [] -> ignore(c2.cl_build()); c.cl_super <- Some (c2,[]); - | _ -> error "Remoting proxy must be a class without parameters" p + | _ -> typing_error "Remoting proxy must be a class without parameters" p let on_inherit ctx c p (is_extends,tp) = if not is_extends then diff --git a/src/typing/matcher.ml b/src/typing/matcher.ml index 0b9dbf7a..752f2993 100644 --- a/src/typing/matcher.ml +++ b/src/typing/matcher.ml @@ -39,15 +39,15 @@ let make_offset_list left right middle other = (ExtList.List.make left other) @ [middle] @ (ExtList.List.make right other) let type_field_access ctx ?(resume=false) e name = - Calls.acc_get ctx (Fields.type_field (Fields.TypeFieldConfig.create resume) ctx e name e.epos MGet WithType.value) e.epos + Calls.acc_get ctx (Fields.type_field (Fields.TypeFieldConfig.create resume) ctx e name e.epos MGet WithType.value) let unapply_type_parameters params monos = let unapplied = ref [] in - List.iter2 (fun (_,t1) t2 -> + List.iter2 (fun tp1 t2 -> match t2,follow t2 with | TMono m1,TMono m2 -> unapplied := (m1,m1.tm_type) :: !unapplied; - Monomorph.bind m1 t1; + Monomorph.bind m1 tp1.ttp_type; | _ -> () ) params monos; !unapplied @@ -68,9 +68,9 @@ let get_general_module_type ctx mt p = | TInst(c,_) -> loop (TClassDecl c) | TEnum(en,_) -> loop (TEnumDecl en) | TAbstract(a,_) -> loop (TAbstractDecl a) - | _ -> error "Cannot use this type as a value" p + | _ -> typing_error "Cannot use this type as a value" p end - | _ -> error "Cannot use this type as a value" p + | _ -> typing_error "Cannot use this type as a value" p in Typeload.load_instance ctx ({tname=loop mt;tpackage=[];tsub=None;tparams=[]},p) true @@ -132,7 +132,7 @@ module Constructor = struct | ConArray i -> make_int ctx.com.basic i p | ConTypeExpr mt -> TyperBase.type_module_type ctx mt None p | ConStatic(c,cf) -> make_static_field c cf p - | ConFields _ -> error "Something went wrong" p + | ConFields _ -> typing_error "Something went wrong" p let hash con = Hashtbl.hash (fst con) end @@ -183,18 +183,18 @@ module Pattern = struct let ctx = pctx.ctx in let p = pos e in let fail () = - error ("Unrecognized pattern: " ^ (Ast.Printer.s_expr e)) p + typing_error ("Unrecognized pattern: " ^ (Ast.Printer.s_expr e)) p in let unify_expected t' = unify ctx t' t p in let verror name p = - error (Printf.sprintf "Variable %s must appear exactly once in each sub-pattern" name) p + typing_error (Printf.sprintf "Variable %s must appear exactly once in each sub-pattern" name) p in let add_local final name p = let is_wildcard_local = name = "_" in - if not is_wildcard_local && pctx.is_postfix_match then error "Pattern variables are not allowed in .match patterns" p; - if not is_wildcard_local && PMap.mem name pctx.current_locals then error (Printf.sprintf "Variable %s is bound multiple times" name) p; + if not is_wildcard_local && pctx.is_postfix_match then typing_error "Pattern variables are not allowed in .match patterns" p; + if not is_wildcard_local && PMap.mem name pctx.current_locals then typing_error (Printf.sprintf "Variable %s is bound multiple times" name) p; match pctx.or_locals with | Some map when not is_wildcard_local -> let v,p = try PMap.find name map with Not_found -> verror name p in @@ -219,6 +219,16 @@ module Pattern = struct let con_type_expr mt p = ConTypeExpr mt,p in let con_array i p = ConArray i,p in let con_fields fl p = ConFields fl,p in + let get_enumerable_idents () = match follow t with + | TEnum(en,_) -> + en.e_names + | TAbstract({a_impl = Some c} as a,pl) when a.a_enum -> + ExtList.List.filter_map (fun cf -> + if has_class_field_flag cf CfImpl && has_class_field_flag cf CfEnum then Some cf.cf_name else None + ) c.cl_ordered_statics + | _ -> + [] + in let check_expr e = let rec loop e = match e.eexpr with | TField(_,FEnum(en,ef)) -> @@ -231,7 +241,19 @@ module Pattern = struct PatConstructor(con_const ct e.epos,[]) | TCast(e1,None) -> loop e1 - | TField _ -> + | TField (ef,f) -> + let s = field_name f in + begin match StringError.get_similar s (get_enumerable_idents()) with + | [] -> () + | l -> + let tpath = match follow t with + | TEnum (e,tl) -> s_type_path e.e_path ^ "." + | TAbstract (a,tl) -> s_type_path a.a_path ^ "." + | _ -> "" + in + let fields = List.map (fun (el) -> tpath ^ el) l in + warning pctx.ctx WTyper ("Potential typo detected (expected similar values are " ^ (String.concat ", " fields) ^ ")") p + end; raise (Bad_pattern "Only inline or read-only (default, never) fields can be used as a pattern") | TTypeExpr mt -> PatConstructor(con_type_expr mt e.epos,[]) @@ -247,12 +269,14 @@ module Pattern = struct if pctx.is_postfix_match then DKMarked else DKPattern toplevel in let catch_errors () = - let old = ctx.on_error in - ctx.on_error <- (fun _ _ _ -> + let old = ctx.com.located_error in + let restore_report_mode = disable_report_mode ctx.com in + ctx.com.located_error <- (fun ?depth _ -> raise Exit ); (fun () -> - ctx.on_error <- old + restore_report_mode(); + ctx.com.located_error <- old ) in let try_typing e = @@ -290,24 +314,14 @@ module Pattern = struct with _ -> restore(); if not (is_lower_ident s) && (match s.[0] with '`' | '_' -> false | _ -> true) then begin - display_error ctx "Pattern variables must be lower-case" p; + display_error ctx.com ("Unknown identifier : " ^ s ^ ", pattern variables must be lower-case or with `var ` prefix") p; end; - let sl = match follow t with - | TEnum(en,_) -> - en.e_names - | TAbstract({a_impl = Some c} as a,pl) when a.a_enum -> - ExtList.List.filter_map (fun cf -> - if has_class_field_flag cf CfImpl && has_class_field_flag cf CfEnum then Some cf.cf_name else None - ) c.cl_ordered_statics - | _ -> - [] - in - begin match StringError.get_similar s sl with + begin match StringError.get_similar s (get_enumerable_idents()) with | [] -> () (* if toplevel then - pctx.ctx.com.warning (Printf.sprintf "`case %s` has been deprecated, use `case var %s` instead" s s) p *) - | l -> pctx.ctx.com.warning ("Potential typo detected (expected similar values are " ^ (String.concat ", " l) ^ "). Consider using `var " ^ s ^ "` instead") p + warning pctx.ctx (Printf.sprintf "`case %s` has been deprecated, use `case var %s` instead" s s) p *) + | l -> warning pctx.ctx WTyper ("Potential typo detected (expected similar values are " ^ (String.concat ", " l) ^ "). Consider using `var " ^ s ^ "` instead") p end; let v = add_local false s p in PatVariable v @@ -322,7 +336,16 @@ module Pattern = struct let e = loop e in pctx.in_reification <- old; e - | EConst((Ident ("false" | "true") | Int _ | String _ | Float _) as ct) -> + | EConst((Ident ("false" | "true") | Int (_,_) | String _ | Float (_,_)) as ct) -> + begin match ct with + | String (value,kind) when kind = Ast.SSingleQuotes -> + let e = ctx.g.do_format_string ctx value p in + begin match e with + | EBinop _, p -> typing_error "String interpolation is not allowed in case patterns" p; + | _ -> () + end; + | _ -> () + end; let p = pos e in let e = Texpr.type_constant ctx.com.basic ct p in unify_expected e.etype; @@ -332,7 +355,7 @@ module Pattern = struct begin match follow t with | TFun(ta,tr) when tr == fake_tuple_type -> if i = "_" then PatTuple(List.map (fun (_,_,t) -> (PatAny,pos e)) ta) - else error "Cannot bind matched tuple to variable, use _ instead" p + else typing_error "Cannot bind matched tuple to variable, use _ instead" p | _ -> if i = "_" then PatAny else handle_ident i (pos e) @@ -365,9 +388,9 @@ module Pattern = struct | [],[] -> [] | [],_ -> - error "Not enough arguments" p + typing_error "Not enough arguments" p | _,[] -> - error "Too many arguments" p + typing_error "Too many arguments" p in let patterns = loop el args in ignore(unapply_type_parameters ef.ef_params monos); @@ -380,7 +403,7 @@ module Pattern = struct try_typing e with | Exit -> fail() - | Bad_pattern s -> error s p + | Bad_pattern s -> typing_error s p end | EArrayDecl el -> let rec pattern seen t = match follow t with @@ -390,8 +413,8 @@ module Pattern = struct let pat = make pctx false t e in pat :: loop el tl | [],[] -> [] - | [],_ -> error "Not enough arguments" p - | (_,p) :: _,[] -> error "Too many arguments" p + | [],_ -> typing_error "Not enough arguments" p + | (_,p) :: _,[] -> typing_error "Too many arguments" p in let patterns = loop el tl in PatTuple patterns @@ -401,7 +424,7 @@ module Pattern = struct ) el in PatConstructor(con_array (List.length patterns) (pos e),patterns) | TAbstract(a,tl) as t when not (List.exists (fun t' -> shallow_eq t t') seen) -> - begin match TyperBase.get_abstract_froms a tl with + begin match TyperBase.get_abstract_froms ctx a tl with | [t2] -> pattern (t :: seen) t2 | _ -> fail() end @@ -446,7 +469,7 @@ module Pattern = struct collect_field cf (apply_params a.a_params tl cf.cf_type) filter ) c.cl_ordered_statics; | _ -> - error (Printf.sprintf "Cannot field-match against %s" (s_type t)) (pos e) + typing_error (Printf.sprintf "Cannot field-match against %s" (s_type t)) (pos e) in collect_fields t None; let is_matchable cf = @@ -463,7 +486,7 @@ module Pattern = struct else patterns,fields ) ([],[]) !known_fields in - List.iter (fun ((s,_,_),e) -> if not (List.mem s fields) then error (Printf.sprintf "%s has no field %s" (s_type t) s) (pos e)) fl; + List.iter (fun ((s,_,_),e) -> if not (List.mem s fields) then typing_error (Printf.sprintf "%s has no field %s" (s_type t) s) (pos e)) fl; PatConstructor(con_fields fields (pos e),patterns) | EBinop(OpOr,e1,e2) -> let pctx1 = {pctx with current_locals = PMap.empty} in @@ -551,7 +574,7 @@ module Case = struct let e2 = collapse_case el in EBinop(OpOr,e,e2),punion (pos e) (pos e2) | [] -> - error "case without pattern" p + typing_error "case without pattern" p in let e = collapse_case el in let monos = List.map (fun _ -> mk_mono()) ctx.type_params in @@ -909,16 +932,16 @@ module Useless = struct (* Sane part *) - let check_case com p (case,bindings,patterns) = + let check_case ctx p (case,bindings,patterns) = let p = List.map (fun (_,_,patterns) -> patterns) p in match u' p (copy p) (copy p) patterns [] [] with - | False -> com.warning "This case is unused" case.case_pos - | Pos p -> com.warning "This pattern is unused" p + | False -> Typecore.warning ctx WUnusedPattern "This case is unused" case.case_pos + | Pos p -> Typecore.warning ctx WUnusedPattern "This pattern is unused" p | True -> () - let check com cases = + let check ctx cases = ignore(List.fold_left (fun acc (case,bindings,patterns) -> - check_case com acc (case,bindings,patterns); + check_case ctx acc (case,bindings,patterns); if case.case_guard = None then acc @ [case,bindings,patterns] else acc ) [] cases) end @@ -988,7 +1011,7 @@ module Compile = struct | ConArray i -> ExtList.List.init i (fun i -> let ei = make_int mctx.ctx.com.basic i e.epos in - Calls.acc_get mctx.ctx (Calls.array_access mctx.ctx e ei MGet e.epos) e.epos + Calls.acc_get mctx.ctx (Calls.array_access mctx.ctx e ei MGet e.epos) ) | ConConst _ | ConTypeExpr _ | ConStatic _ -> [] @@ -1119,9 +1142,9 @@ module Compile = struct | [],[] -> bindings | [],e :: _ -> - error "Invalid match: Not enough patterns" e.epos + typing_error "Invalid match: Not enough patterns" e.epos | (_,p) :: _,[] -> - error "Invalid match: Too many patterns" p + typing_error "Invalid match: Too many patterns" p in let bindings = loop patterns subjects bindings in if bindings = [] then dt else bind mctx (List.rev bindings) dt @@ -1146,11 +1169,12 @@ module Compile = struct | PatBind(v,pat1) -> loop ((v,pos pat,subject) :: bindings) pat1 | PatVariable _ | PatAny -> () | PatExtractor _ -> raise Extractor - | _ -> error ("Unexpected pattern: " ^ (Pattern.to_string pat)) case.case_pos; + | _ -> typing_error ("Unexpected pattern: " ^ (Pattern.to_string pat)) case.case_pos; in loop bindings (List.hd patterns) ) cases; let sigma = ConTable.fold (fun con arg_positions acc -> (con,ConTable.mem unguarded con,arg_positions) :: acc) sigma [] in + let sigma = List.sort (fun ((_,p1),_,_) ((_,p2),_,_) -> p1.pmin - p2.pmin) sigma in sigma,List.rev !null in let sigma,null = get_column_sigma cases in @@ -1258,7 +1282,7 @@ module Compile = struct switch mctx subject [] dt_fail | _ -> let dt = compile mctx subjects cases in - Useless.check mctx.ctx.com cases; + Useless.check mctx.ctx cases; match vars with | [] -> dt | _ -> bind mctx vars dt @@ -1276,8 +1300,6 @@ module TexprConverter = struct | SKEnum | SKLength - exception Not_exhaustive - let s_subject v_lookup s e = let rec loop top s e = match e.eexpr with | TField(_,FEnum(en,ef)) -> @@ -1342,7 +1364,7 @@ module TexprConverter = struct t_dynamic in let t = match fst con with - | ConEnum(en,_) -> TEnum(en,List.map snd en.e_params) + | ConEnum(en,_) -> TEnum(en,extract_param_types en.e_params) | ConArray _ -> ctx.t.tarray t_dynamic | ConConst ct -> begin match ct with @@ -1352,7 +1374,7 @@ module TexprConverter = struct | TBool _ -> ctx.t.tbool | _ -> fail() end - | ConStatic({cl_kind = KAbstractImpl a},_) -> (TAbstract(a,List.map snd a.a_params)) + | ConStatic({cl_kind = KAbstractImpl a},_) -> (TAbstract(a,extract_param_types a.a_params)) | ConTypeExpr mt -> get_general_module_type ctx mt e.epos | ConFields _ | ConStatic _ -> fail() in @@ -1409,7 +1431,7 @@ module TexprConverter = struct | _ -> kind = SKValue in List.iter (fun (con,unguarded,dt) -> - if not (compatible_kind con) then error "Incompatible pattern" dt.dt_pos; + if not (compatible_kind con) then typing_error "Incompatible pattern" dt.dt_pos; if unguarded then ConTable.remove h con ) cases; let unmatched = ConTable.fold (fun con _ acc -> con :: acc) h [] in @@ -1440,7 +1462,12 @@ module TexprConverter = struct | [] -> "_" | _ -> String.concat " | " (List.sort Pervasives.compare sl) in - error (Printf.sprintf "Unmatched patterns: %s" (s_subject v_lookup s e_subject)) e_subject.epos + typing_error (Printf.sprintf "Unmatched patterns: %s" (s_subject v_lookup s e_subject)) e_subject.epos + + type dt_recursion = + | Toplevel + | AfterSwitch + | Deep let to_texpr ctx t_switch match_debug with_type dt = let v_lookup = ref IntMap.empty in @@ -1448,7 +1475,7 @@ module TexprConverter = struct let p = dt.dt_pos in let c_type = match follow (Typeload.load_instance ctx (mk_type_path (["std"],"Type"),p) true) with TInst(c,_) -> c | t -> die "" __LOC__ in let mk_index_call e = - if not ctx.in_macro && not ctx.com.display.DisplayMode.dms_full_typing then + if not ctx.com.is_macro_context && not ctx.com.display.DisplayMode.dms_full_typing then (* If we are in display mode there's a chance that these fields don't exist. Let's just use a (correctly typed) neutral value because it doesn't actually matter. *) mk (TConst (TInt (Int32.of_int 0))) ctx.t.tint e.epos @@ -1456,34 +1483,41 @@ module TexprConverter = struct mk (TEnumIndex e) com.basic.tint e.epos in let mk_name_call e = - if not ctx.in_macro && not ctx.com.display.DisplayMode.dms_full_typing then + if not ctx.com.is_macro_context && not ctx.com.display.DisplayMode.dms_full_typing then mk (TConst (TString "")) ctx.t.tstring e.epos else let cf = PMap.find "enumConstructor" c_type.cl_statics in make_static_call ctx c_type cf (fun t -> t) [e] com.basic.tstring e.epos in - let rec loop toplevel params dt = match dt.dt_texpr with + let rec loop dt_rec params dt = match dt.dt_texpr with | Some e -> - e + Some e | None -> let e = match dt.dt_t with | Leaf case -> begin match case.case_expr with - | Some e -> e - | None -> mk (TBlock []) ctx.t.tvoid case.case_pos + | Some e -> Some e + | None -> Some (mk (TBlock []) ctx.t.tvoid case.case_pos) end | Switch(_,[(ConFields _,_),_,dt],_) -> (* TODO: Can we improve this by making it more general? *) - loop false params dt + loop dt_rec params dt | Switch(e_subject,cases,default) -> + let dt_rec',toplevel = match dt_rec with + | Toplevel -> AfterSwitch,true + | AfterSwitch | Deep -> Deep,false + in let e_subject,unmatched,kind,finiteness = all_ctors ctx e_subject cases in let unmatched = ExtList.List.filter_map (unify_constructor ctx params e_subject.etype) unmatched in - let loop toplevel params dt = - try Some (loop false params dt) - with Not_exhaustive -> match with_type,finiteness with + let loop params dt = match loop dt_rec' params dt with + | None -> + begin match with_type,finiteness with | WithType.NoValue,Infinite when toplevel -> None | _,CompileTimeFinite when unmatched = [] -> None - | _ when ctx.com.display.DisplayMode.dms_error_policy = DisplayMode.EPIgnore -> None + | _ when ignore_error ctx.com -> None | _ -> report_not_exhaustive !v_lookup e_subject unmatched + end + | Some e -> + Some e in let cases = ExtList.List.filter_map (fun (con,_,dt) -> match unify_constructor ctx params e_subject.etype con with | Some(_,params) -> Some (con,dt,params) @@ -1506,10 +1540,10 @@ module TexprConverter = struct | [],RunTimeFinite -> None | _ -> - loop toplevel params default + loop params default in let cases = ExtList.List.filter_map (fun (cons,dt,params) -> - let eo = loop toplevel params dt in + let eo = loop params dt in begin match eo with | None -> None | Some e -> Some (List.map (Constructor.to_texpr ctx match_debug) (List.sort Constructor.compare cons),e) @@ -1521,7 +1555,7 @@ module TexprConverter = struct | SKEnum -> if match_debug then mk_name_call e_subject else mk_index_call e_subject | SKLength -> type_field_access ctx e_subject "length" in - begin match cases,e_default,with_type with + let e = match cases,e_default,with_type with | [_,e2],None,_ when (match finiteness with RunTimeFinite -> true | _ -> false) && not is_nullable_subject -> {e2 with etype = t_switch} | [[e1],e2],Some _,_ @@ -1546,56 +1580,88 @@ module TexprConverter = struct e_subject in mk (TSwitch(e_subject,cases,e_default)) t_switch dt.dt_pos - end + in + Some e | Guard(e,dt1,dt2) -> - let e_then = loop false params dt1 in - begin try - let e_else = loop false params dt2 in - mk (TIf(e,e_then,Some e_else)) t_switch (punion e_then.epos e_else.epos) - with Not_exhaustive when with_type = NoValue -> - mk (TIf(e,e_then,None)) ctx.t.tvoid (punion e.epos e_then.epos) + (* Normal guards are considered toplevel if we're in the toplevel switch. *) + let toplevel = match dt_rec with + | Toplevel | AfterSwitch -> true + | Deep -> false + in + let e_then = loop dt_rec params dt1 in + begin match e_then with + | None -> + None + | Some e_then -> + let e_else = loop dt_rec params dt2 in + begin match e_else with + | Some e_else -> + Some (mk (TIf(e,e_then,Some e_else)) t_switch (punion e_then.epos e_else.epos)) + | None -> + if with_type = NoValue && toplevel then + Some (mk (TIf(e,e_then,None)) ctx.t.tvoid (punion e.epos e_then.epos)) + else + None + end end | GuardNull(e,dt1,dt2) -> + let toplevel = match dt_rec with + | Toplevel -> true + | Deep | AfterSwitch -> false + in let e_null = make_null e.etype e.epos in let f_op e = mk (TBinop(OpEq,e,e_null)) ctx.t.tbool e.epos in - let f = try - let rec loop2 acc dt = match dt.dt_t with - | GuardNull(e,dt1,dt3) when Decision_tree.equal_dt dt2 dt3 -> - loop2 ((f_op e) :: acc) dt1 - | Guard(e,dt1,dt3) when Decision_tree.equal_dt dt2 dt3 -> - loop2 (e :: acc) dt1 - | _ -> - List.rev acc,dt - in - let conds,dt1 = loop2 [] dt1 in - let e_then = loop toplevel params dt1 in - (fun () -> - let e_else = loop toplevel params dt2 in - let e_cond = List.fold_left (fun e1 e2 -> binop OpBoolAnd e1 e2 ctx.t.tbool (punion e1.epos e2.epos)) (f_op e) conds in - mk (TIf(e_cond,e_then,Some e_else)) t_switch (punion e_then.epos e_else.epos) - ) - with Not_exhaustive -> - if toplevel then (fun () -> loop toplevel params dt2) - else if ctx.com.display.DisplayMode.dms_error_policy = DisplayMode.EPIgnore then (fun () -> mk (TConst TNull) (mk_mono()) dt2.dt_pos) - else report_not_exhaustive !v_lookup e [(ConConst TNull,dt.dt_pos),dt.dt_pos] + let rec loop2 acc dt = match dt.dt_t with + | GuardNull(e,dt1,dt3) when Decision_tree.equal_dt dt2 dt3 -> + loop2 ((f_op e) :: acc) dt1 + | Guard(e,dt1,dt3) when Decision_tree.equal_dt dt2 dt3 -> + loop2 (e :: acc) dt1 + | _ -> + List.rev acc,dt in - f() + let conds,dt1 = loop2 [] dt1 in + let e_cond = List.fold_left (fun e1 e2 -> binop OpBoolAnd e1 e2 ctx.t.tbool (punion e1.epos e2.epos)) (f_op e) conds in + let e_then = loop dt_rec params dt1 in + begin match e_then with + | None -> + if toplevel then + loop dt_rec params dt2 + else if ignore_error ctx.com then + Some (mk (TConst TNull) (mk_mono()) dt2.dt_pos) + else + report_not_exhaustive !v_lookup e [(ConConst TNull,dt.dt_pos),dt.dt_pos] + | Some e_then -> + let e_else = loop dt_rec params dt2 in + begin match e_else with + | None -> + if toplevel then + Some (mk (TIf(e_cond,e_then,None)) t_switch e_then.epos) + else + report_not_exhaustive !v_lookup e [] + | Some e_else -> + Some (mk (TIf(e_cond,e_then,Some e_else)) t_switch (punion e_then.epos e_else.epos)) + end + end | Bind(bl,dt) -> let el = List.map (fun (v,p,e) -> v_lookup := IntMap.add v.v_id e !v_lookup; mk (TVar(v,Some e)) com.basic.tvoid p ) bl in - let e = loop toplevel params dt in - mk (TBlock (el @ [e])) e.etype dt.dt_pos + let e = loop dt_rec params dt in + Option.map (fun e -> mk (TBlock (el @ [e])) e.etype dt.dt_pos) e; | Fail -> - raise Not_exhaustive + None in - dt.dt_texpr <- Some e; + dt.dt_texpr <- e; e in - let params = List.map snd ctx.type_params in - let e = loop true params dt in - Texpr.duplicate_tvars e + let params = extract_param_types ctx.type_params in + let e = loop Toplevel params dt in + match e with + | None -> + typing_error "Unmatched patterns: _" p; + | Some e -> + Texpr.duplicate_tvars e end module Match = struct @@ -1672,7 +1738,7 @@ module Match = struct print_endline (Decision_tree.to_string dt); print_endline "DECISION TREE END"; end; - let e = try + let e = let t_switch = infer_switch_type() in (match tmono with | Some t when allow_min_void && ExtType.is_void (follow t) -> () @@ -1680,8 +1746,6 @@ module Match = struct | _ -> () ); TexprConverter.to_texpr ctx t_switch match_debug with_type dt - with TexprConverter.Not_exhaustive -> - error "Unmatched patterns: _" p; in if match_debug then begin print_endline "TEXPR BEGIN"; diff --git a/src/typing/nullSafety.ml b/src/typing/nullSafety.ml index f4c698f5..be3da1f5 100644 --- a/src/typing/nullSafety.ml +++ b/src/typing/nullSafety.ml @@ -70,19 +70,32 @@ let is_string_type t = (** Check for explicit `Null<>` typing *) -let rec is_nullable_type = function +let rec is_nullable_type ?(dynamic_is_nullable=false) = function | TMono r -> (match r.tm_type with None -> false | Some t -> is_nullable_type t) | TAbstract ({ a_path = ([],"Null") },[t]) -> true + | TAbstract ({ a_path = ([],"Any") },[]) -> + false | TAbstract (a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> is_nullable_type (apply_params a.a_params tl a.a_this) | TLazy f -> is_nullable_type (lazy_type f) | TType (t,tl) -> - is_nullable_type (apply_params t.t_params tl t.t_type) + is_nullable_type (apply_typedef t tl) + | (TDynamic _) as t -> + dynamic_is_nullable && t == t_dynamic | _ -> false +(* +(** + Check if `callee` represents `trace` +*) +let is_trace_expr callee = + match callee.eexpr with + | TIdent "`trace" -> true + | _ -> false *) + (** If `expr` is a TCast or TMeta, returns underlying expression (recursively bypassing nested casts). @@ -235,9 +248,9 @@ class unificator = | _, TMono t -> (match t.tm_type with None -> () | Some t -> self#unify a t) | TType (t,tl), _ -> - self#unify_rec a b (fun() -> self#unify (apply_params t.t_params tl t.t_type) b) + self#unify_rec a b (fun() -> self#unify (apply_typedef t tl) b) | _, TType (t,tl) -> - self#unify_rec a b (fun() -> self#unify a (apply_params t.t_params tl t.t_type)) + self#unify_rec a b (fun() -> self#unify a (apply_typedef t tl)) | TAbstract (abstr,tl), _ when not (Meta.has Meta.CoreType abstr.a_meta) -> self#unify (apply_params abstr.a_params tl abstr.a_this) b | _, TAbstract (abstr,tl) when not (Meta.has Meta.CoreType abstr.a_meta) -> @@ -307,22 +320,6 @@ class unificator = traverse a_args b_args end -(** - Checks if execution of provided expression is guaranteed to be terminated with `return`, `throw`, `break` or `continue`. -*) -let rec is_dead_end e = - match e.eexpr with - | TThrow _ -> true - | TReturn _ -> true - | TBreak -> true - | TContinue -> true - | TWhile (_, body, DoWhile) -> is_dead_end body - | TIf (_, if_body, Some else_body) -> is_dead_end if_body && is_dead_end else_body - | TBlock exprs -> List.exists is_dead_end exprs - | TMeta (_, e) -> is_dead_end e - | TCast (e, _) -> is_dead_end e - | _ -> false - (** Check if `expr` is a `trace` (not a call, but identifier itself) *) @@ -341,7 +338,7 @@ let rec unfold_null t = | TAbstract ({ a_path = ([],"Null") }, [t]) -> unfold_null t | TAbstract (abstr,tl) when not (Meta.has Meta.CoreType abstr.a_meta) -> unfold_null (apply_params abstr.a_params tl abstr.a_this) | TLazy f -> unfold_null (lazy_type f) - | TType (t,tl) -> unfold_null (apply_params t.t_params tl t.t_type) + | TType (t,tl) -> unfold_null (apply_typedef t tl) | _ -> t (** @@ -358,23 +355,6 @@ let accessed_field_name access = | FClosure (_, { cf_name = name }) -> name | FEnum (_, { ef_name = name }) -> name -let rec can_pass_type src dst = - if is_nullable_type src && not (is_nullable_type dst) then - false - else - (* TODO *) - match dst with - | TMono r -> (match r.tm_type with None -> true | Some t -> can_pass_type src t) - | TEnum (_, params) -> true - | TInst _ -> true - | TType (t, tl) -> can_pass_type src (apply_params t.t_params tl t.t_type) - | TFun _ -> true - | TAnon _ -> true - | TDynamic _ -> true - | TLazy _ -> true - | TAbstract ({ a_path = ([],"Null") }, [t]) -> true - | TAbstract _ -> true - (** Collect nullable local vars which are checked against `null`. Returns a tuple of (vars_checked_to_be_null * vars_checked_to_be_not_null) in case `condition` evaluates to `true`. @@ -772,6 +752,12 @@ class local_safety (mode:safety_mode) = in scopes <- scope :: scopes; List.iter (fun (v, _) -> scope#declare_var v) fn.tf_args + (** + Should be called upon standalone block declaration. + *) + method block_declared = + let scope = new safety_scope mode STNormal self#get_current_scope#get_safe_locals self#get_current_scope#get_never_safe in + scopes <- scope :: scopes (** Should be called upon entering a loop. *) @@ -921,7 +907,7 @@ class local_safety (mode:safety_mode) = self#get_current_scope#reset_to initial_safe; (** execute `else_body` with known not-null variables *) let handle_dead_end body safe_vars = - if is_dead_end body then + if DeadEnd.has_dead_end body then List.iter self#get_current_scope#add_to_safety safe_vars in (match else_body with @@ -1057,10 +1043,25 @@ class expr_checker mode immediate_execution report = | TReturn (Some e) -> self#is_nullable_expr e | TBinop ((OpAssign | OpAssignOp _), _, right) -> self#is_nullable_expr right | TBlock exprs -> - (match exprs with + local_safety#block_declared; + let rec traverse exprs = + match exprs with + | [] -> false + | [e] -> self#is_nullable_expr e + | e :: exprs -> + (match e.eexpr with + | TVar (v,_) -> local_safety#declare_var v + | _ -> () + ); + traverse exprs + in + let is_nullable = traverse exprs in + local_safety#scope_closed; + is_nullable + (* (match exprs with | [] -> false | _ -> self#is_nullable_expr (List.hd (List.rev exprs)) - ) + ) *) | TIf _ -> let nullable = ref false in let check body = nullable := !nullable || self#is_nullable_expr body in @@ -1085,7 +1086,7 @@ class expr_checker mode immediate_execution report = with Not_found -> false) fields | _, _ -> - if self#is_nullable_expr expr && not (is_nullable_type to_type) then + if self#is_nullable_expr expr && not (is_nullable_type ~dynamic_is_nullable:true to_type) then false else begin let expr_type = unfold_null expr.etype in @@ -1099,7 +1100,6 @@ class expr_checker mode immediate_execution report = true | e -> fail ~msg:"Null safety unification failure" expr.epos __POS__ - (* can_pass_type expr.etype to_type *) end (** Should be called for the root expressions of a method or for then initialization expressions of fields. @@ -1150,11 +1150,16 @@ class expr_checker mode immediate_execution report = Check expressions in a block *) method private check_block exprs p = - match exprs with - | [] -> () - | e :: rest -> - self#check_expr e; - self#check_block rest p + local_safety#block_declared; + let rec traverse exprs = + match exprs with + | [] -> () + | e :: rest -> + self#check_expr e; + traverse rest + in + traverse exprs; + local_safety#scope_closed (** Don't allow to use nullable values as items in declaration of not-nullable arrays *) diff --git a/src/typing/operators.ml b/src/typing/operators.ml index 26b7280e..4cd16810 100644 --- a/src/typing/operators.ml +++ b/src/typing/operators.ml @@ -9,60 +9,11 @@ open Calls open Fields open FieldAccess -class value_reference (ctx : typer) = - -object(self) - val vars = DynArray.create () - - method get_vars = DynArray.to_list vars - - method as_var name e = - let v = alloc_var VGenerated name e.etype e.epos in - DynArray.add vars (v,e); - mk (TLocal v) v.v_type v.v_pos - - method private get_expr_aux depth name e = - let rec loop depth name e = match (Texpr.skip e).eexpr with - | TLocal _ | TTypeExpr _ | TConst _ -> - e - | TField(ef,fa) when depth = 0 -> - let ef = loop (depth + 1) "fh" ef in - {e with eexpr = TField(ef,fa)} - | TArray(e1,e2) when depth = 0 -> - let e1 = loop (depth + 1) "base" e1 in - let e2 = loop (depth + 1) "index" e2 in - {e with eexpr = TArray(e1,e2)} - | _ -> - self#as_var name e - in - loop depth name e - - method get_expr name e = - self#get_expr_aux 0 name e - - method get_expr_part name e = - self#get_expr_aux 1 name e - - method to_texpr e = - begin match self#get_vars with - | [] -> - e - | vl -> - let el = List.map (fun (v,e) -> - mk (TVar(v,Some e)) ctx.t.tvoid v.v_pos - ) vl in - let e = mk (TBlock (el @ [e])) e.etype e.epos in - {e with eexpr = TMeta((Meta.MergeBlock,[],null_pos),e)} - end - - method to_texpr_el el e = - let vl = self#get_vars in - let el_vars = List.map (fun (v,e) -> - mk (TVar(v,Some e)) ctx.t.tvoid v.v_pos - ) vl in - let e = mk (TBlock (el_vars @ el @ [e])) e.etype e.epos in - {e with eexpr = TMeta((Meta.MergeBlock,[],null_pos),e)} -end +let check_error ctx err p depth = match err with + | Module_not_found ([],name) when Diagnostics.error_in_diagnostics_run ctx.com p -> + DisplayToplevel.handle_unresolved_identifier ctx name p true + | _ -> + Common.located_display_error ~depth ctx.com (error_msg p err) module BinopResult = struct @@ -131,18 +82,23 @@ module BinopResult = struct let get_type br = match br with | BinopNormal bn -> bn.binop_type | BinopSpecial(e,_) -> e.etype + + let needs_assign = function + | BinopNormal _ -> true + | BinopSpecial(_,needs_assign) -> needs_assign end let check_assign ctx e = - if ctx.com.display.dms_error_policy <> EPIgnore then match e.eexpr with - | TLocal v when has_var_flag v VFinal -> - error "Cannot assign to final" e.epos + match e.eexpr with + | TLocal v when has_var_flag v VFinal && not (Common.ignore_error ctx.com) -> + typing_error "Cannot assign to final" e.epos | TLocal {v_extra = None} | TArray _ | TField _ | TIdent _ -> () | TConst TThis | TTypeExpr _ when ctx.untyped -> () | _ -> - invalid_assign e.epos + if not (Common.ignore_error ctx.com) then + invalid_assign e.epos type type_class = | KInt @@ -246,14 +202,14 @@ let make_binop ctx op e1 e2 is_assign_op with_type p = | KInt | KFloat | KString -> e | KUnk | KDyn | KNumParam _ | KStrParam _ | KOther -> let std = type_type ctx ([],"Std") e.epos in - let acc = acc_get ctx (type_field_default_cfg ctx std "string" e.epos (MCall []) with_type) e.epos in + let acc = acc_get ctx (type_field_default_cfg ctx std "string" e.epos (MCall []) with_type) in ignore(follow acc.etype); let acc = (match acc.eexpr with TField (e,FClosure (Some (c,tl),f)) -> { acc with eexpr = TField (e,FInstance (c,tl,f)) } | _ -> acc) in make_call ctx acc [e] ctx.t.tstring e.epos | KAbstract (a,tl) -> try AbstractCast.cast_or_unify_raise ctx tstring e p - with Error (Unify _,_) -> + with Error (Unify _,_,_) -> loop (Abstract.get_underlying_type a tl) in loop e.etype @@ -334,7 +290,7 @@ let make_binop ctx op e1 e2 is_assign_op with_type p = | KOther, _ | _ , KOther -> let pr = print_context() in - error ("Cannot add " ^ s_type pr e1.etype ^ " and " ^ s_type pr e2.etype) p + typing_error ("Cannot add " ^ s_type pr e1.etype ^ " and " ^ s_type pr e2.etype) p ) | OpAnd | OpOr @@ -378,16 +334,16 @@ let make_binop ctx op e1 e2 is_assign_op with_type p = | OpNotEq -> let e1,e2 = try (* we only have to check one type here, because unification fails if one is Void and the other is not *) - (match follow e2.etype with TAbstract({a_path=[],"Void"},_) -> error "Cannot compare Void" p | _ -> ()); + (match follow e2.etype with TAbstract({a_path=[],"Void"},_) -> typing_error "Cannot compare Void" p | _ -> ()); AbstractCast.cast_or_unify_raise ctx e2.etype e1 p,e2 - with Error (Unify _,_) -> + with Error (Unify _,_,_) -> e1,AbstractCast.cast_or_unify ctx e1.etype e2 p in if not ctx.com.config.pf_supports_function_equality then begin match e1.eexpr, e2.eexpr with | TConst TNull , _ | _ , TConst TNull -> () | _ -> match follow e1.etype, follow e2.etype with - | TFun _ , _ | _, TFun _ -> ctx.com.warning "Comparison of function values is unspecified on this target, use Reflect.compareMethods instead" p + | TFun _ , _ | _, TFun _ -> warning ctx WClosureCompare "Comparison of function values is unspecified on this target, use Reflect.compareMethods instead" p | _ -> () end; mk_op e1 e2 ctx.t.tbool @@ -426,7 +382,7 @@ let make_binop ctx op e1 e2 is_assign_op with_type p = | KOther , _ | _ , KOther -> let pr = print_context() in - error ("Cannot compare " ^ s_type pr e1.etype ^ " and " ^ s_type pr e2.etype) p + typing_error ("Cannot compare " ^ s_type pr e1.etype ^ " and " ^ s_type pr e2.etype) p ); mk_op e1 e2 ctx.t.tbool | OpBoolAnd @@ -441,9 +397,10 @@ let make_binop ctx op e1 e2 is_assign_op with_type p = unify ctx e2.etype tint e2.epos; BinopSpecial (mk (TNew ((match t with TInst (c,[]) -> c | _ -> die "" __LOC__),[],[e1;e2])) t p,false) | OpArrow -> - error "Unexpected =>" p + typing_error "Unexpected =>" p | OpIn -> - error "Unexpected in" p + typing_error "Unexpected in" p + | OpNullCoal | OpAssign | OpAssignOp _ -> die "" __LOC__ @@ -452,23 +409,28 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op with_type let map = apply_params a.a_params tl in let make op_cf cf e1 e2 tret needs_assign swapped = if cf.cf_expr = None && not (has_class_field_flag cf CfExtern) then begin - if not (Meta.has Meta.NoExpr cf.cf_meta) then display_error ctx "Recursive operator method" p; + if not (Meta.has Meta.NoExpr cf.cf_meta) then Common.display_error ctx.com "Recursive operator method" p; if not (Meta.has Meta.CoreType a.a_meta) then begin (* for non core-types we require that the return type is compatible to the native result type *) let result = make_binop ctx op {e1 with etype = Abstract.follow_with_abstracts e1.etype} {e1 with etype = Abstract.follow_with_abstracts e2.etype} is_assign_op with_type p in let t_expected = BinopResult.get_type result in begin try - unify_raise ctx tret t_expected p - with Error (Unify _,_) -> + unify_raise tret t_expected p + with Error (Unify _,_,depth) -> match follow tret with | TAbstract(a,tl) when type_iseq (Abstract.get_underlying_type a tl) t_expected -> () | _ -> let st = s_type (print_context()) in - error (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p + typing_error ~depth (Printf.sprintf "The result of this operation (%s) is not compatible with declared return type %s" (st t_expected) (st tret)) p end; end; - BinopResult.create_normal op e1 e2 tret needs_assign swapped p + (* + If the `@:op`-field has no expr, then user wants the semantics of an underlying type. + which is supposed to make an assignment for AssignOp's. + Hence we use `is_assign_op` here instead of `needs_assign`. + *) + BinopResult.create_normal op e1 e2 tret is_assign_op swapped p end else if swapped then begin let vr = new value_reference ctx in let e2' = vr#as_var "lhs" e2 in @@ -488,11 +450,18 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op with_type | _ -> () end; - let rec loop find_op ol = match ol with + let rec loop find_op ol = + match ol with | (op_cf,cf) :: ol when op_cf = find_op -> let is_impl = has_class_field_flag cf CfImpl in - begin match follow cf.cf_type with - | TFun([(_,_,t1);(_,_,t2)],tret) -> + begin + match follow cf.cf_type with + | TFun((_,_,t1) :: (_,_,t2) :: pos_infos, tret) -> + (match pos_infos with + | [] -> () + | [_,true,t] when is_pos_infos t -> () + | _ -> die ~p:cf.cf_pos ("Unexpected arguments list of function " ^ cf.cf_name) __LOC__ + ); let check e1 e2 swapped = let map_arguments () = let monos = Monomorph.spawn_constrained_monos (fun t -> t) cf.cf_params in @@ -527,10 +496,10 @@ let find_abstract_binop_overload ctx op e1 e2 a c tl left is_assign_op with_type in begin try check e1 e2 false - with Error (Unify _,_) | Unify_error _ -> try + with Error (Unify _,_,_) | Unify_error _ -> try if not (Meta.has Meta.Commutative cf.cf_meta) then raise Not_found; check e2 e1 true - with Not_found | Error (Unify _,_) | Unify_error _ -> + with Not_found | Error (Unify _,_,_) | Unify_error _ -> loop find_op ol end | _ -> @@ -584,24 +553,30 @@ let type_binop2 ctx op (e1 : texpr) (e2 : Ast.expr) is_assign_op with_type p = let type_assign ctx e1 e2 with_type p = let e1 = !type_access_ref ctx (fst e1) (snd e1) (MSet (Some e2)) with_type in - let type_rhs with_type = type_expr ctx e2 with_type in + let type_rhs with_type = try + type_expr ctx e2 with_type + with Error(e,p,depth) -> + check_error ctx e p depth; + Texpr.Builder.make_null t_dynamic (pos e2) + in let assign_to e1 = let e2 = type_rhs (WithType.with_type e1.etype) in let e2 = AbstractCast.cast_or_unify ctx e1.etype e2 p in check_assign ctx e1; (match e1.eexpr , e2.eexpr with - | TLocal i1 , TLocal i2 when i1 == i2 -> error "Assigning a value to itself" p + | TLocal i1 , TLocal i2 when i1 == i2 -> typing_error "Assigning a value to itself" p | TField ({ eexpr = TConst TThis },FInstance (_,_,f1)) , TField ({ eexpr = TConst TThis },FInstance (_,_,f2)) when f1 == f2 -> - error "Assigning a value to itself" p + typing_error "Assigning a value to itself" p | _ , _ -> ()); mk (TBinop (OpAssign,e1,e2)) e1.etype p in match e1 with - | AKNo s -> error ("Cannot access field or identifier " ^ s ^ " for writing") p - | AKUsingField _ -> - error "Invalid operation" p + | AKNo(_,p) -> + typing_error "This expression cannot be accessed for writing" p + | AKUsingField _ | AKSafeNav _ -> + typing_error "Invalid operation" p | AKExpr { eexpr = TLocal { v_kind = VUser TVOLocalFunction; v_name = name } } -> - error ("Cannot access function " ^ name ^ " for writing") p + typing_error ("Cannot access function " ^ name ^ " for writing") p | AKField fa -> let ef = FieldAccess.get_field_expr fa FWrite in assign_to ef @@ -612,7 +587,7 @@ let type_assign ctx e1 e2 with_type p = dispatcher#accessor_call fa [] [e2] | AKAccess(a,tl,c,ebase,ekey) -> let e2 = type_rhs WithType.value in - mk_array_set_call ctx (AbstractCast.find_array_access ctx a tl ekey (Some e2) p) c ebase p + mk_array_set_call ctx (AbstractCast.find_array_write_access ctx a tl ekey e2 p) c ebase p | AKResolve(sea,name) -> let eparam = sea.se_this in let e_name = Texpr.Builder.make_string ctx.t name null_pos in @@ -620,7 +595,7 @@ let type_assign ctx e1 e2 with_type p = | AKUsingAccessor sea -> let fa_set = match FieldAccess.resolve_accessor sea.se_access (MSet (Some e2)) with | AccessorFound fa -> fa - | _ -> error "Could not resolve accessor" p + | _ -> typing_error "Could not resolve accessor" p in let dispatcher = new call_dispatcher ctx (MCall [e2]) with_type p in dispatcher#field_call fa_set [sea.se_this] [e2] @@ -661,14 +636,14 @@ let process_lhs_expr ctx name e_lhs = let type_assign_op ctx op e1 e2 with_type p = let field_rhs_by_name op name ev with_type = let access_get = type_field_default_cfg ctx ev name p MGet with_type in - let e_get = acc_get ctx access_get p in - e_get.etype,type_binop2 ctx op e_get e2 true (WithType.with_type e_get.etype) p + let e_get = acc_get ctx access_get in + e_get.etype,type_binop2 ctx op e_get e2 true WithType.value p in let field_rhs op cf ev = field_rhs_by_name op cf.cf_name ev (WithType.with_type cf.cf_type) in let assign vr e r_rhs = - check_assign ctx e; + if BinopResult.needs_assign r_rhs then check_assign ctx e; let assign e_rhs = let e_rhs = AbstractCast.cast_or_unify ctx e.etype e_rhs p in match e_rhs.eexpr with @@ -690,16 +665,18 @@ let type_assign_op ctx op e1 e2 with_type p = vr#to_texpr e in (match !type_access_ref ctx (fst e1) (snd e1) (MSet (Some e2)) with_type with - | AKNo s -> + | AKNo(_,p) -> (* try abstract operator overloading *) - (try type_non_assign_op ctx op e1 e2 true true with_type p - with Not_found -> error ("Cannot access field or identifier " ^ s ^ " for writing") p - ) - | AKUsingField _ -> - error "Invalid operation" p + begin try + type_non_assign_op ctx op e1 e2 true true with_type p + with Not_found -> + typing_error "This expression cannot be accessed for writing" p + end + | AKUsingField _ | AKSafeNav _ -> + typing_error "Invalid operation" p | AKExpr e -> let e,vr = process_lhs_expr ctx "lhs" e in - let e_rhs = type_binop2 ctx op e e2 true (WithType.with_type e.etype) p in + let e_rhs = type_binop2 ctx op e e2 true WithType.value p in assign vr e e_rhs | AKField fa -> let vr = new value_reference ctx in @@ -718,7 +695,7 @@ let type_assign_op ctx op e1 e2 with_type p = let t_lhs,e_rhs = field_rhs op fa.fa_field ef in set vr sea.se_access t_lhs e_rhs [ef] | AKAccess(a,tl,c,ebase,ekey) -> - let cf_get,tf_get,r_get,ekey,_ = AbstractCast.find_array_access ctx a tl ekey None p in + let cf_get,tf_get,r_get,ekey = AbstractCast.find_array_read_access ctx a tl ekey p in (* bind complex keys to a variable so they do not make it into the output twice *) let save = save_locals ctx in let maybe_bind_to_temp e = match Optimizer.make_constant_expression ctx e with @@ -730,13 +707,12 @@ let type_assign_op ctx op e1 e2 with_type p = in let ekey,ekey' = maybe_bind_to_temp ekey in let ebase,ebase' = maybe_bind_to_temp ebase in - let eget = mk_array_get_call ctx (cf_get,tf_get,r_get,ekey,None) c ebase p in - let eget = type_binop2 ctx op eget e2 true (WithType.with_type eget.etype) p in + let eget = mk_array_get_call ctx (cf_get,tf_get,r_get,ekey) c ebase p in + let eget = type_binop2 ctx op eget e2 true WithType.value p in let vr = new value_reference ctx in let eget = BinopResult.to_texpr vr eget (fun e -> e) in unify ctx eget.etype r_get p; - let cf_set,tf_set,r_set,ekey,eget = AbstractCast.find_array_access ctx a tl ekey (Some eget) p in - let eget = match eget with None -> die "" __LOC__ | Some e -> e in + let cf_set,tf_set,r_set,ekey,eget = AbstractCast.find_array_write_access ctx a tl ekey eget p in let et = type_module_type ctx (TClassDecl c) None p in let e = match cf_set.cf_expr,cf_get.cf_expr with | None,None -> @@ -752,7 +728,7 @@ let type_assign_op ctx op e1 e2 with_type p = | el -> mk (TBlock el) r_set p end | _ -> - error "Invalid array access getter/setter combination" p + typing_error "Invalid array access getter/setter combination" p in save(); vr#to_texpr e @@ -772,7 +748,7 @@ let type_binop ctx op e1 e2 is_assign_op with_type p = | OpAssign -> type_assign ctx e1 e2 with_type p | OpAssignOp (OpBoolAnd | OpBoolOr) -> - error "The operators ||= and &&= are not supported" p + typing_error "The operators ||= and &&= are not supported" p | OpAssignOp op -> type_assign_op ctx op e1 e2 with_type p | _ -> @@ -815,7 +791,7 @@ let type_unop ctx op flag e with_type p = raise Not_found in let unexpected_spread p = - error "Spread unary operator is only allowed for unpacking the last argument in a call with rest arguments" p + typing_error "Spread unary operator is only allowed for unpacking the last argument in a call with rest arguments" p in let make e = let check_int () = @@ -829,7 +805,7 @@ let type_unop ctx op flag e with_type p = in let t = match op with | Not -> - if flag = Postfix then error "Postfix ! is not supported" p; + if flag = Postfix then Common.display_error ctx.com "Postfix ! is not supported" p; unify ctx e.etype ctx.t.tbool e.epos; ctx.t.tbool | NegBits -> @@ -857,7 +833,7 @@ let type_unop ctx op flag e with_type p = unexpected_spread p | Not | Neg | NegBits -> let access_get = !type_access_ref ctx (fst e) (snd e) MGet WithType.value (* WITHTYPETODO *) in - let e = acc_get ctx access_get p in + let e = acc_get ctx access_get in find_overload_or_make e | Increment | Decrement -> let binop = if op = Increment then OpAdd else OpSub in @@ -871,7 +847,7 @@ let type_unop ctx op flag e with_type p = in let read_on vr ef fa = let access_get = type_field_default_cfg ctx ef fa.fa_field.cf_name p MGet WithType.value in - let e_lhs = acc_get ctx access_get p in + let e_lhs = acc_get ctx access_get in let e_lhs,e_out = maybe_tempvar_postfix vr e_lhs in e_lhs,e_out in @@ -881,8 +857,12 @@ let type_unop ctx op flag e with_type p = in let access_set = !type_access_ref ctx (fst e) (snd e) (MSet None) WithType.value (* WITHTYPETODO *) in match access_set with - | AKNo name -> - error ("The field or identifier " ^ name ^ " is not accessible for writing") p + | AKNo(acc,p) -> + begin try + try_abstract_unop_overloads (acc_get ctx acc) + with Not_found -> + typing_error "This expression cannot be accessed for writing" p + end | AKExpr e -> find_overload_or_make e | AKField fa -> @@ -895,7 +875,7 @@ let type_unop ctx op flag e with_type p = find_overload_or_make e,None | _ -> let e_set = FieldAccess.get_field_expr {fa with fa_on = ef} FWrite in - let e_lhs = acc_get ctx access_get p in + let e_lhs = acc_get ctx access_get in let e_lhs,e_out = maybe_tempvar_postfix vr e_lhs in let e_op = mk (TBinop(binop,e_lhs,e_one)) e_lhs.etype p in mk (TBinop(OpAssign,e_set,e_op)) e_set.etype p,e_out @@ -924,7 +904,7 @@ let type_unop ctx op flag e with_type p = let evar_key = mk (TVar(v_key,Some ekey)) ctx.com.basic.tvoid ekey.epos in let ekey = mk (TLocal v_key) ekey.etype ekey.epos in (* get *) - let e_get = mk_array_get_call ctx (AbstractCast.find_array_access_raise ctx a tl ekey None p) c ebase p in + let e_get = mk_array_get_call ctx (AbstractCast.find_array_read_access_raise ctx a tl ekey p) c ebase p in let v_get = alloc_var VGenerated "tmp" e_get.etype e_get.epos in let ev_get = mk (TLocal v_get) v_get.v_type p in let evar_get = mk (TVar(v_get,Some e_get)) ctx.com.basic.tvoid p in @@ -932,12 +912,12 @@ let type_unop ctx op flag e with_type p = let e_one = mk (TConst (TInt (Int32.of_int 1))) ctx.com.basic.tint p in let e_op = mk (TBinop((if op = Increment then OpAdd else OpSub),ev_get,e_one)) ev_get.etype p in (* set *) - let e_set = mk_array_set_call ctx (AbstractCast.find_array_access_raise ctx a tl ekey (Some e_op) p) c ebase p in + let e_set = mk_array_set_call ctx (AbstractCast.find_array_write_access_raise ctx a tl ekey e_op p) c ebase p in let el = evar_key :: evar_get :: e_set :: (if flag = Postfix then [ev_get] else []) in mk (TBlock el) e_set.etype p with Not_found -> - let e = mk_array_get_call ctx (AbstractCast.find_array_access ctx a tl ekey None p) c ebase p in + let e = mk_array_get_call ctx (AbstractCast.find_array_read_access ctx a tl ekey p) c ebase p in find_overload_or_make e end - | AKUsingField _ | AKResolve _ -> - error "Invalid operation" p \ No newline at end of file + | AKUsingField _ | AKResolve _ | AKSafeNav _ -> + typing_error "Invalid operation" p diff --git a/src/typing/overloadResolution.ml b/src/typing/overloadResolution.ml index 5d1a8308..bd16f135 100644 --- a/src/typing/overloadResolution.ml +++ b/src/typing/overloadResolution.ml @@ -92,7 +92,7 @@ let maybe_resolve_instance_overload is_ctor map_type c cf el = resolve_instance_overload is_ctor map_type c cf.cf_name el else match unify_cf map_type c cf el with | Some fcc -> Some (fcc.fc_data) - | None -> Some(c,cf,List.map snd cf.cf_params) + | None -> Some(c,cf,extract_param_types cf.cf_params) let maybe_resolve_constructor_overload c tl el = let cf,c,tl = get_constructor_class c tl in diff --git a/src/typing/strictMeta.ml b/src/typing/strictMeta.ml new file mode 100644 index 00000000..99b198f1 --- /dev/null +++ b/src/typing/strictMeta.ml @@ -0,0 +1,154 @@ +open Globals +open Ast +open Type +open Common +open Typecore + +let get_native_repr md pos = + let path, meta = match md with + | TClassDecl cl -> cl.cl_path, cl.cl_meta + | TEnumDecl e -> e.e_path, e.e_meta + | TTypeDecl t -> t.t_path, t.t_meta + | TAbstractDecl a -> a.a_path, a.a_meta + in + let rec loop acc = function + | (Meta.JavaCanonical,[EConst(String(pack,_)),_; EConst(String(name,_)),_],_) :: _ -> + ExtString.String.nsplit pack ".", name + | (Meta.Native,[EConst(String(name,_)),_],_) :: meta -> + loop (Ast.parse_path name) meta + | _ :: meta -> + loop acc meta + | [] -> + acc + in + let pack, name = loop path meta in + match pack with + | [] -> + (EConst(Ident(name)), pos) + | hd :: tl -> + let rec loop pack expr = match pack with + | hd :: tl -> + loop tl (efield(expr,hd),pos) + | [] -> + (efield(expr,name),pos) + in + loop tl (EConst(Ident(hd)),pos) + +let rec process_meta_argument ?(toplevel=true) ctx expr = match expr.eexpr with + | TField(e,f) -> + (efield(process_meta_argument ~toplevel:false ctx e,field_name f),expr.epos) + | TConst(TInt i) -> + (EConst(Int (Int32.to_string i, None)), expr.epos) + | TConst(TFloat f) -> + (EConst(Float (f, None)), expr.epos) + | TConst(TString s) -> + (EConst(String(s,SDoubleQuotes)), expr.epos) + | TConst TNull -> + (EConst(Ident "null"), expr.epos) + | TConst(TBool b) -> + (EConst(Ident (string_of_bool b)), expr.epos) + | TCast(e,_) | TMeta(_,e) | TParenthesis(e) -> + process_meta_argument ~toplevel ctx e + | TTypeExpr md when toplevel -> + let p = expr.epos in + if ctx.com.platform = Cs then + (ECall( (EConst(Ident "typeof"), p), [get_native_repr md expr.epos] ), p) + else + (efield(get_native_repr md expr.epos, "class"), p) + | TTypeExpr md -> + get_native_repr md expr.epos + | _ -> + display_error ctx.com "This expression is too complex to be a strict metadata argument" expr.epos; + (EConst(Ident "null"), expr.epos) + +let handle_fields ctx fields_to_check with_type_expr = + List.map (fun ((name,_,_),expr) -> + let pos = snd expr in + let field = (efield(with_type_expr,name), pos) in + let fieldexpr = (EConst(Ident name),pos) in + let left_side = match ctx.com.platform with + | Cs -> field + | Java -> (ECall(field,[]),pos) + | _ -> die "" __LOC__ + in + + let left = type_expr ctx left_side NoValue in + let right = type_expr ctx expr (WithType.with_type left.etype) in + unify ctx left.etype right.etype (snd expr); + (EBinop(Ast.OpAssign,fieldexpr,process_meta_argument ctx right), pos) + ) fields_to_check + +let make_meta ctx texpr extra = + match texpr.eexpr with + | TNew(c,_,el) -> + ECall(get_native_repr (TClassDecl c) texpr.epos, (List.map (process_meta_argument ctx) el) @ extra), texpr.epos + | TTypeExpr(md) -> + ECall(get_native_repr md texpr.epos, extra), texpr.epos + | _ -> + display_error ctx.com "Unexpected expression" texpr.epos; die "" __LOC__ + +let get_strict_meta ctx meta params pos = + let pf = ctx.com.platform in + let changed_expr, fields_to_check, ctype = match params with + | [ECall(ef, el),p] -> + let tpath = field_to_type_path ctx.com ef in + begin match pf with + | Cs -> + let el, fields = match List.rev el with + | (EObjectDecl(decl),_) :: el -> + List.rev el, decl + | _ -> + el, [] + in + (ENew((tpath,snd ef), el), p), fields, CTPath tpath + | Java -> + let fields = match el with + | [EObjectDecl(fields),_] -> + fields + | [] -> + [] + | (_,p) :: _ -> + display_error ctx.com "Object declaration expected" p; + [] + in + ef, fields, CTPath tpath + | _ -> + Error.typing_error "@:strict is not supported on this target" p + end + | [EConst(Ident i),p as expr] -> + let tpath = { tpackage=[]; tname=i; tparams=[]; tsub=None } in + if pf = Cs then + (ENew((tpath,p), []), p), [], CTPath tpath + else + expr, [], CTPath tpath + | [ (EField(_),p as field) ] -> + let tpath = field_to_type_path ctx.com field in + if pf = Cs then + (ENew((tpath,p), []), p), [], CTPath tpath + else + field, [], CTPath tpath + | _ -> + display_error ctx.com "A @:strict metadata must contain exactly one parameter. Please check the documentation for more information" pos; + raise Exit + in + let texpr = type_expr ctx changed_expr NoValue in + let with_type_expr = (ECheckType( (EConst (Ident "null"), pos), (ctype,null_pos) ), pos) in + let extra = handle_fields ctx fields_to_check with_type_expr in + meta, [make_meta ctx texpr extra], pos + +let check_strict_meta ctx metas = + let pf = ctx.com.platform in + match pf with + | Cs | Java -> + let ret = ref [] in + List.iter (function + | Meta.AssemblyStrict,params,pos -> (try + ret := get_strict_meta ctx Meta.AssemblyMeta params pos :: !ret + with | Exit -> ()) + | Meta.Strict,params,pos -> (try + ret := get_strict_meta ctx Meta.Meta params pos :: !ret + with | Exit -> ()) + | _ -> () + ) metas; + !ret + | _ -> [] \ No newline at end of file diff --git a/src/typing/typeload.ml b/src/typing/typeload.ml index 29a6217f..4ab8fd5c 100644 --- a/src/typing/typeload.ml +++ b/src/typing/typeload.ml @@ -58,16 +58,16 @@ let check_field_access ctx cff = try let _,p2 = List.find (fun (access',_) -> access = access') acc in if p1 <> null_pos && p2 <> null_pos then begin - display_error ctx (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access)) p1; - display_error ctx (compl_msg "Previously defined here") p2; + display_error ctx.com (Printf.sprintf "Duplicate access modifier %s" (Ast.s_access access)) p1; + display_error ~depth:1 ctx.com (compl_msg "Previously defined here") p2; end; loop p1 acc l with Not_found -> match access with | APublic | APrivate -> begin try let _,p2 = List.find (fun (access',_) -> match access' with APublic | APrivate -> true | _ -> false) acc in - display_error ctx (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1; - display_error ctx (compl_msg "Conflicts with this") p2; + display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1; + display_error ~depth:1 ctx.com (compl_msg "Conflicts with this") p2; loop p1 acc l with Not_found -> loop p1 ((access,p1) :: acc) l @@ -92,14 +92,14 @@ let find_type_in_module_raise ctx m tname p = let infos = t_infos mt in if snd infos.mt_path = tname then if ctx.m.curmod != infos.mt_module && infos.mt_private then - raise_error (Type_not_found (m.m_path,tname,Private_type)) p + raise_typing_error (Type_not_found (m.m_path,tname,Private_type)) p else true else false ) m.m_types with Not_found -> - raise_error (Type_not_found (m.m_path,tname,Not_defined)) p + raise_typing_error (Type_not_found (m.m_path,tname,Not_defined)) p (* raises Module_not_found or Type_not_found *) let load_type_raise ctx mpath tname p = @@ -109,7 +109,7 @@ let load_type_raise ctx mpath tname p = (* raises Not_found *) let load_type ctx mpath tname p = try load_type_raise ctx mpath tname p -with Error((Module_not_found _ | Type_not_found _),p2) when p = p2 -> +with Error((Module_not_found _ | Type_not_found _),p2,_) when p = p2 -> raise Not_found (** since load_type_def and load_instance are used in PASS2, they should not access the structure of a type **) @@ -126,7 +126,7 @@ let find_type_in_current_module_context ctx pack name = List.find path_matches ctx.m.curmod.m_types with Not_found -> (* Check the local imports *) - let t,pi = List.find (fun (t2,pi) -> path_matches t2) ctx.m.module_types in + let t,pi = List.find (fun (t2,pi) -> path_matches t2) ctx.m.module_imports in ImportHandling.mark_import_position ctx pi; t @@ -142,7 +142,7 @@ let find_in_wildcard_imports ctx mname p f = let m = try ctx.g.do_load_module ctx path p - with Error (Module_not_found mpath,_) when mpath = path -> + with Error (Module_not_found mpath,_,_) when mpath = path -> raise Not_found in let r = f m ~resume:true in @@ -163,7 +163,7 @@ let find_in_modules_starting_from_current_package ~resume ctx mname p f = let m = try ctx.g.do_load_module ctx path p - with Error (Module_not_found mpath,_) when resume && mpath = path -> + with Error (Module_not_found mpath,_,_) when resume && mpath = path -> raise Not_found in f m ~resume:resume @@ -172,7 +172,7 @@ let find_in_modules_starting_from_current_package ~resume ctx mname p f = let m = try ctx.g.do_load_module ctx path p - with Error (Module_not_found mpath,_) when mpath = path -> + with Error (Module_not_found mpath,_,_) when mpath = path -> raise Not_found in f m ~resume:true; @@ -200,7 +200,7 @@ let load_unqualified_type_def ctx mname tname p = let load_module ctx path p = try ctx.g.do_load_module ctx path p - with Error (Module_not_found mpath,_) as exc when mpath = path -> + with Error (Module_not_found mpath,_,_) as exc when mpath = path -> match path with | ("std" :: pack, name) -> ctx.g.do_load_module ctx (pack,name) p @@ -254,9 +254,9 @@ let is_redefined ctx cf1 fields p = let cf2 = PMap.find cf1.cf_name fields in let st = s_type (print_context()) in if not (type_iseq cf1.cf_type cf2.cf_type) then begin - display_error ctx ("Cannot redefine field " ^ cf1.cf_name ^ " with different type") p; - display_error ctx ("First type was " ^ (st cf1.cf_type)) cf1.cf_pos; - error ("Second type was " ^ (st cf2.cf_type)) cf2.cf_pos + display_error ctx.com ("Cannot redefine field " ^ cf1.cf_name ^ " with different type") p; + display_error ctx.com ("First type was " ^ (st cf1.cf_type)) cf1.cf_pos; + typing_error ("Second type was " ^ (st cf2.cf_type)) cf2.cf_pos end else true with Not_found -> @@ -270,7 +270,7 @@ let make_extension_type ctx tl = else fields ) a.a_fields fields | _ -> - error "Can only extend structures" p + typing_error "Can only extend structures" p in let fields = List.fold_left mk_extension PMap.empty tl in let tl = List.map (fun (t,_) -> t) tl in @@ -285,16 +285,16 @@ let check_param_constraints ctx t map c p = List.iter (fun ti -> let ti = map ti in try - unify_raise ctx t ti p - with Error(Unify l,p) -> + unify_raise t ti p + with Error(Unify l,p,depth) -> let fail() = - if not ctx.untyped then display_error ctx (error_msg (Unify (Constraint_failure (s_type_path c.cl_path) :: l))) p; + if not ctx.untyped then located_display_error ~depth ctx.com (error_msg p (Unify (Constraint_failure (s_type_path c.cl_path) :: l))); in match follow t with | TInst({cl_kind = KExpr e},_) -> let e = type_expr {ctx with locals = PMap.empty} e (WithType.with_type ti) in - begin try unify_raise ctx e.etype ti p - with Error (Unify _,_) -> fail() end + begin try unify_raise e.etype ti p + with Error (Unify _,_,_) -> fail() end | _ -> fail() @@ -304,8 +304,8 @@ let check_param_constraints ctx t map c p = let rec load_instance' ctx (t,p) allow_no_params = let t = try if t.tpackage <> [] || t.tsub <> None then raise Not_found; - let pt = List.assoc t.tname ctx.type_params in - if t.tparams <> [] then error ("Class type parameter " ^ t.tname ^ " can't have parameters") p; + let pt = lookup_param t.tname ctx.type_params in + if t.tparams <> [] then typing_error ("Class type parameter " ^ t.tname ^ " can't have parameters") p; pt with Not_found -> let mt = load_type_def ctx p t in @@ -328,15 +328,15 @@ let rec load_instance' ctx (t,p) allow_no_params = | _ -> false,false,false in let types , path , f = ctx.g.do_build_instance ctx mt p in - let is_rest = is_generic_build && (match types with ["Rest",_] -> true | _ -> false) in + let is_rest = is_generic_build && (match types with [{ttp_name="Rest"}] -> true | _ -> false) in if allow_no_params && t.tparams = [] && not is_rest then begin let monos = Monomorph.spawn_constrained_monos (fun t -> t) types in f (monos) end else if path = ([],"Dynamic") then match t.tparams with | [] -> t_dynamic - | [TPType t] -> TDynamic (load_complex_type ctx true t) - | _ -> error "Too many parameters for Dynamic" p + | [TPType t] -> TDynamic (Some (load_complex_type ctx true t)) + | _ -> typing_error "Too many parameters for Dynamic" p else begin let is_java_rest = ctx.com.platform = Java && is_extern in let is_rest = is_rest || is_java_rest in @@ -345,8 +345,8 @@ let rec load_instance' ctx (t,p) allow_no_params = | TPExpr e -> let name = (match fst e with | EConst (String(s,_)) -> "S" ^ s - | EConst (Int i) -> "I" ^ i - | EConst (Float f) -> "F" ^ f + | EConst (Int (_,_) as c) -> "I" ^ s_constant c + | EConst (Float (_,_) as c) -> "F" ^ s_constant c | EDisplay _ -> ignore(type_expr ctx e WithType.value); "Expr" @@ -359,7 +359,7 @@ let rec load_instance' ctx (t,p) allow_no_params = in let checks = DynArray.create () in let rec loop tl1 tl2 is_rest = match tl1,tl2 with - | t :: tl1,(name,t2) :: tl2 -> + | t :: tl1,({ttp_name=name;ttp_type=t2}) :: tl2 -> let t,pt = load_param t in let check_const c = let is_expression = (match t with TInst ({ cl_kind = KExpr _ },_) -> true | _ -> false) in @@ -367,9 +367,9 @@ let rec load_instance' ctx (t,p) allow_no_params = let accepts_expression = name = "Rest" in if is_expression then begin if not expects_expression && not accepts_expression then - error "Constant value unexpected here" p + typing_error "Constant value unexpected here" p end else if expects_expression then - error "Type parameter is expected to be a constant value" p + typing_error "Type parameter is expected to be a constant value" p in let is_rest = is_rest || name = "Rest" && is_generic_build in let t = match follow t2 with @@ -385,23 +385,28 @@ let rec load_instance' ctx (t,p) allow_no_params = t :: loop tl1 tl2 is_rest | [],[] -> [] - | [],["Rest",_] when is_generic_build -> + | [],[{ttp_name="Rest"}] when is_generic_build -> [] - | [],(_,t) :: tl -> + | [],({ttp_type=t;ttp_default=def}) :: tl -> if is_java_rest then t_dynamic :: loop [] tl is_rest - else if ctx.com.display.dms_error_policy = EPIgnore then - t :: loop [] tl is_rest - else - error ("Not enough type parameters for " ^ s_type_path path) p + else begin match def with + | None -> + if ignore_error ctx.com then + t :: loop [] tl is_rest + else + typing_error ("Not enough type parameters for " ^ s_type_path path) p + | Some t -> + t :: loop [] tl is_rest + end | t :: tl,[] -> let t,pt = load_param t in if is_rest then t :: loop tl [] true - else if ctx.com.display.dms_error_policy = EPIgnore then + else if ignore_error ctx.com then [] else - error ("Too many type parameters for " ^ s_type_path path) pt + typing_error ("Too many type parameters for " ^ s_type_path path) pt in let params = loop t.tparams types false in if not is_rest then begin @@ -432,9 +437,9 @@ and load_instance ctx ?(allow_display=false) ((_,pn) as tp) allow_no_params = let t = load_instance' ctx tp allow_no_params in if allow_display then DisplayEmitter.check_display_type ctx t tp; t - with Error (Module_not_found path,_) when ctx.macro_depth <= 0 && (ctx.com.display.dms_kind = DMDefault) && DisplayPosition.display_position#enclosed_in pn -> + with Error (Module_not_found path,_,_) when ctx.macro_depth <= 0 && (ctx.com.display.dms_kind = DMDefault) && DisplayPosition.display_position#enclosed_in pn -> let s = s_type_path path in - DisplayToplevel.collect_and_raise ctx TKType NoValue CRTypeHint (s,pn) {pn with pmin = pn.pmax - String.length s;} + DisplayToplevel.collect_and_raise ctx TKType NoValue CRTypeHint (s,pn) (patch_string_pos pn s) (* build an instance from a complex type @@ -444,13 +449,13 @@ and load_complex_type' ctx allow_display (t,p) = | CTParent t -> load_complex_type ctx allow_display t | CTPath { tpackage = ["$"]; tname = "_hx_mono" } -> spawn_monomorph ctx p | CTPath t -> load_instance ~allow_display ctx (t,p) false - | CTOptional _ -> error "Optional type not allowed here" p - | CTNamed _ -> error "Named type not allowed here" p + | CTOptional _ -> typing_error "Optional type not allowed here" p + | CTNamed _ -> typing_error "Named type not allowed here" p | CTIntersection tl -> let tl = List.map (fun (t,pn) -> try (load_complex_type ctx allow_display (t,pn),pn) - with DisplayException(DisplayFields Some({fkind = CRTypeHint} as r)) -> + with DisplayException(DisplayFields ({fkind = CRTypeHint} as r)) -> let l = List.filter (fun item -> match item.ci_kind with | ITType({kind = Struct},_) -> true | _ -> false @@ -472,13 +477,13 @@ and load_complex_type' ctx allow_display (t,p) = let mk_extension (t,p) = match follow t with | TInst ({cl_kind = KTypeParameter _},_) -> - error "Cannot structurally extend type parameters" p + typing_error "Cannot structurally extend type parameters" p | TMono _ -> - error "Loop found in cascading signatures definitions. Please change order/import" p + typing_error "Loop found in cascading signatures definitions. Please change order/import" p | TAnon a2 -> PMap.iter (fun _ cf -> ignore(is_redefined ctx cf a2.a_fields p)) a.a_fields; mk_anon ~fields:(PMap.foldi PMap.add a.a_fields a2.a_fields) (ref (Extend [t])) - | _ -> error "Can only extend structures" p + | _ -> typing_error "Can only extend structures" p in let loop (t,p) = match follow t with | TAnon a2 -> @@ -487,12 +492,12 @@ and load_complex_type' ctx allow_display (t,p) = a.a_fields <- PMap.add f cf a.a_fields ) a2.a_fields | _ -> - error "Can only extend structures" p + typing_error "Can only extend structures" p in let il = List.map (fun (t,pn) -> try (load_instance ctx ~allow_display (t,pn) false,pn) - with DisplayException(DisplayFields Some({fkind = CRTypeHint} as r)) -> + with DisplayException(DisplayFields ({fkind = CRTypeHint} as r)) -> let l = List.filter (fun item -> match item.ci_kind with | ITType({kind = Struct},_) -> true | _ -> false @@ -519,16 +524,17 @@ and load_complex_type' ctx allow_display (t,p) = let displayed_field = ref None in let rec loop acc f = let n = fst f.cff_name in + let pf = snd f.cff_name in let p = f.cff_pos in - if PMap.mem n acc then error ("Duplicate field declaration : " ^ n) p; + if PMap.mem n acc then typing_error ("Duplicate field declaration : " ^ n) pf; let topt = function - | None -> error ("Explicit type required for field " ^ n) p + | None -> typing_error ("Explicit type required for field " ^ n) p | Some t -> load_complex_type ctx allow_display t in - if n = "new" then ctx.com.warning "Structures with new are deprecated, use haxe.Constraints.Constructible instead" p; + if n = "new" then warning ctx WDeprecated "Structures with new are deprecated, use haxe.Constraints.Constructible instead" p; let no_expr = function | None -> () - | Some (_,p) -> error "Expression not allowed here" p + | Some (_,p) -> typing_error "Expression not allowed here" p in let pub = ref true in let dyn = ref false in @@ -541,19 +547,19 @@ and load_complex_type' ctx allow_display (t,p) = | APrivate -> let p = pos a in if Filename.basename p.pfile <> "NativeIterable.hx" then (* Terrible workaround for #7436 *) - ctx.com.warning "private structure fields are deprecated" p; + warning ctx WDeprecated "private structure fields are deprecated" p; pub := false; | ADynamic when (match f.cff_kind with FFun _ -> true | _ -> false) -> dyn := true | AFinal -> final := true - | AStatic | AOverride | AInline | ADynamic | AMacro | AExtern | AAbstract | AOverload as a -> error ("Invalid access " ^ Ast.s_access a) p + | AStatic | AOverride | AInline | ADynamic | AMacro | AExtern | AAbstract | AOverload as a -> typing_error ("Invalid access " ^ Ast.s_access a) p ) f.cff_access; let t , access = (match f.cff_kind with | FVar(t,e) when !final -> no_expr e; - let t = (match t with None -> error "Type required for structure property" p | Some t -> t) in + let t = (match t with None -> typing_error "Type required for structure property" p | Some t -> t) in load_complex_type ctx allow_display t, Var { v_read = AccNormal; v_write = AccNever } | FVar (Some (CTPath({tpackage=[];tname="Void"}),_), _) | FProp (_,_,Some (CTPath({tpackage=[];tname="Void"}),_),_) -> - error "Fields of type Void are not allowed in structures" p + typing_error "Fields of type Void are not allowed in structures" p | FVar (t, e) -> no_expr e; topt t, Var { v_read = AccNormal; v_write = AccNormal } @@ -579,9 +585,9 @@ and load_complex_type' ctx allow_display (t,p) = | x when get && x = "get_" ^ n -> AccCall | x when not get && x = "set_" ^ n -> AccCall | _ -> - error "Custom property access is no longer supported in Haxe 3" f.cff_pos; + typing_error "Custom property access is no longer supported in Haxe 3" f.cff_pos; in - let t = (match t with None -> error "Type required for structure property" p | Some t -> t) in + let t = (match t with None -> typing_error "Type required for structure property" p | Some t -> t) in load_complex_type ctx allow_display t, Var { v_read = access i1 true; v_write = access i2 false } ) in let t = if Meta.has Meta.Optional f.cff_meta then ctx.t.tnull t else t in @@ -622,11 +628,11 @@ and load_complex_type' ctx allow_display (t,p) = and load_complex_type ctx allow_display (t,pn) = try load_complex_type' ctx allow_display (t,pn) - with Error(Module_not_found(([],name)),p) as exc -> - if Diagnostics.is_diagnostics_run ctx.com p then begin + with Error(Module_not_found(([],name)),p,_) as exc -> + if Diagnostics.error_in_diagnostics_run ctx.com p then begin delay ctx PForce (fun () -> DisplayToplevel.handle_unresolved_identifier ctx name p true); t_dynamic - end else if ctx.com.display.dms_display && not (DisplayPosition.display_position#enclosed_in pn) then + end else if ignore_error ctx.com && not (DisplayPosition.display_position#enclosed_in pn) then t_dynamic else raise exc @@ -641,18 +647,23 @@ and init_meta_overloads ctx co cf = cf.cf_meta <- List.filter (fun m -> match m with | (Meta.Overload,[(EFunction (kind,f),p)],_) -> - (match kind with FKNamed _ -> error "Function name must not be part of @:overload" p | _ -> ()); - (match f.f_expr with Some (EBlock [], _) -> () | _ -> error "Overload must only declare an empty method body {}" p); + (match kind with FKNamed _ -> typing_error "Function name must not be part of @:overload" p | _ -> ()); + (match f.f_expr with Some (EBlock [], _) -> () | _ -> typing_error "Overload must only declare an empty method body {}" p); (match cf.cf_kind with - | Method MethInline -> error "Cannot @:overload inline function" p + | Method MethInline -> typing_error "Cannot @:overload inline function" p | _ -> ()); let old = ctx.type_params in - (match cf.cf_params with - | [] -> () - | l -> ctx.type_params <- List.filter (fun t -> not (List.mem t l)) ctx.type_params); - let params = (!type_function_params_rec) ctx f cf.cf_name p in + begin match cf.cf_params with + | [] -> + () + | l -> + ctx.type_params <- List.filter (fun t -> + not (List.mem t l) (* TODO: this still looks suspicious *) + ) ctx.type_params + end; + let params : type_params = (!type_function_params_rec) ctx f cf.cf_name p in ctx.type_params <- params @ ctx.type_params; - let topt = function None -> error "Explicit type required" p | Some t -> load_complex_type ctx true t in + let topt = function None -> typing_error "Explicit type required" p | Some t -> load_complex_type ctx true t in let args = List.map (fun ((a,_),opt,_,t,cto) -> @@ -669,15 +680,15 @@ and init_meta_overloads ctx co cf = false | (Meta.Overload,[],_) when ctx.com.config.pf_overload -> add_class_field_flag cf CfOverload; - let topt (n,_,t) = match t with | TMono t when t.tm_type = None -> error ("Explicit type required for overload functions\n... For function argument '" ^ n ^ "'") cf.cf_pos | _ -> () in + let topt (n,_,t) = match t with | TMono t when t.tm_type = None -> typing_error ("Explicit type required for overload functions\n... For function argument '" ^ n ^ "'") cf.cf_pos | _ -> () in (match follow cf.cf_type with | TFun (args,_) -> List.iter topt args | _ -> () (* could be a variable *)); true | (Meta.Overload,[],p) -> - error "This platform does not support this kind of overload declaration. Try @:overload(function()... {}) instead" p + typing_error "This platform does not support this kind of overload declaration. Try @:overload(function()... {}) instead" p | (Meta.Overload,_,p) -> - error "Invalid @:overload metadata format" p + typing_error "Invalid @:overload metadata format" p | _ -> true ) cf.cf_meta; @@ -689,11 +700,11 @@ let hide_params ctx = let old_deps = ctx.g.std.m_extra.m_deps in ctx.m <- { curmod = ctx.g.std; - module_types = []; + module_imports = []; module_using = []; module_globals = PMap.empty; wildcard_packages = []; - module_imports = []; + import_statements = []; }; ctx.type_params <- []; (fun() -> @@ -726,7 +737,7 @@ let t_iterator ctx = add_dependency ctx.m.curmod t.t_module; if List.length t.t_params <> 1 then die "" __LOC__; let pt = mk_mono() in - apply_params t.t_params [pt] t.t_type, pt + apply_typedef t [pt], pt | _ -> die "" __LOC__ @@ -743,65 +754,54 @@ let load_type_hint ?(opt=false) ctx pcur t = (* ---------------------------------------------------------------------- *) (* PASS 1 & 2 : Module and Class Structure *) -let field_to_type_path ctx e = - let rec loop e pack name = match e with - | EField(e,f),p when Char.lowercase (String.get f 0) <> String.get f 0 -> (match name with - | [] | _ :: [] -> - loop e pack (f :: name) - | _ -> (* too many name paths *) - display_error ctx ("Unexpected " ^ f) p; - raise Exit) - | EField(e,f),_ -> - loop e (f :: pack) name - | EConst(Ident f),_ -> - let pack, name, sub = match name with - | [] -> - let fchar = String.get f 0 in - if Char.uppercase fchar = fchar then - pack, f, None - else begin - display_error ctx "A class name must start with an uppercase letter" (snd e); - raise Exit - end - | [name] -> - f :: pack, name, None - | [name; sub] -> - f :: pack, name, Some sub - | _ -> - die "" __LOC__ - in - { tpackage=pack; tname=name; tparams=[]; tsub=sub } - | _,pos -> - display_error ctx "Unexpected expression when building strict meta" pos; - raise Exit - in - loop e [] [] +type type_param_host = + | TPHType + | TPHConstructor + | TPHMethod + | TPHEnumConstructor -let rec type_type_param ?(enum_constructor=false) ctx path get_params p tp = +let rec type_type_param ctx host path get_params p tp = let n = fst tp.tp_name in let c = mk_class ctx.m.curmod (fst path @ [snd path],n) (pos tp.tp_name) (pos tp.tp_name) in - c.cl_params <- type_type_params ctx c.cl_path get_params p tp.tp_params; + c.cl_params <- type_type_params ctx host c.cl_path get_params p tp.tp_params; c.cl_kind <- KTypeParameter []; c.cl_meta <- tp.Ast.tp_meta; - if enum_constructor then c.cl_meta <- (Meta.EnumConstructorParam,[],null_pos) :: c.cl_meta; - let t = TInst (c,List.map snd c.cl_params) in + if host = TPHEnumConstructor then c.cl_meta <- (Meta.EnumConstructorParam,[],null_pos) :: c.cl_meta; + let t = TInst (c,extract_param_types c.cl_params) in if ctx.is_display_file && DisplayPosition.display_position#enclosed_in (pos tp.tp_name) then DisplayEmitter.display_type ctx t (pos tp.tp_name); + let default = match tp.tp_default with + | None -> + None + | Some ct -> + let t = load_complex_type ctx true ct in + begin match host with + | TPHType -> + () + | TPHConstructor + | TPHMethod + | TPHEnumConstructor -> + display_error ctx.com "Default type parameters are only supported on types" (pos ct) + end; + Some t + in match tp.tp_constraints with | None -> - n, t + mk_type_param n t default | Some th -> let r = exc_protect ctx (fun r -> r := lazy_processing (fun() -> t); let ctx = { ctx with type_params = ctx.type_params @ get_params() } in - let constr = match fst th with + let rec loop th = match fst th with | CTIntersection tl -> List.map (load_complex_type ctx true) tl + | CTParent ct -> loop ct | _ -> [load_complex_type ctx true th] in + let constr = loop th in (* check against direct recursion *) let rec loop t = match follow t with - | TInst (c2,_) when c == c2 -> error "Recursive constraint parameter is not allowed" p + | TInst (c2,_) when c == c2 -> typing_error "Recursive constraint parameter is not allowed" p | TInst ({ cl_kind = KTypeParameter cl },_) -> List.iter loop cl | _ -> @@ -811,25 +811,25 @@ let rec type_type_param ?(enum_constructor=false) ctx path get_params p tp = c.cl_kind <- KTypeParameter constr; t ) "constraint" in - n, TLazy r + mk_type_param n (TLazy r) default -and type_type_params ?(enum_constructor=false) ctx path get_params p tpl = +and type_type_params ctx host path get_params p tpl = let names = ref [] in List.map (fun tp -> - if List.exists (fun name -> name = fst tp.tp_name) !names then display_error ctx ("Duplicate type parameter name: " ^ fst tp.tp_name) (pos tp.tp_name); + if List.exists (fun name -> name = fst tp.tp_name) !names then display_error ctx.com ("Duplicate type parameter name: " ^ fst tp.tp_name) (pos tp.tp_name); names := (fst tp.tp_name) :: !names; - type_type_param ~enum_constructor ctx path get_params p tp + type_type_param ctx host path get_params p tp ) tpl let load_core_class ctx c = let ctx2 = (match ctx.g.core_api with | None -> - let com2 = Common.clone ctx.com in + let com2 = Common.clone ctx.com ctx.com.is_macro_context in com2.defines.Define.values <- PMap.empty; Common.define com2 Define.CoreApi; Common.define com2 Define.Sys; Define.raw_define_value com2.defines "target.threaded" "true"; (* hack because we check this in sys.thread classes *) - if ctx.in_macro then Common.define com2 Define.Macro; + if ctx.com.is_macro_context then Common.define com2 Define.Macro; com2.class_path <- ctx.com.std_path; if com2.display.dms_check_core_api then com2.display <- {com2.display with dms_check_core_api = false}; CommonCache.lock_signature com2 "load_core_class"; @@ -854,23 +854,23 @@ let load_core_class ctx c = let init_core_api ctx c = let ccore = load_core_class ctx c in begin try - List.iter2 (fun (n1,t1) (n2,t2) -> match follow t1, follow t2 with + List.iter2 (fun tp1 tp2 -> match follow tp1.ttp_type, follow tp2.ttp_type with | TInst({cl_kind = KTypeParameter l1},_),TInst({cl_kind = KTypeParameter l2},_) -> begin try List.iter2 (fun t1 t2 -> type_eq EqCoreType t2 t1) l1 l2 with | Invalid_argument _ -> - error "Type parameters must have the same number of constraints as core type" c.cl_pos + typing_error "Type parameters must have the same number of constraints as core type" c.cl_pos | Unify_error l -> - display_error ctx ("Type parameter " ^ n2 ^ " has different constraint than in core type") c.cl_pos; - display_error ctx (error_msg (Unify l)) c.cl_pos + display_error ctx.com ("Type parameter " ^ tp2.ttp_name ^ " has different constraint than in core type") c.cl_pos; + located_display_error ctx.com (error_msg c.cl_pos (Unify l)); end | t1,t2 -> Printf.printf "%s %s" (s_type (print_context()) t1) (s_type (print_context()) t2); die "" __LOC__ ) ccore.cl_params c.cl_params; with Invalid_argument _ -> - error "Class must have the same number of type parameters as core type" c.cl_pos + typing_error "Class must have the same number of type parameters as core type" c.cl_pos end; (match c.cl_doc with | None -> c.cl_doc <- ccore.cl_doc @@ -878,11 +878,11 @@ let init_core_api ctx c = let compare_fields f f2 = let p = (match f2.cf_expr with None -> c.cl_pos | Some e -> e.epos) in (try - type_eq EqCoreType (apply_params ccore.cl_params (List.map snd c.cl_params) f.cf_type) f2.cf_type + type_eq EqCoreType (apply_params ccore.cl_params (extract_param_types c.cl_params) f.cf_type) f2.cf_type with Unify_error l -> - display_error ctx ("Field " ^ f.cf_name ^ " has different type than in core type") p; - display_error ctx (error_msg (Unify l)) p); - if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then error ("Field " ^ f.cf_name ^ " has different visibility than core type") p; + display_error ctx.com ("Field " ^ f.cf_name ^ " has different type than in core type") p; + located_display_error ctx.com (error_msg p (Unify l))); + if (has_class_field_flag f2 CfPublic) <> (has_class_field_flag f CfPublic) then typing_error ("Field " ^ f.cf_name ^ " has different visibility than core type") p; (match f2.cf_doc with | None -> f2.cf_doc <- f.cf_doc | Some _ -> ()); @@ -891,25 +891,25 @@ let init_core_api ctx c = | Method MethInline, Method MethNormal -> () (* allow to add 'inline' *) | Method MethNormal, Method MethInline -> () (* allow to disable 'inline' *) | _ -> - error ("Field " ^ f.cf_name ^ " has different property access than core type") p; + typing_error ("Field " ^ f.cf_name ^ " has different property access than core type") p; end; (match follow f.cf_type, follow f2.cf_type with | TFun (pl1,_), TFun (pl2,_) -> - if List.length pl1 != List.length pl2 then error "Argument count mismatch" p; + if List.length pl1 != List.length pl2 then typing_error "Argument count mismatch" p; List.iter2 (fun (n1,_,_) (n2,_,_) -> - if n1 <> n2 then error ("Method parameter name '" ^ n2 ^ "' should be '" ^ n1 ^ "'") p; + if n1 <> n2 then typing_error ("Method parameter name '" ^ n2 ^ "' should be '" ^ n1 ^ "'") p; ) pl1 pl2; | _ -> ()); in let check_fields fcore fl = PMap.iter (fun i f -> if not (has_class_field_flag f CfPublic) then () else - let f2 = try PMap.find f.cf_name fl with Not_found -> error ("Missing field " ^ i ^ " required by core type") c.cl_pos in + let f2 = try PMap.find f.cf_name fl with Not_found -> typing_error ("Missing field " ^ i ^ " required by core type") c.cl_pos in compare_fields f f2; ) fcore; PMap.iter (fun i f -> let p = (match f.cf_expr with None -> c.cl_pos | Some e -> e.epos) in - if (has_class_field_flag f CfPublic) && not (Meta.has Meta.Hack f.cf_meta) && not (PMap.mem f.cf_name fcore) && not (has_class_field_flag f CfOverride) then error ("Public field " ^ i ^ " is not part of core type") p; + if (has_class_field_flag f CfPublic) && not (Meta.has Meta.Hack f.cf_meta) && not (PMap.mem f.cf_name fcore) && not (has_class_field_flag f CfOverride) then typing_error ("Public field " ^ i ^ " is not part of core type") p; ) fl; in check_fields ccore.cl_fields c.cl_fields; @@ -919,43 +919,8 @@ let init_core_api ctx c = | Some cf, _ when not (has_class_field_flag cf CfPublic) -> () | Some f, Some f2 -> compare_fields f f2 | None, Some cf when not (has_class_field_flag cf CfPublic) -> () - | _ -> error "Constructor differs from core type" c.cl_pos) + | _ -> typing_error "Constructor differs from core type" c.cl_pos) let string_list_of_expr_path (e,p) = try string_list_of_expr_path_raise (e,p) - with Exit -> error "Invalid path" p - -let handle_using ctx path p = - let t = match List.rev path with - | (s1,_) :: (s2,_) :: sl -> - if is_lower_ident s2 then mk_type_path ((List.rev (s2 :: List.map fst sl)),s1) - else mk_type_path ~sub:s1 (List.rev (List.map fst sl),s2) - | (s1,_) :: sl -> - mk_type_path (List.rev (List.map fst sl),s1) - | [] -> - DisplayException.raise_fields (DisplayToplevel.collect ctx TKType NoValue true) CRUsing (DisplayTypes.make_subject None {p with pmin = p.pmax}); - in - let types = (match t.tsub with - | None -> - let md = ctx.g.do_load_module ctx (t.tpackage,t.tname) p in - let types = List.filter (fun t -> not (t_infos t).mt_private) md.m_types in - Option.map_default (fun c -> (TClassDecl c) :: types) types md.m_statics - | Some _ -> - let t = load_type_def ctx p t in - [t] - ) in - (* delay the using since we need to resolve typedefs *) - let filter_classes types = - let rec loop acc types = match types with - | td :: l -> - (match resolve_typedef td with - | TClassDecl c | TAbstractDecl({a_impl = Some c}) -> - loop ((c,p) :: acc) l - | td -> - loop acc l) - | [] -> - acc - in - loop [] types - in - types,filter_classes \ No newline at end of file + with Exit -> typing_error "Invalid path" p diff --git a/src/typing/typeloadCheck.ml b/src/typing/typeloadCheck.ml index f7981583..616be2ec 100644 --- a/src/typing/typeloadCheck.ml +++ b/src/typing/typeloadCheck.ml @@ -37,16 +37,17 @@ exception Build_canceled of build_state let is_generic_parameter ctx c = (* first check field parameters, then class parameters *) + let name = snd c.cl_path in try - ignore (List.assoc (snd c.cl_path) ctx.curfield.cf_params); + ignore(lookup_param name ctx.curfield.cf_params); has_class_field_flag ctx.curfield CfGeneric with Not_found -> try - ignore(List.assoc (snd c.cl_path) ctx.type_params); + ignore(lookup_param name ctx.type_params); (match ctx.curclass.cl_kind with | KGeneric -> true | _ -> false); with Not_found -> false -let valid_redefinition ctx f1 t1 f2 t2 = (* child, parent *) +let valid_redefinition ctx map1 map2 f1 t1 f2 t2 = (* child, parent *) let valid t1 t2 = Type.unify t1 t2; if is_null t1 <> is_null t2 || ((follow t1) == t_dynamic && (follow t2) != t_dynamic) then raise (Unify_error [Cannot_unify (t1,t2)]); @@ -61,8 +62,9 @@ let valid_redefinition ctx f1 t1 f2 t2 = (* child, parent *) | [], [] -> t1, t2 | l1, l2 when List.length l1 = List.length l2 -> let to_check = ref [] in - let monos = List.map2 (fun (name,p1) (_,p2) -> - (match follow p1, follow p2 with + (* TPTODO: defaults *) + let monos = List.map2 (fun tp1 tp2 -> + (match follow tp1.ttp_type, follow tp2.ttp_type with | TInst ({ cl_kind = KTypeParameter ct1 } as c1,pl1), TInst ({ cl_kind = KTypeParameter ct2 } as c2,pl2) -> (match ct1, ct2 with | [], [] -> () @@ -71,8 +73,8 @@ let valid_redefinition ctx f1 t1 f2 t2 = (* child, parent *) let check monos = List.iter2 (fun t1 t2 -> try - let t1 = apply_params l1 monos (apply_params c1.cl_params pl1 t1) in - let t2 = apply_params l2 monos (apply_params c2.cl_params pl2 t2) in + let t1 = apply_params l1 monos (apply_params c1.cl_params pl1 (map2 t1)) in + let t2 = apply_params l2 monos (apply_params c2.cl_params pl2 (map1 t2)) in type_eq EqStrict t1 t2 with Unify_error l -> raise (Unify_error (Unify_custom "Constraints differ" :: l)) @@ -82,7 +84,7 @@ let valid_redefinition ctx f1 t1 f2 t2 = (* child, parent *) | _ -> raise (Unify_error [Unify_custom "Different number of constraints"])) | _ -> ()); - TInst (mk_class null_module ([],name) null_pos null_pos,[]) + TInst (mk_class null_module ([],tp1.ttp_name) null_pos null_pos,[]) ) l1 l2 in List.iter (fun f -> f monos) !to_check; apply_params l1 monos t1, apply_params l2 monos t2 @@ -146,12 +148,12 @@ let get_native_name meta = | [] -> raise Not_found | _ -> - error "String expected" mp + typing_error "String expected" mp let check_native_name_override ctx child base = let error base_pos child_pos = - display_error ctx ("Field " ^ child.cf_name ^ " has different @:native value than in superclass") child_pos; - display_error ctx (compl_msg "Base field is defined here") base_pos + display_error ctx.com ("Field " ^ child.cf_name ^ " has different @:native value than in superclass") child_pos; + display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") base_pos in try let child_name, child_pos = get_native_name child.cf_meta in @@ -166,14 +168,14 @@ let check_native_name_override ctx child base = let check_overriding ctx c f = match c.cl_super with | None -> - if has_class_field_flag f CfOverride then display_error ctx ("Field " ^ f.cf_name ^ " is declared 'override' but doesn't override any field") f.cf_pos + if has_class_field_flag f CfOverride then display_error ctx.com ("Field " ^ f.cf_name ^ " is declared 'override' but doesn't override any field") f.cf_pos | _ when (has_class_flag c CExtern) && Meta.has Meta.CsNative c.cl_meta -> () (* -net-lib specific: do not check overrides on extern CsNative classes *) | Some (csup,params) -> let p = f.cf_name_pos in let i = f.cf_name in let check_field f get_super_field is_overload = try (if is_overload && not (has_class_field_flag f CfOverload) then - display_error ctx ("Missing overload declaration for field " ^ i) p); + display_error ctx.com ("Missing overload declaration for field " ^ i) p); let f_has_override = has_class_field_flag f CfOverride in let t, f2 = get_super_field csup i in check_native_name_override ctx f f2; @@ -183,34 +185,35 @@ let check_overriding ctx c f = | _ -> ()); if has_class_field_flag f2 CfAbstract then begin if f_has_override then - display_error ctx ("Field " ^ i ^ " is declared 'override' but parent field " ^ i ^ " is 'abstract' and does not provide any implementation to override") p + display_error ctx.com ("Field " ^ i ^ " is declared 'override' but parent field " ^ i ^ " is 'abstract' and does not provide any implementation to override") p else add_class_field_flag f CfOverride (* our spec requires users to not "override" abstract functions, but our implementation depends on implementations to be declared with "override" ¯\_(ツ)_/¯ *) end; if (has_class_field_flag f2 CfOverload && not (has_class_field_flag f CfOverload)) then - display_error ctx ("Field " ^ i ^ " should be declared with overload since it was already declared as overload in superclass") p + display_error ctx.com ("Field " ^ i ^ " should be declared with overload since it was already declared as overload in superclass") p else if not f_has_override && not (has_class_field_flag f2 CfAbstract) then begin if has_class_flag c CExtern then add_class_field_flag f CfOverride - else display_error ctx ("Field " ^ i ^ " should be declared with 'override' since it is inherited from superclass " ^ s_type_path csup.cl_path) p + else display_error ctx.com ("Field " ^ i ^ " should be declared with 'override' since it is inherited from superclass " ^ s_type_path csup.cl_path) p end else if not (has_class_field_flag f CfPublic) && (has_class_field_flag f2 CfPublic) then - display_error ctx ("Field " ^ i ^ " has less visibility (public/private) than superclass one") p + display_error ctx.com ("Field " ^ i ^ " has less visibility (public/private) than superclass one") p else (match f.cf_kind, f2.cf_kind with | _, Method MethInline -> - display_error ctx ("Field " ^ i ^ " is inlined and cannot be overridden") p + display_error ctx.com ("Field " ^ i ^ " is inlined and cannot be overridden") p | a, b when a = b -> () | Method MethInline, Method MethNormal -> () (* allow to redefine a method as inlined *) | _ -> - display_error ctx ("Field " ^ i ^ " has different property access than in superclass") p); - if (has_class_field_flag f2 CfFinal) then display_error ctx ("Cannot override final method " ^ i) p; + display_error ctx.com ("Field " ^ i ^ " has different property access than in superclass") p); + if (has_class_field_flag f2 CfFinal) then display_error ctx.com ("Cannot override final method " ^ i) p; try let t = apply_params csup.cl_params params t in - valid_redefinition ctx f f.cf_type f2 t; + let map = TClass.get_map_function csup params in + valid_redefinition ctx map map f f.cf_type f2 t; with Unify_error l -> - display_error ctx ("Field " ^ i ^ " overrides parent class with different or incomplete type") p; - display_error ctx (compl_msg "Base field is defined here") f2.cf_name_pos; - display_error ctx (compl_msg (error_msg (Unify l))) p; + display_error ctx.com ("Field " ^ i ^ " overrides parent class with different or incomplete type") p; + display_error ~depth:1 ctx.com (compl_msg "Base field is defined here") f2.cf_name_pos; + located_display_error ~depth:1 ctx.com (compl_located_msg (error_msg p (Unify l))); with Not_found -> if has_class_field_flag f CfOverride then @@ -224,7 +227,7 @@ let check_overriding ctx c f = ) fields [] in StringError.string_error i fields ("Field " ^ i ^ " is declared 'override' but doesn't override any field") end in - display_error ctx msg p + display_error ctx.com msg p in if has_class_field_flag f CfOverload then begin let overloads = Overloads.get_overloads ctx.com csup i in @@ -232,7 +235,7 @@ let check_overriding ctx c f = (* check if any super class fields are vars *) match f2.cf_kind with | Var _ -> - display_error ctx ("A variable named '" ^ f2.cf_name ^ "' was already declared in a superclass") f.cf_pos + display_error ctx.com ("A variable named '" ^ f2.cf_name ^ "' was already declared in a superclass") f.cf_pos | _ -> () ) overloads; List.iter (fun f -> @@ -251,7 +254,7 @@ let check_overriding ctx c f = let class_field_no_interf c i = try let f = PMap.find i c.cl_fields in - f.cf_type , f + (fun t -> t),f.cf_type , f with Not_found -> match c.cl_super with | None -> @@ -259,19 +262,14 @@ let class_field_no_interf c i = | Some (c,tl) -> (* rec over class_field *) let _, t , f = raw_class_field (fun f -> f.cf_type) c tl i in - apply_params c.cl_params tl t , f + let map = TClass.get_map_function c tl in + map,apply_params c.cl_params tl t , f let rec return_flow ctx e = let error() = - display_error ctx (Printf.sprintf "Missing return: %s" (s_type (print_context()) ctx.ret)) e.epos; raise Exit + display_error ctx.com (Printf.sprintf "Missing return: %s" (s_type (print_context()) ctx.ret)) e.epos; raise Exit in let return_flow = return_flow ctx in - let rec uncond e = match e.eexpr with - | TIf _ | TWhile _ | TSwitch _ | TTry _ | TFunction _ -> () - | TReturn _ | TThrow _ -> raise Exit - | _ -> Type.iter uncond e - in - let has_unconditional_flow e = try uncond e; false with Exit -> true in match e.eexpr with | TReturn _ | TThrow _ -> () | TParenthesis e | TMeta(_,e) -> @@ -280,7 +278,7 @@ let rec return_flow ctx e = let rec loop = function | [] -> error() | [e] -> return_flow e - | e :: _ when has_unconditional_flow e -> () + | e :: _ when DeadEnd.has_dead_end e -> () | _ :: l -> loop l in loop el @@ -305,7 +303,7 @@ let rec return_flow ctx e = in loop e | _ -> - error() + if not (DeadEnd.has_dead_end e) then error() let check_global_metadata ctx meta f_add mpath tpath so = let sl1 = full_dot_path2 mpath tpath in @@ -319,12 +317,16 @@ let check_global_metadata ctx meta f_add mpath tpath so = let check_module_types ctx m p t = let t = t_infos t in try - let m2 = Hashtbl.find ctx.g.types_module t.mt_path in - if m.m_path <> m2 && String.lowercase (s_type_path m2) = String.lowercase (s_type_path m.m_path) then error ("Module " ^ s_type_path m2 ^ " is loaded with a different case than " ^ s_type_path m.m_path) p; - error ("Type name " ^ s_type_path t.mt_path ^ " is redefined from module " ^ s_type_path m2) p + let path2 = ctx.com.type_to_module#find t.mt_path in + if m.m_path <> path2 && String.lowercase (s_type_path path2) = String.lowercase (s_type_path m.m_path) then typing_error ("Module " ^ s_type_path path2 ^ " is loaded with a different case than " ^ s_type_path m.m_path) p; + let m2 = ctx.com.module_lut#find path2 in + let hex1 = Digest.to_hex m.m_extra.m_sign in + let hex2 = Digest.to_hex m2.m_extra.m_sign in + let s = if hex1 = hex2 then hex1 else Printf.sprintf "was %s, is %s" hex2 hex1 in + typing_error (Printf.sprintf "Type name %s is redefined from module %s (%s)" (s_type_path t.mt_path) (s_type_path path2) s) p with Not_found -> - Hashtbl.add ctx.g.types_module t.mt_path m.m_path + ctx.com.type_to_module#add t.mt_path m.m_path module Inheritance = struct let is_basic_class_path path = match path with @@ -333,29 +335,44 @@ module Inheritance = struct let check_extends ctx c t p = match follow t with | TInst (csup,params) -> - if is_basic_class_path csup.cl_path && not ((has_class_flag c CExtern) && (has_class_flag csup CExtern)) then error "Cannot extend basic class" p; - if extends csup c then error "Recursive class" p; + if is_basic_class_path csup.cl_path && not ((has_class_flag c CExtern) && (has_class_flag csup CExtern)) then typing_error "Cannot extend basic class" p; + if extends csup c then typing_error "Recursive class" p; begin match csup.cl_kind with | KTypeParameter _ -> - if is_generic_parameter ctx csup then error "Extending generic type parameters is no longer allowed in Haxe 4" p; - error "Cannot extend type parameters" p + if is_generic_parameter ctx csup then typing_error "Extending generic type parameters is no longer allowed in Haxe 4" p; + typing_error "Cannot extend type parameters" p | _ -> csup,params end - | _ -> error "Should extend by using a class" p + | _ -> typing_error "Should extend by using a class" p let rec check_interface ctx missing c intf params = List.iter (fun (i2,p2) -> check_interface ctx missing c i2 (List.map (apply_params intf.cl_params params) p2) ) intf.cl_implements; let p = c.cl_name_pos in - let rec check_field i f = + let check_field f = let t = (apply_params intf.cl_params params f.cf_type) in let is_overload = ref false in + let make_implicit_field () = + let cf = {f with cf_overloads = []; cf_type = apply_params intf.cl_params params f.cf_type} in + begin try + let cf' = PMap.find cf.cf_name c.cl_fields in + ctx.com.overload_cache#remove (c.cl_path,f.cf_name); + cf'.cf_overloads <- cf :: cf'.cf_overloads + with Not_found -> + TClass.add_field c cf + end; + cf + in + let is_method () = match f.cf_kind with + | Method _ -> true + | Var _ -> false + in try - let t2, f2 = class_field_no_interf c i in + let map2, t2, f2 = class_field_no_interf c f.cf_name in let t2, f2 = if f2.cf_overloads <> [] || has_class_field_flag f2 CfOverload then - let overloads = Overloads.get_overloads ctx.com c i in + let overloads = Overloads.get_overloads ctx.com c f.cf_name in is_overload := true; List.find (fun (t1,f1) -> Overloads.same_overload_args t t1 f f1) overloads else @@ -370,49 +387,48 @@ module Inheritance = struct | MethMacro -> 2 in if (has_class_field_flag f CfPublic) && not (has_class_field_flag f2 CfPublic) && not (Meta.has Meta.CompilerGenerated f.cf_meta) then - display_error ctx ("Field " ^ i ^ " should be public as requested by " ^ s_type_path intf.cl_path) p + display_error ctx.com ("Field " ^ f.cf_name ^ " should be public as requested by " ^ s_type_path intf.cl_path) p else if not (unify_kind f2.cf_kind f.cf_kind) || not (match f.cf_kind, f2.cf_kind with Var _ , Var _ -> true | Method m1, Method m2 -> mkind m1 = mkind m2 | _ -> false) then - display_error ctx ("Field " ^ i ^ " has different property access than in " ^ s_type_path intf.cl_path ^ " (" ^ s_kind f2.cf_kind ^ " should be " ^ s_kind f.cf_kind ^ ")") p + display_error ctx.com ("Field " ^ f.cf_name ^ " has different property access than in " ^ s_type_path intf.cl_path ^ " (" ^ s_kind f2.cf_kind ^ " should be " ^ s_kind f.cf_kind ^ ")") p else try - valid_redefinition ctx f2 t2 f (apply_params intf.cl_params params f.cf_type) + let map1 = TClass.get_map_function intf params in + valid_redefinition ctx map1 map2 f2 t2 f (apply_params intf.cl_params params f.cf_type) with Unify_error l -> if not (Meta.has Meta.CsNative c.cl_meta && (has_class_flag c CExtern)) then begin - display_error ctx ("Field " ^ i ^ " has different type than in " ^ s_type_path intf.cl_path) p; - display_error ctx (compl_msg "Interface field is defined here") f.cf_pos; - display_error ctx (compl_msg (error_msg (Unify l))) p; + display_error ctx.com ("Field " ^ f.cf_name ^ " has different type than in " ^ s_type_path intf.cl_path) p; + display_error ~depth:1 ctx.com (compl_msg "Interface field is defined here") f.cf_pos; + located_display_error ~depth:1 ctx.com (compl_located_msg (error_msg p (Unify l))); end ) - with - | Not_found when (has_class_flag c CAbstract) -> - let cf = {f with cf_overloads = []} in + with Not_found -> + if (has_class_flag c CAbstract) && is_method() then begin + let cf = make_implicit_field () in add_class_field_flag cf CfAbstract; - begin try - let cf' = PMap.find cf.cf_name c.cl_fields in - Hashtbl.remove ctx.com.overload_cache (c.cl_path,i); - cf'.cf_overloads <- cf :: cf'.cf_overloads - with Not_found -> - TClass.add_field c cf - end - | Not_found when not (has_class_flag c CInterface) -> - if Diagnostics.is_diagnostics_run ctx.com c.cl_pos then + end else if has_class_field_flag f CfDefault then begin + let cf = make_implicit_field () in + cf.cf_expr <- None; + add_class_field_flag cf CfExtern; + add_class_field_flag cf CfOverride; + end else if not (has_class_flag c CInterface) then begin + if Diagnostics.error_in_diagnostics_run ctx.com c.cl_pos then DynArray.add missing (f,t) else begin let msg = if !is_overload then let ctx = print_context() in let args = match follow f.cf_type with | TFun(args,_) -> String.concat ", " (List.map (fun (n,o,t) -> (if o then "?" else "") ^ n ^ " : " ^ (s_type ctx t)) args) | _ -> die "" __LOC__ in - "No suitable overload for " ^ i ^ "( " ^ args ^ " ), as needed by " ^ s_type_path intf.cl_path ^ " was found" + "No suitable overload for " ^ f.cf_name ^ "( " ^ args ^ " ), as needed by " ^ s_type_path intf.cl_path ^ " was found" else - ("Field " ^ i ^ " needed by " ^ s_type_path intf.cl_path ^ " is missing") + ("Field " ^ f.cf_name ^ " needed by " ^ s_type_path intf.cl_path ^ " is missing") in - display_error ctx msg p + display_error ctx.com msg p end - | Not_found -> () + end in - let check_field i cf = - check_field i cf; + let check_field _ cf = + check_field cf; if has_class_field_flag cf CfOverload then - List.iter (check_field i) (List.rev cf.cf_overloads) + List.iter check_field (List.rev cf.cf_overloads) in PMap.iter check_field intf.cl_fields @@ -461,7 +477,7 @@ module Inheritance = struct match !missing with | [] -> () - | l when Diagnostics.is_diagnostics_run ctx.com c.cl_pos -> + | l when Diagnostics.error_in_diagnostics_run ctx.com c.cl_pos -> let diag = { mf_pos = c.cl_name_pos; mf_on = TClassDecl c; @@ -472,8 +488,8 @@ module Inheritance = struct display.module_diagnostics <- MissingFields diag :: display.module_diagnostics | l -> let singular = match l with [_] -> true | _ -> false in - display_error ctx (Printf.sprintf "This class extends abstract class %s but doesn't implement the following method%s" (s_type_path csup.cl_path) (if singular then "" else "s")) c.cl_name_pos; - display_error ctx (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path)) c.cl_name_pos; + display_error ctx.com (Printf.sprintf "This class extends abstract class %s but doesn't implement the following method%s" (s_type_path csup.cl_path) (if singular then "" else "s")) c.cl_name_pos; + display_error ctx.com (Printf.sprintf "Implement %s or make %s abstract as well" (if singular then "it" else "them") (s_type_path c.cl_path)) c.cl_name_pos; let pctx = print_context() in List.iter (fun (cf,_) -> let s = match follow cf.cf_type with @@ -482,7 +498,7 @@ module Inheritance = struct | t -> s_type pctx t in - display_error ctx (Printf.sprintf "... %s(%s)" cf.cf_name s) cf.cf_name_pos + display_error ~depth:1 ctx.com (compl_msg (Printf.sprintf "%s(%s)" cf.cf_name s)) cf.cf_name_pos ) (List.rev !missing) let set_heritance ctx c herits p = @@ -496,7 +512,7 @@ module Inheritance = struct | _ -> () ) csup.cl_meta; if has_class_flag csup CFinal && not (((has_class_flag csup CExtern) && Meta.has Meta.Hack c.cl_meta) || (match c.cl_kind with KTypeParameter _ -> true | _ -> false)) then - error ("Cannot extend a final " ^ if (has_class_flag c CInterface) then "interface" else "class") p; + typing_error ("Cannot extend a final " ^ if (has_class_flag c CInterface) then "interface" else "class") p; in let check_cancel_build csup = match csup.cl_build() with @@ -521,7 +537,7 @@ module Inheritance = struct let lt = try List.find path_matches ctx.m.curmod.m_types with Not_found -> - let t,pi = List.find (fun (lt,_) -> path_matches lt) ctx.m.module_types in + let t,pi = List.find (fun (lt,_) -> path_matches lt) ctx.m.module_imports in ImportHandling.mark_import_position ctx pi; t in @@ -537,18 +553,29 @@ module Inheritance = struct let herits = List.filter (ctx.g.do_inherit ctx c p) herits in (* Pass 1: Check and set relations *) let check_herit t is_extends p = + let rec check_interfaces_or_delay () = + match c.cl_build() with + | BuildMacro pending -> + (* Ok listen... we're still building this class, which means we can't check its interfaces yet. However, + we do want to check them at SOME point. So we use this pending list which was maybe designed for this + purpose. However, we STILL have to delay the check because at the time pending is handled, the class + is not built yet. See issue #10847. *) + pending := (fun () -> delay ctx PConnectField check_interfaces_or_delay) :: !pending + | _ -> + check_interfaces ctx c + in if is_extends then begin - if c.cl_super <> None then error "Cannot extend several classes" p; + if c.cl_super <> None then typing_error "Cannot extend several classes" p; let csup,params = check_extends ctx c t p in if (has_class_flag c CInterface) then begin - if not (has_class_flag csup CInterface) then error "Cannot extend by using a class" p; + if not (has_class_flag csup CInterface) then typing_error "Cannot extend by using a class" p; c.cl_implements <- (csup,params) :: c.cl_implements; if not !has_interf then begin - if not is_lib then delay ctx PConnectField (fun() -> check_interfaces ctx c); + if not is_lib then delay ctx PConnectField check_interfaces_or_delay; has_interf := true; end end else begin - if (has_class_flag csup CInterface) then error "Cannot extend by using an interface" p; + if (has_class_flag csup CInterface) then typing_error "Cannot extend by using an interface" p; c.cl_super <- Some (csup,params) end; (fun () -> @@ -557,16 +584,16 @@ module Inheritance = struct ) end else begin match follow t with | TInst ({ cl_path = [],"ArrayAccess" } as ca,[t]) when (has_class_flag ca CExtern) -> - if c.cl_array_access <> None then error "Duplicate array access" p; + if c.cl_array_access <> None then typing_error "Duplicate array access" p; c.cl_array_access <- Some t; (fun () -> ()) | TInst (intf,params) -> - if extends intf c then error "Recursive class" p; - if (has_class_flag c CInterface) then error "Interfaces cannot implement another interface (use extends instead)" p; - if not (has_class_flag intf CInterface) then error "You can only implement an interface" p; + if extends intf c then typing_error "Recursive class" p; + if (has_class_flag c CInterface) then typing_error "Interfaces cannot implement another interface (use extends instead)" p; + if not (has_class_flag intf CInterface) then typing_error "You can only implement an interface" p; c.cl_implements <- (intf, params) :: c.cl_implements; if not !has_interf && not is_lib && not (Meta.has (Meta.Custom "$do_not_check_interf") c.cl_meta) then begin - delay ctx PConnectField (fun() -> check_interfaces ctx c); + delay ctx PConnectField check_interfaces_or_delay; has_interf := true; end; (fun () -> @@ -574,19 +601,19 @@ module Inheritance = struct process_meta intf; ) | TDynamic t -> - if c.cl_dynamic <> None then error "Cannot have several dynamics" p; - if not (has_class_flag c CExtern) then display_error ctx "In haxe 4, implements Dynamic is only supported on externs" p; - c.cl_dynamic <- Some t; + if c.cl_dynamic <> None then typing_error "Cannot have several dynamics" p; + if not (has_class_flag c CExtern) then display_error ctx.com "In haxe 4, implements Dynamic is only supported on externs" p; + c.cl_dynamic <- Some (match t with None -> t_dynamic | Some t -> t); (fun () -> ()) | _ -> - error "Should implement by using an interface" p + typing_error "Should implement by using an interface" p end in let fl = ExtList.List.filter_map (fun (is_extends,(ct,p)) -> try let t = try Typeload.load_instance ~allow_display:true ctx (ct,p) false - with DisplayException(DisplayFields Some({fkind = CRTypeHint} as r)) -> + with DisplayException(DisplayFields ({fkind = CRTypeHint} as r)) -> (* We don't allow `implements` on interfaces. Just raise fields completion with no fields. *) if not is_extends && (has_class_flag c CInterface) then raise_fields [] CRImplements r.fsubject; let l = List.filter (fun item -> match item.ci_kind with @@ -600,8 +627,8 @@ module Inheritance = struct raise_fields l (if is_extends then CRExtends else CRImplements) r.fsubject in Some (check_herit t is_extends p) - with Error(Module_not_found(([],name)),p) when ctx.com.display.dms_kind <> DMNone -> - if Diagnostics.is_diagnostics_run ctx.com p then DisplayToplevel.handle_unresolved_identifier ctx name p true; + with Error(Module_not_found(([],name)),p,_) when ctx.com.display.dms_kind <> DMNone -> + if Diagnostics.error_in_diagnostics_run ctx.com p then DisplayToplevel.handle_unresolved_identifier ctx name p true; None ) herits in fl @@ -609,12 +636,22 @@ end let check_final_vars ctx e = let final_vars = Hashtbl.create 0 in - List.iter (fun cf -> match cf.cf_kind with - | Var _ when (has_class_field_flag cf CfFinal) && cf.cf_expr = None -> - Hashtbl.add final_vars cf.cf_name cf + let ordered_fields = DynArray.create () in + let rec loop c = + List.iter (fun cf -> match cf.cf_kind with + | Var _ when (has_class_field_flag cf CfFinal) && cf.cf_expr = None && not (Hashtbl.mem final_vars cf.cf_name) -> + Hashtbl.add final_vars cf.cf_name false; + DynArray.add ordered_fields (c,cf) + | _ -> + () + ) c.cl_ordered_fields; + match c.cl_super with + | Some(c,_) when has_class_flag c CAbstract && not (has_constructor c) -> + loop c | _ -> () - ) ctx.curclass.cl_ordered_fields; + in + loop ctx.curclass; if Hashtbl.length final_vars > 0 then begin let rec find_inits e = match e.eexpr with | TBinop(OpAssign,{eexpr = TField({eexpr = TConst TThis},fa)},e2) -> @@ -624,7 +661,10 @@ let check_final_vars ctx e = Type.iter find_inits e in find_inits e; - Hashtbl.iter (fun _ cf -> - display_error ctx ("final field " ^ cf.cf_name ^ " must be initialized immediately or in the constructor") cf.cf_pos; - ) final_vars - end \ No newline at end of file + if Hashtbl.length final_vars > 0 then + display_error ctx.com "Some final fields are uninitialized in this class" ctx.curclass.cl_name_pos; + DynArray.iter (fun (c,cf) -> + if Hashtbl.mem final_vars cf.cf_name then + display_error ~depth:1 ctx.com "Uninitialized field" cf.cf_name_pos + ) ordered_fields + end diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index 9a5d90a3..1c0323a7 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -66,6 +66,7 @@ type field_init_ctx = { is_inline : bool; is_final : bool; is_static : bool; + default : pos option; override : pos option; overload : pos option; is_extern : bool; @@ -82,8 +83,39 @@ type field_init_ctx = { (* If true, cf_expr = None makes a difference in the logic. We insert a dummy expression in display mode in order to address this. *) mutable expr_presence_matters : bool; + mutable had_error : bool; } +type method_kind = + | MKNormal + | MKGetter + | MKSetter + +module FieldError = struct + let maybe_display_error com fctx s p = + if not fctx.had_error then begin + fctx.had_error <- true; + display_error com s p + end + + let invalid_modifier_combination fctx com fctx a b p = + maybe_display_error com fctx (Printf.sprintf "Invalid modifier combination: %s + %s" a b) p + + let invalid_modifier com fctx m c p = + maybe_display_error com fctx (Printf.sprintf "Invalid modifier: %s on %s" m c) p + + let invalid_modifier_only com fctx m c p = + maybe_display_error com fctx (Printf.sprintf "Invalid modifier: %s is only supported %s" m c) p + + let missing_expression com fctx reason p = + maybe_display_error com fctx (Printf.sprintf "%s" reason) p + + let unexpected_expression com fctx reason p = + maybe_display_error com fctx (Printf.sprintf "%s" reason) p +end + +open FieldError + let dump_class_context cctx = Printer.s_record_fields "" [ "tclass",Printer.s_tclass "\t" cctx.tclass; @@ -116,11 +148,10 @@ let dump_field_context fctx = "expr_presence_matters",string_of_bool fctx.expr_presence_matters; ] - let is_java_native_function ctx meta pos = try match Meta.get Meta.Native meta with | (Meta.Native,[],_) -> - ctx.com.warning "@:native metadata for jni functions is deprecated. Use @:java.native instead." pos; + warning ctx WDeprecated "@:native metadata for jni functions is deprecated. Use @:java.native instead." pos; true | _ -> false with | Not_found -> Meta.has Meta.NativeJni meta @@ -175,7 +206,7 @@ let ensure_struct_init_constructor ctx c ast_fields p = ast_fields in let super_args,super_expr,super_tl = get_struct_init_super_info ctx c p in - let params = List.map snd c.cl_params in + let params = extract_param_types c.cl_params in let ethis = mk (TConst TThis) (TInst(c,params)) p in let doc_buf = Buffer.create 0 in let args,el,tl = List.fold_left (fun (args,el,tl) cf -> match cf.cf_kind with @@ -184,7 +215,7 @@ let ensure_struct_init_constructor ctx c ast_fields p = let has_default_expr = field_has_default_expr cf.cf_name in let opt = has_default_expr || (Meta.has Meta.Optional cf.cf_meta) in let t = if opt then ctx.t.tnull cf.cf_type else cf.cf_type in - let v = alloc_var VGenerated cf.cf_name t p in + let v = alloc_var VGenerated cf.cf_name t cf.cf_name_pos in let ef = mk (TField(ethis,FInstance(c,params,cf))) cf.cf_type p in let ev = mk (TLocal v) v.v_type p in if opt && not (Meta.has Meta.Optional v.v_meta) then @@ -240,20 +271,20 @@ let transform_abstract_field com this_t a_t a f = | FProp ((("get" | "never"),_),(("set" | "never"),_),_,_) when not stat -> f | FProp _ when not stat && not (Meta.has Meta.Enum f.cff_meta) -> - error "Member property accessors must be get/set or never" p; + typing_error "Member property accessors must be get/set or never" p; | FFun fu when fst f.cff_name = "new" && not stat -> - let init p = (EVars [mk_evar ~t:this_t ("this",null_pos)],p) in + let init p = (EVars [mk_evar ~t:this_t ~meta:([Meta.This,[],null_pos]) ("this",null_pos)],p) in let cast e = (ECast(e,None)),pos e in let ret p = (EReturn (Some (cast (EConst (Ident "this"),p))),p) in let meta = (Meta.NoCompletion,[],null_pos) :: f.cff_meta in if Meta.has Meta.MultiType a.a_meta then begin - if List.mem_assoc AInline f.cff_access then error "MultiType constructors cannot be inline" f.cff_pos; - if fu.f_expr <> None then error "MultiType constructors cannot have a body" f.cff_pos; + if List.mem_assoc AInline f.cff_access then typing_error "inline on MultiType constructor" f.cff_pos; + if fu.f_expr <> None then display_error com "MultiType constructors cannot have a body" f.cff_pos; f.cff_access <- (AExtern,null_pos) :: f.cff_access; end; (try let _, p = List.find (fun (acc, _) -> acc = AMacro) f.cff_access in - error "Macro abstract constructors are not supported" p + typing_error "Invalid modifier: macro on abstract constructor" p with Not_found -> ()); (* We don't want the generated expression positions to shadow the real code. *) let p = { p with pmax = p.pmin } in @@ -268,7 +299,7 @@ let transform_abstract_field com this_t a_t a f = } in { f with cff_name = "_new",pos f.cff_name; cff_kind = FFun fu; cff_meta = meta } | FFun fu when not stat -> - if Meta.has Meta.From f.cff_meta then error "@:from cast functions must be static" f.cff_pos; + if Meta.has Meta.From f.cff_meta then typing_error "@:from cast functions must be static" f.cff_pos; { f with cff_kind = FFun fu } | _ -> f @@ -386,12 +417,12 @@ let build_enum_abstract ctx c a fields p = | VUnknown -> () | VPublic(access,p2) | VPrivate(access,p2) -> - display_error ctx (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1; - display_error ctx "Conflicts with this" p2; + display_error ctx.com (Printf.sprintf "Conflicting access modifier %s" (Ast.s_access access)) p1; + display_error ~depth:1 ctx.com "Conflicts with this" p2; in let rec loop visibility acc = match acc with | (AExtern,p) :: acc -> - display_error ctx "extern modifier is not allowed on enum abstract fields" p; + display_error ctx.com "Invalid modifier: extern on field of enum abstract" p; loop visibility acc | (APrivate,p) as access :: acc -> check_visibility_conflict visibility p; @@ -409,7 +440,7 @@ let build_enum_abstract ctx c a fields p = field.cff_meta <- (Meta.Enum,[],null_pos) :: field.cff_meta; let ct = match ct with | Some _ -> ct - | None -> Some (TExprToExpr.convert_type (TAbstract(a,List.map snd a.a_params)),null_pos) + | None -> Some (TExprToExpr.convert_type (TAbstract(a,extract_param_types a.a_params)),null_pos) in begin match eo with | None -> @@ -417,14 +448,14 @@ let build_enum_abstract ctx c a fields p = | EAString -> set_field field ct (EConst (String (fst field.cff_name,SDoubleQuotes)),null_pos) | EAInt i -> - set_field field ct (EConst (Int (string_of_int !i)),null_pos); + set_field field ct (EConst (Int (string_of_int !i, None)),null_pos); incr i; | EAOther -> - error "Value required" field.cff_pos + display_error ctx.com "Value required" field.cff_pos end else field.cff_kind <- FProp(("default",null_pos),("never",null_pos),ct,None) | Some e -> begin match mode,e with - | EAInt i,(EConst(Int s),_) -> + | EAInt i,(EConst(Int (s, None)),_) -> begin try let i' = int_of_string s in i := (i' + 1) @@ -443,7 +474,7 @@ let build_enum_abstract ctx c a fields p = let apply_macro ctx mode path el p = let cpath, meth = (match List.rev (ExtString.String.nsplit path ".") with | meth :: name :: pack -> (List.rev pack,name), meth - | _ -> error "Invalid macro path" p + | _ -> typing_error "Invalid macro path" p ) in ctx.g.do_macro ctx mode cpath meth el p @@ -453,32 +484,35 @@ let build_module_def ctx mt meta fvars context_init fbuild = | Meta.Build,args,p when not is_typedef -> (fun () -> let epath, el = (match args with | [ECall (epath,el),p] -> epath, el - | _ -> error "Invalid build parameters" p + | _ -> typing_error "Invalid build parameters" p ) in - let s = try String.concat "." (List.rev (string_list_of_expr_path epath)) with Error (_,p) -> error "Build call parameter must be a class path" p in - if ctx.in_macro then error "You cannot use @:build inside a macro : make sure that your type is not used in macro" p; + let s = try String.concat "." (List.rev (string_list_of_expr_path epath)) with Error (_,p,depth) -> typing_error ~depth "Build call parameter must be a class path" p in + if ctx.com.is_macro_context then typing_error "You cannot use @:build inside a macro : make sure that your type is not used in macro" p; let old = ctx.get_build_infos in - ctx.get_build_infos <- (fun() -> Some (mt, List.map snd (t_infos mt).mt_params, fvars())); + ctx.get_build_infos <- (fun() -> Some (mt, extract_param_types (t_infos mt).mt_params, fvars())); context_init#run; let r = try apply_macro ctx MBuild s el p with e -> ctx.get_build_infos <- old; raise e in ctx.get_build_infos <- old; (match r with - | None -> error "Build failure" p + | None -> typing_error "Build failure" p | Some e -> fbuild e) ) :: f_build | Meta.Using,el,p -> (fun () -> List.iter (fun e -> try let path = List.rev (string_pos_list_of_expr_path_raise e) in - let types,filter_classes = handle_using ctx path (pos e) in + let types,filter_classes = ImportHandling.handle_using ctx path (pos e) in let ti = match mt with | TClassDecl { cl_kind = KAbstractImpl a } -> t_infos (TAbstractDecl a) | _ -> t_infos mt in - ti.mt_using <- (filter_classes types) @ ti.mt_using; + (* Delay for #10107, but use delay_late to make sure base classes run before their children do. *) + delay_late ctx PConnectField (fun () -> + ti.mt_using <- (filter_classes types) @ ti.mt_using + ) with Exit -> - error "dot path expected" (pos e) + typing_error "dot path expected" (pos e) ) el; ) :: f_build | _ -> @@ -486,23 +520,25 @@ let build_module_def ctx mt meta fvars context_init fbuild = in (* let errors go through to prevent resume if build fails *) let f_build = List.fold_left loop [] meta in - (* Go for @:using in parents and interfaces *) let f_enum = match mt with | TClassDecl ({cl_kind = KAbstractImpl a} as c) when a.a_enum -> Some (fun () -> (* if p <> null_pos && not (Define.is_haxe3_compat ctx.com.defines) then - ctx.com.warning "`@:enum abstract` is deprecated in favor of `enum abstract`" p; *) + warning ctx WDeprecated "`@:enum abstract` is deprecated in favor of `enum abstract`" p; *) context_init#run; let e = build_enum_abstract ctx c a (fvars()) a.a_name_pos in fbuild e; ) | TClassDecl { cl_super = csup; cl_implements = interfaces; cl_kind = kind } -> + (* Go for @:using in parents and interfaces *) let ti = t_infos mt in let inherit_using (c,_) = ti.mt_using <- ti.mt_using @ (t_infos (TClassDecl c)).mt_using in - Option.may inherit_using csup; - List.iter inherit_using interfaces; + delay_late ctx PConnectField (fun () -> + Option.may inherit_using csup; + List.iter inherit_using interfaces; + ); None | _ -> None @@ -510,37 +546,14 @@ let build_module_def ctx mt meta fvars context_init fbuild = List.iter (fun f -> f()) (List.rev f_build); (match f_enum with None -> () | Some f -> f()) -let create_class_context ctx c context_init p = - locate_macro_error := true; - incr stats.s_classes_built; +let create_class_context c context_init p = let abstract = match c.cl_kind with | KAbstractImpl a -> Some a | _ -> None in - let ctx = { - ctx with - curclass = c; - type_params = c.cl_params; - pass = PBuildClass; - tthis = (match abstract with - | Some a -> - (match a.a_this with - | TMono r when r.tm_type = None -> TAbstract (a,List.map snd c.cl_params) - | t -> t) - | None -> TInst (c,List.map snd c.cl_params)); - on_error = (fun ctx msg ep -> - ctx.com.error msg ep; - (* macros expressions might reference other code, let's recall which class we are actually compiling *) - let open TFunctions in - if not (ExtString.String.starts_with msg "...") && !locate_macro_error && (is_pos_outside_class c ep) && not (is_module_fields_class c) then ctx.com.error (compl_msg "Defined in this class") c.cl_pos - ); - } in - (* a lib type will skip most checks *) let is_lib = Meta.has Meta.LibType c.cl_meta in - if is_lib && not (has_class_flag c CExtern) then ctx.com.error "@:libType can only be used in extern classes" c.cl_pos; (* a native type will skip one check: the static vs non-static field *) let is_native = Meta.has Meta.JavaNative c.cl_meta || Meta.has Meta.CsNative c.cl_meta in - if Meta.has Meta.Macro c.cl_meta then display_error ctx "Macro classes are no longer allowed in haxe 3" c.cl_pos; let rec extends_public c = Meta.has Meta.PublicFields c.cl_meta || match c.cl_super with @@ -561,20 +574,29 @@ let create_class_context ctx c context_init p = delayed_expr = []; has_display_field = false; } in - ctx,cctx + cctx -let create_field_context (ctx,cctx) c cff = - DeprecationCheck.check_is ctx.com (fst cff.cff_name) cff.cff_meta (snd cff.cff_name); +let create_typer_context_for_class ctx cctx p = + locate_macro_error := true; + incr stats.s_classes_built; + let c = cctx.tclass in + if cctx.is_lib && not (has_class_flag c CExtern) then ctx.com.error "@:libType can only be used in extern classes" c.cl_pos; + if Meta.has Meta.Macro c.cl_meta then display_error ctx.com "Macro classes are no longer allowed in haxe 3" c.cl_pos; let ctx = { ctx with - pass = PBuildClass; (* will be set later to PTypeExpr *) - locals = PMap.empty; - opened = []; - monomorphs = { - perfunction = []; - }; + curclass = c; + type_params = c.cl_params; + pass = PBuildClass; + tthis = (match cctx.abstract with + | Some a -> + (match a.a_this with + | TMono r when r.tm_type = None -> TAbstract (a,extract_param_types c.cl_params) + | t -> t) + | None -> TInst (c,extract_param_types c.cl_params)); } in - let display_modifier = Typeload.check_field_access ctx cff in + ctx + +let create_field_context cctx cff is_display_file display_modifier = let is_static = List.mem_assoc AStatic cff.cff_access in let is_static,is_abstract_member = if cctx.abstract <> None && not is_static then true,true else is_static,false in let is_extern = ref (List.mem_assoc AExtern cff.cff_access) in @@ -585,28 +607,15 @@ let create_field_context (ctx,cctx) c cff = | Meta.Final -> is_final := true; (* if p <> null_pos && not (Define.is_haxe3_compat ctx.com.defines) then - ctx.com.warning "`@:final` is deprecated in favor of `final`" p; *) + warning ctx WDeprecated "`@:final` is deprecated in favor of `final`" p; *) | Meta.Extern -> (* if not (Define.is_haxe3_compat ctx.com.defines) then - ctx.com.warning "`@:extern` on fields is deprecated in favor of `extern`" (pos cff.cff_name); *) + warning ctx WDeprecated "`@:extern` on fields is deprecated in favor of `extern`" (pos cff.cff_name); *) is_extern := true; | _ -> () ) cff.cff_meta; let is_inline = List.mem_assoc AInline cff.cff_access in - if (is_abstract && not (has_meta Meta.LibType c.cl_meta)) then begin - if is_static then - display_error ctx "Static methods may not be abstract" (pos cff.cff_name) - else if !is_final then - display_error ctx "Abstract methods may not be final" (pos cff.cff_name) - else if is_inline then - display_error ctx "Abstract methods may not be inline" (pos cff.cff_name) - else if not (has_class_flag c CAbstract) then begin - display_error ctx "This class should be declared abstract because it has at least one abstract field" c.cl_name_pos; - display_error ctx "First abstract field was here" (pos cff.cff_name); - add_class_flag c CAbstract; - end; - end; let override = try Some (List.assoc AOverride cff.cff_access) with Not_found -> None in let overload = try Some (List.assoc AOverload cff.cff_access) with Not_found -> None in let is_macro = List.mem_assoc AMacro cff.cff_access in @@ -615,16 +624,24 @@ let create_field_context (ctx,cctx) c cff = | "__init__" when is_static -> FKInit | _ -> FKNormal in + let default = try + let (_,_,p) = Meta.get Meta.JavaDefault cff.cff_meta in + Some p + with Not_found -> + None + in + let c = cctx.tclass in let fctx = { is_inline = is_inline; is_static = is_static; + default = default; override = override; overload = overload; is_macro = is_macro; is_extern = !is_extern; is_abstract = is_abstract; is_final = !is_final; - is_display_field = ctx.is_display_file && DisplayPosition.display_position#enclosed_in cff.cff_pos; + is_display_field = is_display_file && DisplayPosition.display_position#enclosed_in cff.cff_pos; is_field_debug = cctx.is_class_debug || Meta.has (Meta.Custom ":debug.typeload") cff.cff_meta; display_modifier = display_modifier; is_abstract_member = is_abstract_member; @@ -633,9 +650,37 @@ let create_field_context (ctx,cctx) c cff = do_bind = (((not ((has_class_flag c CExtern) || !is_extern) || is_inline) && not is_abstract && not (has_class_flag c CInterface)) || field_kind = FKInit); do_add = true; expr_presence_matters = false; + had_error = false; } in if fctx.is_display_field then cctx.has_display_field <- true; - ctx,fctx + fctx + +let create_typer_context_for_field ctx cctx fctx cff = + DeprecationCheck.check_is ctx.com (fst cff.cff_name) cff.cff_meta (snd cff.cff_name); + let ctx = { + ctx with + pass = PBuildClass; (* will be set later to PTypeExpr *) + locals = PMap.empty; + opened = []; + monomorphs = { + perfunction = []; + }; + } in + let c = cctx.tclass in + if (fctx.is_abstract && not (has_meta Meta.LibType c.cl_meta)) then begin + if fctx.is_static then + invalid_modifier_combination fctx ctx.com fctx "abstract" "static" (pos cff.cff_name) + else if fctx.is_final then + invalid_modifier_combination fctx ctx.com fctx "abstract" "final" (pos cff.cff_name) + else if fctx.is_inline then + invalid_modifier_combination fctx ctx.com fctx "abstract" "inline" (pos cff.cff_name) + else if not (has_class_flag c CAbstract) then begin + display_error ctx.com "This class should be declared abstract because it has at least one abstract field" c.cl_name_pos; + display_error ctx.com "First abstract field was here" (pos cff.cff_name); + add_class_flag c CAbstract; + end; + end; + ctx let is_public (ctx,cctx) access parent = let c = cctx.tclass in @@ -656,21 +701,24 @@ let rec get_parent c name = with Not_found -> get_parent csup name +let transform_abstract_field2 ctx a cff = + let a_t = TExprToExpr.convert_type' (TAbstract(a,extract_param_types a.a_params)) in + let this_t = TExprToExpr.convert_type' a.a_this in (* TODO: better pos? *) + transform_abstract_field ctx.com this_t a_t a cff + let transform_field (ctx,cctx) c f fields p = let f = match cctx.abstract with | Some a -> - let a_t = TExprToExpr.convert_type' (TAbstract(a,List.map snd a.a_params)) in - let this_t = TExprToExpr.convert_type' a.a_this in (* TODO: better pos? *) - transform_abstract_field ctx.com this_t a_t a f + transform_abstract_field2 ctx a f | None -> f in if List.mem_assoc AMacro f.cff_access then (match ctx.g.macros with - | Some (_,mctx) when Hashtbl.mem mctx.g.types_module c.cl_path -> + | Some (_,mctx) when mctx.com.type_to_module#mem c.cl_path -> (* assume that if we had already a macro with the same name, it has not been changed during the @:build operation *) if not (List.exists (fun f2 -> f2.cff_name = f.cff_name && List.mem_assoc AMacro f2.cff_access) (!fields)) then - error "Class build macro cannot return a macro function when the class has already been compiled into the macro context" p + typing_error "Class build macro cannot return a macro function when the class has already been compiled into the macro context" p | _ -> ()); f @@ -697,7 +745,7 @@ let build_fields (ctx,cctx) c fields = | EVars [{ ev_type = Some (CTAnonymous f,p); ev_expr = None }] -> let f = List.map (fun f -> transform_field (ctx,cctx) c f fields p) f in fields := f - | _ -> error "Class build macro must return a single variable with anonymous fields" p + | _ -> typing_error "Class build macro must return a single variable with anonymous fields" p ); c.cl_build <- (fun() -> Building [c]); List.iter (fun f -> f()) !pending; @@ -740,28 +788,36 @@ module TypeBinding = struct | TMono r -> (match r.tm_type with None -> false | Some t -> is_full_type t) | TAbstract _ | TInst _ | TEnum _ | TLazy _ | TDynamic _ | TAnon _ | TType _ -> true in - let force_macro () = + let force_macro display = (* force macro system loading of this class in order to get completion *) - delay ctx PTypeField (fun() -> try ignore(ctx.g.do_macro ctx MDisplay c.cl_path cf.cf_name [] p) with Exit | Error _ -> ()) + delay ctx PTypeField (fun() -> + try + ignore(ctx.g.do_macro ctx MDisplay c.cl_path cf.cf_name [] p) + with + | Exit -> + () + | Error _ when display -> + () + ) in let handle_display_field () = - if fctx.is_macro && not ctx.in_macro then - force_macro() + if fctx.is_macro && not ctx.com.is_macro_context then + force_macro true else begin cf.cf_type <- TLazy r; cctx.delayed_expr <- (ctx,Some r) :: cctx.delayed_expr; end in if ctx.com.display.dms_full_typing then begin - if fctx.is_macro && not ctx.in_macro then - force_macro () + if fctx.is_macro && not ctx.com.is_macro_context then + force_macro false else begin cf.cf_type <- TLazy r; (* is_lib ? *) cctx.delayed_expr <- (ctx,Some r) :: cctx.delayed_expr; end - end else if ctx.com.display.dms_force_macro_typing && fctx.is_macro && not ctx.in_macro then - force_macro() + end else if ctx.com.display.dms_force_macro_typing && fctx.is_macro && not ctx.com.is_macro_context then + force_macro true else begin if fctx.is_display_field then begin handle_display_field() @@ -798,14 +854,14 @@ module TypeBinding = struct | Some (csup,_) -> (* this can happen on -net-lib generated classes if a combination of explicit interfaces and variables with the same name happens *) if not ((has_class_flag csup CInterface) && Meta.has Meta.CsNative c.cl_meta) then - error ("Redefinition of variable " ^ cf.cf_name ^ " in subclass is not allowed. Previously declared at " ^ (s_type_path csup.cl_path) ) cf.cf_name_pos + display_error ctx.com ("Redefinition of variable " ^ cf.cf_name ^ " in subclass is not allowed. Previously declared at " ^ (s_type_path csup.cl_path) ) cf.cf_name_pos end let bind_var_expression ctx cctx fctx cf e = let c = cctx.tclass in let t = cf.cf_type in let p = cf.cf_pos in - if (has_class_flag c CInterface) then display_error ctx "Default values on interfaces are not allowed" (pos e); + if (has_class_flag c CInterface) then unexpected_expression ctx.com fctx "Initialization on field of interface" (pos e); cf.cf_meta <- ((Meta.Value,[e],null_pos) :: cf.cf_meta); let check_cast e = (* insert cast to keep explicit field type (issue #1901) *) @@ -824,41 +880,37 @@ module TypeBinding = struct if not !return_partial_type || (match fst e with EConst _ -> true | _ -> false) then begin r := lazy_processing (fun() -> t); cctx.context_init#run; - if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.in_macro then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ cf.cf_name); + if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.com.is_macro_context then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ cf.cf_name); let e = type_var_field ctx t e fctx.is_static fctx.is_display_field p in let maybe_run_analyzer e = match e.eexpr with | TConst _ | TLocal _ | TFunction _ -> e - | _ -> !analyzer_run_on_expr_ref ctx.com e + | _ -> !analyzer_run_on_expr_ref ctx.com (Printf.sprintf "%s.%s" (s_type_path cctx.tclass.cl_path) cf.cf_name) e in let require_constant_expression e msg = - if ctx.com.display.dms_kind <> DMNone && ctx.com.display.dms_error_policy <> EPCollect then - e - else match Optimizer.make_constant_expression ctx (maybe_run_analyzer e) with + match Optimizer.make_constant_expression ctx (maybe_run_analyzer e) with | Some e -> e - | None -> display_error ctx msg p; e + | None -> display_error ctx.com msg p; e in let e = (match cf.cf_kind with | Var v when (has_class_flag c CExtern) || fctx.is_extern -> if not fctx.is_static then begin - display_error ctx "Extern non-static variables may not be initialized" p; + unexpected_expression ctx.com fctx "on extern non-static variable" p; e end else if not fctx.is_inline then begin - display_error ctx "Extern non-inline variables may not be initialized" p; + unexpected_expression ctx.com fctx "on extern non-inline variable" p; e end else require_constant_expression e "Extern variable initialization must be a constant value" | Var v when not (is_physical_field cf) -> (* disallow initialization of non-physical fields (issue #1958) *) - display_error ctx "This field cannot be initialized because it is not a real variable" p; e + unexpected_expression ctx.com fctx "on field that is not a real variable" p; e | Var v when not fctx.is_static -> - let e = if ctx.com.display.dms_display && ctx.com.display.dms_error_policy <> EPCollect then - e - else begin + let e = begin let rec check_this e = match e.eexpr with | TConst TThis -> - display_error ctx "Cannot access this or other member field in variable initialization" e.epos; + display_error ctx.com "Cannot access this or other member field in variable initialization" e.epos; raise Exit | TLocal v when (match ctx.vthis with Some v2 -> v == v2 | None -> false) -> - display_error ctx "Cannot access this or other member field in variable initialization" e.epos; + display_error ctx.com "Cannot access this or other member field in variable initialization" e.epos; raise Exit | _ -> Type.iter check_this e @@ -906,7 +958,7 @@ module TypeBinding = struct r := lazy_processing (fun() -> t); cctx.context_init#run; incr stats.s_methods_typed; - if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.in_macro then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ cf.cf_name); + if ctx.com.verbose then Common.log ctx.com ("Typing " ^ (if ctx.com.is_macro_context then "macro " else "") ^ s_type_path c.cl_path ^ "." ^ cf.cf_name); let fmode = (match cctx.abstract with | Some _ -> if fctx.is_abstract_member then FunMemberAbstract else FunStatic @@ -916,7 +968,7 @@ module TypeBinding = struct begin match ctx.com.platform with | Java when is_java_native_function ctx cf.cf_meta cf.cf_pos -> if e <> None then - ctx.com.warning "@:java.native function definitions shouldn't include an expression. This behaviour is deprecated." cf.cf_pos; + warning ctx WDeprecated "@:java.native function definitions shouldn't include an expression. This behaviour is deprecated." cf.cf_pos; cf.cf_expr <- None; cf.cf_type <- t | _ -> @@ -928,7 +980,7 @@ module TypeBinding = struct end; (* Disabled for now, see https://github.com/HaxeFoundation/haxe/issues/3033 *) (* List.iter (fun (v,_) -> - if v.v_name <> "_" && has_mono v.v_type then ctx.com.warning "Uninferred function argument, please add a type-hint" v.v_pos; + if v.v_name <> "_" && has_mono v.v_type then warning ctx WTemp "Uninferred function argument, please add a type-hint" v.v_pos; ) fargs; *) let tf = { tf_args = args#for_expr; @@ -952,26 +1004,28 @@ module TypeBinding = struct bind_type ctx cctx fctx cf r p end +let load_variable_type_hint ctx fctx eo p = function + | None when eo = None -> + missing_expression ctx.com fctx "Variable requires type-hint or initialization" p; + t_dynamic + | None -> + mk_mono() + | Some t -> + lazy_display_type ctx (fun () -> load_type_hint ctx p (Some t)) + let create_variable (ctx,cctx,fctx) c f t eo p = let is_abstract_enum_field = Meta.has Meta.Enum f.cff_meta in - if fctx.is_abstract_member && not is_abstract_enum_field then error (fst f.cff_name ^ ": Cannot declare member variable in abstract") p; - if fctx.is_inline && not fctx.is_static then error (fst f.cff_name ^ ": Inline variable must be static") p; - if fctx.is_inline && eo = None then error (fst f.cff_name ^ ": Inline variable must be initialized") p; + if fctx.is_abstract_member && not is_abstract_enum_field then typing_error "Cannot declare member variable in abstract" p; + if fctx.is_inline && not fctx.is_static then invalid_modifier ctx.com fctx "inline" "non-static variable" p; + if fctx.is_inline && eo = None then missing_expression ctx.com fctx "Inline variable must be initialized" p; let missing_initialization = fctx.is_final && not (fctx.is_extern || (has_class_flag c CExtern) || (has_class_flag c CInterface)) && eo = None in if missing_initialization && fctx.is_static && fctx.is_final then - error (fst f.cff_name ^ ": Static final variable must be initialized") p; - let t = (match t with - | None when eo = None -> - error ("Variable requires type-hint or initialization") (pos f.cff_name); - | None -> - mk_mono() - | Some t -> - lazy_display_type ctx (fun () -> load_type_hint ctx p (Some t)) - ) in + missing_expression ctx.com fctx "Static final variable must be initialized" p; + let t = load_variable_type_hint ctx fctx eo (pos f.cff_name) t in let kind = if fctx.is_inline then { v_read = AccInline ; v_write = AccNever } else if fctx.is_final then @@ -1017,29 +1071,36 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p = let r = exc_protect ctx (fun r -> r := lazy_processing (fun () -> t); (* the return type of a from-function must be the abstract, not the underlying type *) - if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> error (error_msg (Unify l)) p); + if not fctx.is_macro then (try type_eq EqStrict ret ta with Unify_error l -> located_typing_error (error_msg p (Unify l))); match t with | TFun([_,_,t],_) -> t - | _ -> error (cf.cf_name ^ ": @:from cast functions must accept exactly one argument") p + | TFun([(_,_,t1);(_,true,t2)],_) when is_pos_infos t2 -> t1 + | _ -> typing_error ("@:from cast functions must accept exactly one argument") p ) "@:from" in a.a_from_field <- (TLazy r,cf) :: a.a_from_field; | (Meta.To,_,_) :: _ -> - if fctx.is_macro then error (cf.cf_name ^ ": Macro cast functions are not supported") p; + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "cast function" p; + let are_valid_args args = + match args with + | [_] -> true + | [_; (_,true,t)] when is_pos_infos t -> true + | _ -> false + in (match cf.cf_kind, cf.cf_type with | Var _, _ -> - error "@:to meta should be used on methods" p - | Method _, TFun(args, _) when not fctx.is_abstract_member && List.length args <> 1 -> + typing_error "Invalid metadata: @:to must be used on method of abstract" p + | Method _, TFun(args, _) when not fctx.is_abstract_member && not (are_valid_args args) -> if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) - error ("static @:to method should have one argument") p - | Method _, TFun(args, _) when fctx.is_abstract_member && List.length args <> 1 -> + typing_error "static @:to method should have one argument" p + | Method _, TFun(args, _) when fctx.is_abstract_member && not (are_valid_args args) -> if not (Meta.has Meta.MultiType a.a_meta) then (* TODO: get rid of this check once multitype is removed *) - error "@:to method should have no arguments" p + typing_error "@:to method should have no arguments" p | _ -> () ); (* TODO: this doesn't seem quite right... *) if not (has_class_field_flag cf CfImpl) then add_class_field_flag cf CfImpl; let resolve_m args = - (try unify_raise ctx t (tfun (tthis :: args) m) cf.cf_pos with Error (Unify l,p) -> error (error_msg (Unify l)) p); + (try unify_raise t (tfun (tthis :: args) m) cf.cf_pos with Error (Unify l,p,depth) -> located_typing_error ~depth (error_msg p (Unify l))); match follow m with | TMono _ when (match t with TFun(_,r) -> r == t_dynamic | _ -> false) -> t_dynamic | m -> m @@ -1053,7 +1114,7 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p = let ctor = try PMap.find "_new" c.cl_statics with Not_found -> - error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos + typing_error "Constructor of multi-type abstract must be defined before the individual @:to-functions are" cf.cf_pos in (* delay ctx PFinal (fun () -> unify ctx m tthis f.cff_pos); *) let args = match follow (monomorphs a.a_params ctor.cf_type) with @@ -1062,61 +1123,77 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p = in args end else - [] + match cf.cf_type with + | TFun([_;(_,true,t)],_) when is_pos_infos t -> [t] + | _ -> [] in let t = resolve_m args in t ) "@:to" in a.a_to_field <- (TLazy r, cf) :: a.a_to_field | ((Meta.ArrayAccess,_,_) | (Meta.Op,[(EArrayDecl _),_],_)) :: _ -> - if fctx.is_macro then error (cf.cf_name ^ ": Macro array-access functions are not supported") p; + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "array-access function" p; a.a_array <- cf :: a.a_array; allow_no_expr(); | (Meta.Op,[EBinop(OpAssign,_,_),_],_) :: _ -> - error (cf.cf_name ^ ": Assignment overloading is not supported") p; + typing_error "Assignment overloading is not supported" p; + | (Meta.Op,[EBinop(OpAssignOp OpNullCoal,_,_),_],_) :: _ + | (Meta.Op,[EBinop(OpNullCoal,_,_),_],_) :: _ -> + typing_error "Null coalescing overloading is not supported" p; | (Meta.Op,[ETernary(_,_,_),_],_) :: _ -> - error (cf.cf_name ^ ": Ternary overloading is not supported") p; + typing_error "Ternary overloading is not supported" p; | (Meta.Op,[EBinop(op,_,_),_],_) :: _ -> - if fctx.is_macro then error (cf.cf_name ^ ": Macro operator functions are not supported") p; + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; let targ = if fctx.is_abstract_member then tthis else ta in - let left_eq,right_eq = match follow t with + let left_eq,right_eq = + match follow t with | TFun([(_,_,t1);(_,_,t2)],_) -> type_iseq targ t1,type_iseq targ t2 + | TFun([(_,_,t1);(_,_,t2);(_,true,t3)],_) when is_pos_infos t3 -> + type_iseq targ t1,type_iseq targ t2 | _ -> if fctx.is_abstract_member then - error (cf.cf_name ^ ": Member @:op functions must accept exactly one argument") cf.cf_pos + typing_error ("Member @:op functions must accept exactly one argument") cf.cf_pos else - error (cf.cf_name ^ ": Static @:op functions must accept exactly two arguments") cf.cf_pos + typing_error ("Static @:op functions must accept exactly two arguments") cf.cf_pos in - if not (left_eq || right_eq) then error (cf.cf_name ^ ": The left or right argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; - if right_eq && Meta.has Meta.Commutative cf.cf_meta then error (cf.cf_name ^ ": @:commutative is only allowed if the right argument is not " ^ (s_type (print_context()) targ)) cf.cf_pos; + if not (left_eq || right_eq) then typing_error ("The left or right argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; + if right_eq && Meta.has Meta.Commutative cf.cf_meta then typing_error ("Invalid metadata: @:commutative is only allowed if the right argument is not " ^ (s_type (print_context()) targ)) cf.cf_pos; a.a_ops <- (op,cf) :: a.a_ops; allow_no_expr(); | (Meta.Op,[EUnop(op,flag,_),_],_) :: _ -> - if fctx.is_macro then error (cf.cf_name ^ ": Macro operator functions are not supported") p; + if fctx.is_macro then invalid_modifier ctx.com fctx "macro" "operator function" p; let targ = if fctx.is_abstract_member then tthis else ta in - (try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise (Error ((Unify l),cf.cf_pos))); + (try type_eq EqStrict t (tfun [targ] (mk_mono())) with Unify_error l -> raise (Error ((Unify l),cf.cf_pos,0))); a.a_unops <- (op,flag,cf) :: a.a_unops; allow_no_expr(); + | (Meta.Op,[ECall _,_],_) :: _ -> + begin match a.a_call with + | None -> + a.a_call <- Some cf + | Some cf' -> + cf'.cf_overloads <- cf :: cf'.cf_overloads + end; + allow_no_expr(); | ((Meta.Resolve,_,_) | (Meta.Op,[EField _,_],_)) :: _ -> let targ = if fctx.is_abstract_member then tthis else ta in let check_fun t1 t2 = if not fctx.is_macro then begin - if not (type_iseq targ t1) then error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; - if not (type_iseq ctx.t.tstring t2) then error ("Second argument type must be String") cf.cf_pos + if not (type_iseq targ t1) then typing_error ("First argument type must be " ^ (s_type (print_context()) targ)) cf.cf_pos; + if not (type_iseq ctx.t.tstring t2) then typing_error ("Second argument type must be String") cf.cf_pos end in begin match follow t with - | TFun([(_,_,t1);(_,_,t2)],_) -> - if a.a_read <> None then error "Multiple resolve-read methods are not supported" cf.cf_pos; + | TFun((_,_,t1) :: (_,_,t2) :: args,_) when is_empty_or_pos_infos args -> + if a.a_read <> None then typing_error "Multiple resolve-read methods are not supported" cf.cf_pos; check_fun t1 t2; a.a_read <- Some cf; - | TFun([(_,_,t1);(_,_,t2);(_,_,t3)],_) -> - if a.a_write <> None then error "Multiple resolve-write methods are not supported" cf.cf_pos; + | TFun((_,_,t1) :: (_,_,t2) :: (_,_,t3) :: args,_) when is_empty_or_pos_infos args -> + if a.a_write <> None then typing_error "Multiple resolve-write methods are not supported" cf.cf_pos; check_fun t1 t2; a.a_write <- Some cf; | _ -> - error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos + typing_error ("Field type of resolve must be " ^ (s_type (print_context()) targ) ^ " -> String -> T") cf.cf_pos end; | _ -> ()); match ml with @@ -1126,8 +1203,8 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p = loop cf.cf_meta; if cf.cf_name = "_new" && Meta.has Meta.MultiType a.a_meta then fctx.do_bind <- false; if fd.f_expr = None then begin - if fctx.is_inline then error (cf.cf_name ^ ": Inline functions must have an expression") cf.cf_pos; - if fd.f_type = None then error (cf.cf_name ^ ": Functions without expressions must have an explicit return type") cf.cf_pos; + if fctx.is_inline then missing_expression ctx.com fctx "Inline functions must have an expression" cf.cf_pos; + if fd.f_type = None then typing_error ("Functions without expressions must have an explicit return type") cf.cf_pos; if !allows_no_expr then begin cf.cf_meta <- (Meta.NoExpr,[],null_pos) :: cf.cf_meta; fctx.do_bind <- false; @@ -1137,12 +1214,91 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p = | _ -> () +let type_opt (ctx,cctx,fctx) p t = + let c = cctx.tclass in + let is_truly_extern = + (has_class_flag c CExtern || fctx.is_extern) + && not fctx.is_inline (* if it's inline, we can infer the type from the expression *) + in + match t with + | None when is_truly_extern || (has_class_flag c CInterface) -> + display_error ctx.com "Type required for extern classes and interfaces" p; + t_dynamic + | None when cctx.is_core_api -> + display_error ctx.com "Type required for core api classes" p; + t_dynamic + | None when fctx.is_abstract -> + display_error ctx.com "Type required for abstract functions" p; + t_dynamic + | _ -> + Typeload.load_type_hint ctx p t + +let setup_args_ret ctx cctx fctx name fd p = + let c = cctx.tclass in + let mk = lazy ( + if String.length name < 4 then + MKNormal + else match String.sub name 0 4 with + | "get_" -> + begin match fd.f_args with + | [] -> MKGetter + | _ -> MKNormal + end + | "set_" -> + begin match fd.f_args with + | [_] -> MKSetter + | _ -> MKNormal + end + | _ -> + MKNormal + ) in + let try_find_property_type () = + let name = String.sub name 4 (String.length name - 4) in + let cf = if fctx.is_static then PMap.find name c.cl_statics else PMap.find name c.cl_fields (* TODO: inheritance? *) in + match Lazy.force mk, cf.cf_kind with + | MKGetter, Var({v_read = AccCall}) | MKSetter, Var({v_write = AccCall}) -> cf.cf_type + | _ -> raise Not_found; + in + let maybe_use_property_type th check def = + if th = None && check() then + try + try_find_property_type() + with Not_found -> + def() + else + def() + in + let ret = if fctx.field_kind = FKConstructor then + ctx.t.tvoid + else begin + let def () = + type_opt (ctx,cctx,fctx) p fd.f_type + in + maybe_use_property_type fd.f_type (fun () -> match Lazy.force mk with MKGetter | MKSetter -> true | _ -> false) def + end in + let abstract_this = match cctx.abstract with + | Some a when fctx.is_abstract_member && name <> "_new" (* TODO: this sucks *) && not fctx.is_macro -> + Some a.a_this + | _ -> + None + in + let is_extern = fctx.is_extern || has_class_flag ctx.curclass CExtern in + let type_arg i opt cto p = + let def () = + type_opt (ctx,cctx,fctx) p cto + in + if i = 0 then maybe_use_property_type cto (fun () -> match Lazy.force mk with MKSetter -> true | _ -> false) def else def() + in + let args = new FunctionArguments.function_arguments ctx type_arg is_extern fctx.is_display_field abstract_this fd.f_args in + args,ret + let create_method (ctx,cctx,fctx) c f fd p = - let params = TypeloadFunction.type_function_params ctx fd (fst f.cff_name) p in + let name = fst f.cff_name in + let params = TypeloadFunction.type_function_params ctx fd name p in if fctx.is_generic then begin - if params = [] then error (fst f.cff_name ^ ": Generic functions must have type parameters") p; + if params = [] then typing_error "Generic functions must have type parameters" p; end; - let fd = if fctx.is_macro && not ctx.in_macro && not fctx.is_static then + let fd = if fctx.is_macro && not ctx.com.is_macro_context && not fctx.is_static then (* remove display of first argument which will contain the "this" expression *) { fd with f_args = match fd.f_args with [] -> [] | _ :: l -> l } else @@ -1151,7 +1307,7 @@ let create_method (ctx,cctx,fctx) c f fd p = let fd = if not fctx.is_macro then fd else begin - if ctx.in_macro then begin + if ctx.com.is_macro_context then begin (* a class with a macro cannot be extern in macro context (issue #2015) *) remove_class_flag c CExtern; let texpr = CTPath (mk_type_path (["haxe";"macro"],"Expr")) in @@ -1172,7 +1328,7 @@ let create_method (ctx,cctx,fctx) c f fd p = let to_dyn p t = match t with | { tpackage = ["haxe";"macro"]; tname = "Expr"; tsub = Some ("ExprOf"); tparams = [TPType t] } -> Some t | { tpackage = []; tname = ("ExprOf"); tsub = None; tparams = [TPType t] } -> Some t - | { tpackage = ["haxe"]; tname = ("PosInfos"); tsub = None; tparams = [] } -> error "haxe.PosInfos is not allowed on macro functions, use Context.currentPos() instead" p + | { tpackage = ["haxe"]; tname = ("PosInfos"); tsub = None; tparams = [] } -> typing_error "haxe.PosInfos is not allowed on macro functions, use Context.currentPos() instead" p | _ -> tdyn in { @@ -1184,44 +1340,38 @@ let create_method (ctx,cctx,fctx) c f fd p = end in begin match (has_class_flag c CInterface),fctx.field_kind with | true,FKConstructor -> - error "An interface cannot have a constructor" p; + typing_error "An interface cannot have a constructor" p; | true,_ -> - if not fctx.is_static && fd.f_expr <> None then error (fst f.cff_name ^ ": An interface method cannot have a body") p; - if fctx.is_inline && (has_class_flag c CInterface) then error (fst f.cff_name ^ ": You can't declare inline methods in interfaces") p; + if not fctx.is_static && fd.f_expr <> None then unexpected_expression ctx.com fctx ("An interface method cannot have a body") p; + if fctx.is_inline && (has_class_flag c CInterface) then invalid_modifier ctx.com fctx "inline" "method of interface" p; | false,FKConstructor -> - if fctx.is_static then error "A constructor must not be static" p; + if fctx.is_static then invalid_modifier ctx.com fctx "static" "constructor" p; begin match fd.f_type with | None -> () | Some (CTPath ({ tpackage = []; tname = "Void" } as tp),p) -> if ctx.is_display_file && DisplayPosition.display_position#enclosed_in p then ignore(load_instance ~allow_display:true ctx (tp,p) false); - | _ -> error "A class constructor can't have a return value" p; + | _ -> typing_error "A class constructor can't have a return type" p; end | false,_ -> () end; - let parent = (if not fctx.is_static then get_parent c (fst f.cff_name) else None) in + let parent = (if not fctx.is_static then get_parent c name else None) in let dynamic = List.mem_assoc ADynamic f.cff_access || (match parent with Some { cf_kind = Method MethDynamic } -> true | _ -> false) in - if fctx.is_abstract && dynamic then display_error ctx "Abstract methods may not be dynamic" p; - if fctx.is_inline && dynamic then error (fst f.cff_name ^ ": 'inline' is not allowed on 'dynamic' functions") p; + if dynamic then begin + if fctx.is_abstract then invalid_modifier_combination fctx ctx.com fctx "abstract" "dynamic" p; + if fctx.is_inline then invalid_modifier_combination fctx ctx.com fctx "dynamic" "inline" p; + if fctx.is_abstract_member then invalid_modifier ctx.com fctx "dynamic" "method of abstract" p; + end; let is_override = Option.is_some fctx.override in - if (is_override && fctx.is_static) then error (fst f.cff_name ^ ": 'override' is not allowed on 'static' functions") p; + if (is_override && fctx.is_static) then invalid_modifier_combination fctx ctx.com fctx "override" "static" p; ctx.type_params <- if fctx.is_static && not fctx.is_abstract_member then params else params @ ctx.type_params; (* TODO is_lib: avoid forcing the return type to be typed *) - let ret = if fctx.field_kind = FKConstructor then ctx.t.tvoid else FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p fd.f_type in - let abstract_this = match cctx.abstract with - | Some a when fctx.is_abstract_member && fst f.cff_name <> "_new" (* TODO: this sucks *) && not fctx.is_macro -> - Some a.a_this - | _ -> - None - in - let is_extern = fctx.is_extern || has_class_flag ctx.curclass CExtern in - let type_arg opt t p = FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p t in - let args = new FunctionArguments.function_arguments ctx type_arg is_extern fctx.is_display_field abstract_this fd.f_args in + let args,ret = setup_args_ret ctx cctx fctx (fst f.cff_name) fd p in let t = TFun (args#for_type,ret) in let cf = { - (mk_field (fst f.cff_name) ~public:(is_public (ctx,cctx) f.cff_access parent) t f.cff_pos (pos f.cff_name)) with + (mk_field name ~public:(is_public (ctx,cctx) f.cff_access parent) t f.cff_pos (pos f.cff_name)) with cf_doc = f.cff_doc; cf_meta = f.cff_meta; cf_kind = Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal); @@ -1229,19 +1379,40 @@ let create_method (ctx,cctx,fctx) c f fd p = } in if fctx.is_final then add_class_field_flag cf CfFinal; if fctx.is_extern then add_class_field_flag cf CfExtern; - if fctx.is_abstract then add_class_field_flag cf CfAbstract; + if fctx.is_abstract then begin + if fctx.field_kind = FKConstructor then begin + let p = + try List.assoc AAbstract f.cff_access + with Not_found -> p + in + invalid_modifier ctx.com fctx "abstract" "constructor" p + end; + add_class_field_flag cf CfAbstract; + end; if fctx.is_abstract_member then add_class_field_flag cf CfImpl; if fctx.is_generic then add_class_field_flag cf CfGeneric; + begin match fctx.default with + | Some p -> + begin match ctx.com.platform with + | Java -> + if not (has_class_flag ctx.curclass CExtern) || not (has_class_flag c CInterface) then invalid_modifier_only ctx.com fctx "default" "on extern interfaces" p; + add_class_field_flag cf CfDefault; + | _ -> + invalid_modifier_only ctx.com fctx "default" "on the Java target" p + end; + | None -> + () + end; begin match fctx.overload with | Some p -> if ctx.com.config.pf_overload then add_class_field_flag cf CfOverload else if fctx.field_kind = FKConstructor then - display_error ctx "Constructors cannot be overloaded on this target" p + invalid_modifier ctx.com fctx "overload" "constructor" p else begin add_class_field_flag cf CfOverload; if not (has_class_flag c CExtern || fctx.is_extern) then - display_error ctx "Only extern functions may be overloaded on this target" p + invalid_modifier_only ctx.com fctx "overload" "on extern functions" p end | None -> () @@ -1274,8 +1445,8 @@ let create_method (ctx,cctx,fctx) c f fd p = end else delay ctx PTypeField (fun () -> args#verify_extern); if fd.f_expr <> None then begin - if fctx.is_abstract then display_error ctx "Abstract methods may not have an expression" p - else if not (fctx.is_inline || fctx.is_macro) then ctx.com.warning "Extern non-inline function may not have an expression" p; + if fctx.is_abstract then unexpected_expression ctx.com fctx "Abstract methods may not have an expression" p + else if not (fctx.is_inline || fctx.is_macro) then warning ctx WExternWithExpr "Extern non-inline function may not have an expression" p; end; end; cf @@ -1284,14 +1455,14 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = let name = fst f.cff_name in (* TODO is_lib: lazify load_complex_type *) let ret = (match t, eo with - | None, None -> error (name ^ ": Property requires type-hint or initialization") p; + | None, None -> typing_error "Property requires type-hint or initialization" p; | None, _ -> mk_mono() | Some t, _ -> lazy_display_type ctx (fun () -> load_type_hint ctx p (Some t)) ) in let t_get,t_set = match cctx.abstract with | Some a when fctx.is_abstract_member -> - if Meta.has Meta.IsVar f.cff_meta then error (name ^ ": Abstract properties cannot be real variables") f.cff_pos; - let ta = apply_params a.a_params (List.map snd a.a_params) a.a_this in + if Meta.has Meta.IsVar f.cff_meta then typing_error "Abstract properties cannot be real variables" f.cff_pos; + let ta = apply_params a.a_params (extract_param_types a.a_params) a.a_this in tfun [ta] ret, tfun [ta;ret] ret | _ -> tfun [] ret, TFun(["value",false,ret],ret) in @@ -1309,9 +1480,7 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = } in if fctx.is_abstract_member then add_class_field_flag cf CfImpl; let check_method m t is_getter = - if ctx.com.display.dms_error_policy = EPIgnore then - () - else try + try let overloads = find_accessor m in let rec get_overload overl = match overl with | [tf] -> @@ -1336,14 +1505,14 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = try (match f2.cf_kind with | Method MethMacro -> - display_error ctx (f2.cf_name ^ ": Macro methods cannot be used as property accessor") p; - display_error ctx (compl_msg (f2.cf_name ^ ": Accessor method is here")) f2.cf_pos; + display_error ctx.com (f2.cf_name ^ ": Macro methods cannot be used as property accessor") p; + display_error ~depth:1 ctx.com (compl_msg (f2.cf_name ^ ": Accessor method is here")) f2.cf_pos; | _ -> ()); - unify_raise ctx t2 t f2.cf_pos; + unify_raise t2 t f2.cf_pos; if (fctx.is_abstract_member && not (has_class_field_flag f2 CfImpl)) || (has_class_field_flag f2 CfImpl && not (fctx.is_abstract_member)) then - display_error ctx "Mixing abstract implementation and static properties/accessors is not allowed" f2.cf_pos; - with Error (Unify l,p) -> - raise (Error (Stack (Custom ("In method " ^ m ^ " required by property " ^ name),Unify l),p)) + display_error ctx.com "Mixing abstract implementation and static properties/accessors is not allowed" f2.cf_pos; + with Error (Unify l,p,depth) -> + raise (Error (Stack [(Custom ("In method " ^ m ^ " required by property " ^ name),p);(Unify l,p)],p,depth+1)) ) with | Not_found -> @@ -1357,7 +1526,7 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = let cf = generate_field () in c.cl_fields <- PMap.add cf.cf_name cf c.cl_fields; c.cl_ordered_fields <- cf :: c.cl_ordered_fields; - end else if Diagnostics.is_diagnostics_run ctx.com f.cff_pos then begin + end else if Diagnostics.error_in_diagnostics_run ctx.com f.cff_pos then begin let cf_accessor = generate_field() in remove_class_field_flag cf_accessor CfPublic; if fctx.is_static then add_class_field_flag cf_accessor CfStatic; @@ -1371,10 +1540,10 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = display.module_diagnostics <- MissingFields diag :: display.module_diagnostics end else if not (has_class_flag c CExtern) then begin try - let _, _, f2 = (if not fctx.is_static then let f = PMap.find m c.cl_statics in None, f.cf_type, f else class_field c (List.map snd c.cl_params) m) in - display_error ctx (Printf.sprintf "Method %s is no valid accessor for %s because it is %sstatic" m (name) (if fctx.is_static then "not " else "")) f2.cf_pos + let _, _, f2 = (if not fctx.is_static then let f = PMap.find m c.cl_statics in None, f.cf_type, f else class_field c (extract_param_types c.cl_params) m) in + display_error ctx.com (Printf.sprintf "Method %s is no valid accessor for %s because it is %sstatic" m (name) (if fctx.is_static then "not " else "")) f2.cf_pos with Not_found -> - display_error ctx ("Method " ^ m ^ " required by property " ^ name ^ " is missing") p + display_error ctx.com ("Method " ^ m ^ " required by property " ^ name ^ " is missing") p end in let display_accessor m p = @@ -1396,7 +1565,7 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = if not cctx.is_lib then delay_check (fun() -> check_method get t_get true); AccCall | _,pget -> - display_error ctx (name ^ ": Custom property accessor is no longer supported, please use `get`") pget; + display_error ctx.com (name ^ ": Custom property accessor is no longer supported, please use `get`") pget; AccCall ) in let set = (match set with @@ -1415,10 +1584,10 @@ let create_property (ctx,cctx,fctx) c f (get,set,t,eo) p = if not cctx.is_lib then delay_check (fun() -> check_method set t_set false); AccCall | _,pset -> - display_error ctx (name ^ ": Custom property accessor is no longer supported, please use `set`") pset; + display_error ctx.com (name ^ ": Custom property accessor is no longer supported, please use `set`") pset; AccCall ) in - if (set = AccNever && get = AccNever) then error (name ^ ": Unsupported property combination") p; + if (set = AccNever && get = AccNever) then typing_error (name ^ ": Unsupported property combination") p; cf.cf_kind <- Var { v_read = get; v_write = set }; if fctx.is_extern then add_class_field_flag cf CfExtern; if Meta.has Meta.Enum cf.cf_meta then add_class_field_flag cf CfEnum; @@ -1439,7 +1608,7 @@ let reject_final_static_method ctx cctx fctx f = try snd (List.find (fun (a,p) -> a = AStatic) f.cff_access) with Not_found -> f.cff_pos in - ctx.com.error "Static method cannot be final" p + invalid_modifier_combination fctx ctx.com fctx "final" "static" p let init_field (ctx,cctx,fctx) f = let c = cctx.tclass in @@ -1451,8 +1620,8 @@ let init_field (ctx,cctx,fctx) f = match (fst acc, f.cff_kind) with | APublic, _ | APrivate, _ | AStatic, _ | AFinal, _ | AExtern, _ -> () | ADynamic, FFun _ | AOverride, FFun _ | AMacro, FFun _ | AInline, FFun _ | AInline, FVar _ | AAbstract, FFun _ | AOverload, FFun _ -> () - | _, FVar _ -> display_error ctx ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for variable " ^ name) (snd acc) - | _, FProp _ -> display_error ctx ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for property " ^ name) (snd acc) + | _, FVar _ -> display_error ctx.com ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for variable " ^ name) (snd acc) + | _, FProp _ -> display_error ctx.com ("Invalid accessor '" ^ Ast.s_placed_access acc ^ "' for property " ^ name) (snd acc) ) f.cff_access; begin match fctx.override with | Some _ -> @@ -1462,7 +1631,7 @@ let init_field (ctx,cctx,fctx) f = try List.assoc AOverride f.cff_access with Not_found -> p in - error ("Invalid override on field '" ^ name ^ "': class has no super class") p + invalid_modifier ctx.com fctx "override" "field of class that has no parent" p | _ -> () ); | None -> () @@ -1481,7 +1650,7 @@ let init_field (ctx,cctx,fctx) f = | FProp (get,set,t,eo) -> create_property (ctx,cctx,fctx) c f (get,set,t,eo) p in - (if (fctx.is_static || fctx.is_macro && ctx.in_macro) then add_class_field_flag cf CfStatic); + (if (fctx.is_static || fctx.is_macro && ctx.com.is_macro_context) then add_class_field_flag cf CfStatic); if Meta.has Meta.InheritDoc cf.cf_meta then delay ctx PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) cf); cf @@ -1494,8 +1663,8 @@ let check_overload ctx f fs = Overloads.same_overload_args f.cf_type f2.cf_type f f2 ) fs in - display_error ctx ("Another overloaded field of same signature was already declared : " ^ f.cf_name) f.cf_pos; - display_error ctx (compl_msg "The second field is declared here") f2.cf_pos; + display_error ctx.com ("Another overloaded field of same signature was already declared : " ^ f.cf_name) f.cf_pos; + display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos; false with Not_found -> try (* OVERLOADTODO: generalize this and respect whether or not we actually generate the functions *) @@ -1507,12 +1676,12 @@ let check_overload ctx f fs = Overloads.same_overload_args ~get_vmtype f.cf_type f2.cf_type f f2 ) fs in - display_error ctx ( + display_error ctx.com ( "Another overloaded field of similar signature was already declared : " ^ f.cf_name ^ "\nThe signatures are different in Haxe, but not in the target language" ) f.cf_pos; - display_error ctx (compl_msg "The second field is declared here") f2.cf_pos; + display_error ~depth:1 ctx.com (compl_msg "The second field is declared here") f2.cf_pos; false with Not_found -> true @@ -1529,8 +1698,42 @@ let check_overloads ctx c = List.iter check_field c.cl_ordered_statics; Option.may check_field c.cl_constructor +let finalize_class ctx cctx = + (* push delays in reverse order so they will be run in correct order *) + List.iter (fun (ctx,r) -> + init_class_done ctx; + (match r with + | None -> () + | Some r -> delay ctx PTypeField (fun() -> ignore(lazy_type r))) + ) cctx.delayed_expr + +let check_functional_interface ctx c = + let is_normal_field cf = + (* TODO: more? *) + not (has_class_field_flag cf CfDefault) + in + let rec loop o l = match l with + | cf :: l -> + if is_normal_field cf then begin + if o = None then + loop (Some cf) l + else + None + end else + loop o l + | [] -> + o + in + match loop None c.cl_ordered_fields with + | None -> + () + | Some cf -> + add_class_flag c CFunctionalInterface; + ctx.g.functional_interface_lut#add c.cl_path cf + let init_class ctx c p context_init herits fields = - let ctx,cctx = create_class_context ctx c context_init p in + let cctx = create_class_context c context_init p in + let ctx = create_typer_context_for_class ctx cctx p in if cctx.is_class_debug then print_endline ("Created class context: " ^ dump_class_context cctx); let fields = patch_class ctx c fields in let fields = build_fields (ctx,cctx) c fields in @@ -1558,7 +1761,7 @@ let init_class ctx c p context_init herits fields = | e :: l -> let sc = match fst e with | EConst (Ident s) -> s - | EBinop ((OpEq|OpNotEq|OpGt|OpGte|OpLt|OpLte) as op,(EConst (Ident s),_),(EConst ((Int _ | Float _ | String _) as c),_)) -> s ^ s_binop op ^ s_constant c + | EBinop ((OpEq|OpNotEq|OpGt|OpGte|OpLt|OpLte) as op,(EConst (Ident s),_),(EConst ((Int (_,_) | Float (_,_) | String _) as c),_)) -> s ^ s_binop op ^ s_constant c | _ -> "" in if not (ParserEntry.is_true (ParserEntry.eval ctx.com.defines e)) then @@ -1572,7 +1775,7 @@ let init_class ctx c p context_init herits fields = in let rec check_if_feature = function | [] -> [] - | (Meta.IfFeature,el,_) :: _ -> List.map (fun (e,p) -> match e with EConst (String(s,_)) -> s | _ -> error "String expected" p) el + | (Meta.IfFeature,el,_) :: _ -> List.map (fun (e,p) -> match e with EConst (String(s,_)) -> s | _ -> typing_error "String expected" p) el | _ :: l -> check_if_feature l in let cl_if_feature = check_if_feature c.cl_meta in @@ -1581,18 +1784,20 @@ let init_class ctx c p context_init herits fields = List.iter (fun f -> let p = f.cff_pos in try - let ctx,fctx = create_field_context (ctx,cctx) c f in + let display_modifier = Typeload.check_field_access ctx f in + let fctx = create_field_context cctx f ctx.is_display_file display_modifier in + let ctx = create_typer_context_for_field ctx cctx fctx f in if fctx.is_field_debug then print_endline ("Created field context: " ^ dump_field_context fctx); let cf = init_field (ctx,cctx,fctx) f in if fctx.field_kind = FKInit then begin if !has_init then - display_error ctx ("Duplicate class field declaration : " ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name) cf.cf_name_pos + display_error ctx.com ("Duplicate class field declaration : " ^ (s_type_path c.cl_path) ^ "." ^ cf.cf_name) cf.cf_name_pos else has_init := true end; if fctx.is_field_debug then print_endline ("Created field: " ^ Printer.s_tclass_field "" cf); if fctx.is_static && (has_class_flag c CInterface) && fctx.field_kind <> FKInit && not cctx.is_lib && not ((has_class_flag c CExtern)) then - error "You can only declare static fields in extern interfaces" p; + typing_error "You can only declare static fields in extern interfaces" p; let set_feature s = ctx.m.curmod.m_extra.m_if_feature <- (s,(c,cf,fctx.is_static)) :: ctx.m.curmod.m_extra.m_if_feature in @@ -1617,23 +1822,27 @@ let init_class ctx c p context_init herits fields = if has_class_field_flag cf CfOverload && has_class_field_flag ctor CfOverload then ctor.cf_overloads <- cf :: ctor.cf_overloads else - display_error ctx ("If using overloaded constructors, all constructors must be declared with 'overload'") (if has_class_field_flag cf CfOverload then ctor.cf_pos else cf.cf_pos) + display_error ctx.com ("If using overloaded constructors, all constructors must be declared with 'overload'") (if has_class_field_flag cf CfOverload then ctor.cf_pos else cf.cf_pos) | Some ctor -> - display_error ctx "Duplicate constructor" p + display_error ctx.com "Duplicate constructor" p end | FKInit -> () | FKNormal -> let dup = if fctx.is_static then PMap.exists cf.cf_name c.cl_fields || has_field cf.cf_name c.cl_super else PMap.exists cf.cf_name c.cl_statics in - if not cctx.is_native && not (has_class_flag c CExtern) && dup then error ("Same field name can't be used for both static and instance : " ^ cf.cf_name) p; + if not cctx.is_native && not (has_class_flag c CExtern) && dup then typing_error ("Same field name can't be used for both static and instance : " ^ cf.cf_name) p; if fctx.override <> None then add_class_field_flag cf CfOverride; - let is_var cf = match cf.cf_kind with | Var _ -> true | _ -> false in + let is_var cf = match cf.cf_kind with + | Var {v_read = AccRequire _; v_write = AccRequire _} -> false + | Var _ -> true + | _ -> false + in if PMap.mem cf.cf_name (if fctx.is_static then c.cl_statics else c.cl_fields) then if has_class_field_flag cf CfOverload && not (is_var cf) then let mainf = PMap.find cf.cf_name (if fctx.is_static then c.cl_statics else c.cl_fields) in - if is_var mainf then display_error ctx "Cannot declare a variable with same name as a method" mainf.cf_pos; - (if not (has_class_field_flag mainf CfOverload) then display_error ctx ("Overloaded methods must have 'overload' accessor") mainf.cf_pos); + if is_var mainf then display_error ctx.com "Cannot declare a variable with same name as a method" mainf.cf_pos; + (if not (has_class_field_flag mainf CfOverload) then display_error ctx.com ("Overloaded methods must have 'overload' accessor") mainf.cf_pos); mainf.cf_overloads <- cf :: cf.cf_overloads @ mainf.cf_overloads else let type_kind,path = match c.cl_kind with @@ -1641,21 +1850,23 @@ let init_class ctx c p context_init herits fields = | KModuleFields m -> "module",m.m_path | _ -> "class",c.cl_path in - display_error ctx ("Duplicate " ^ type_kind ^ " field declaration : " ^ s_type_path path ^ "." ^ cf.cf_name) cf.cf_name_pos + display_error ctx.com ("Duplicate " ^ type_kind ^ " field declaration : " ^ s_type_path path ^ "." ^ cf.cf_name) cf.cf_name_pos else if fctx.do_add then TClass.add_field c cf end - with Error (Custom str,p2) when p = p2 -> - display_error ctx str p + with Error (Custom str,p2,depth) when p = p2 -> + display_error ~depth ctx.com str p ) fields; - (match cctx.abstract with - | Some a -> - a.a_to_field <- List.rev a.a_to_field; - a.a_from_field <- List.rev a.a_from_field; - a.a_ops <- List.rev a.a_ops; - a.a_unops <- List.rev a.a_unops; - a.a_array <- List.rev a.a_array; - | None -> ()); + begin match cctx.abstract with + | Some a -> + a.a_to_field <- List.rev a.a_to_field; + a.a_from_field <- List.rev a.a_from_field; + a.a_ops <- List.rev a.a_ops; + a.a_unops <- List.rev a.a_unops; + a.a_array <- List.rev a.a_array; + | None -> + if (has_class_flag c CInterface) && ctx.com.platform = Java then check_functional_interface ctx c; + end; c.cl_ordered_statics <- List.rev c.cl_ordered_statics; c.cl_ordered_fields <- List.rev c.cl_ordered_fields; (* if ctx.is_display_file && not cctx.has_display_field && Display.is_display_position c.cl_pos && ctx.com.display.dms_kind = DMToplevel then begin @@ -1693,14 +1904,12 @@ let init_class ctx c p context_init herits fields = in if has_struct_init then if (has_class_flag c CInterface) then - display_error ctx "@:structInit is not allowed on interfaces" struct_init_pos - else if (has_class_flag c CAbstract) then - display_error ctx "@:structInit is not allowed on abstract classes" struct_init_pos + display_error ctx.com "@:structInit is not allowed on interfaces" struct_init_pos else ensure_struct_init_constructor ctx c fields p; begin match cctx.uninitialized_final with - | cf :: cfl when c.cl_constructor = None -> - if Diagnostics.is_diagnostics_run ctx.com cf.cf_name_pos then begin + | cf :: cfl when c.cl_constructor = None && not (has_class_flag c CAbstract) -> + if Diagnostics.error_in_diagnostics_run ctx.com cf.cf_name_pos then begin let diag = { mf_pos = c.cl_name_pos; mf_on = TClassDecl c; @@ -1710,8 +1919,8 @@ let init_class ctx c p context_init herits fields = let display = ctx.com.display_information in display.module_diagnostics <- MissingFields diag :: display.module_diagnostics end else begin - display_error ctx "This class has uninitialized final vars, which requires a constructor" p; - display_error ctx "Example of an uninitialized final var" cf.cf_name_pos; + display_error ctx.com "This class has uninitialized final vars, which requires a constructor" p; + display_error ctx.com "Example of an uninitialized final var" cf.cf_name_pos; end | _ -> () @@ -1719,10 +1928,4 @@ let init_class ctx c p context_init herits fields = if not has_struct_init then (* add_constructor does not deal with overloads correctly *) if not ctx.com.config.pf_overload then TypeloadFunction.add_constructor ctx c cctx.force_constructor p; - (* push delays in reverse order so they will be run in correct order *) - List.iter (fun (ctx,r) -> - init_class_done ctx; - (match r with - | None -> () - | Some r -> delay ctx PTypeField (fun() -> ignore(lazy_type r))) - ) cctx.delayed_expr + finalize_class ctx cctx diff --git a/src/typing/typeloadFunction.ml b/src/typing/typeloadFunction.ml index d401f197..92679a37 100644 --- a/src/typing/typeloadFunction.ml +++ b/src/typing/typeloadFunction.ml @@ -47,7 +47,7 @@ let save_field_state ctx = let type_function_params ctx fd fname p = let params = ref [] in - params := Typeload.type_type_params ctx ([],fname) (fun() -> !params) p fd.f_params; + params := Typeload.type_type_params ctx TPHMethod ([],fname) (fun() -> !params) p fd.f_params; !params let type_function ctx (args : function_arguments) ret fmode e do_display p = @@ -59,7 +59,7 @@ let type_function ctx (args : function_arguments) ret fmode e do_display p = args#bring_into_context; let e = match e with | None -> - if ctx.com.display.dms_error_policy = EPIgnore then + if ignore_error ctx.com then (* when we don't care because we're in display mode, just act like the function has an empty block body. this is fine even if function defines a return type, because returns aren't checked in this mode @@ -67,9 +67,9 @@ let type_function ctx (args : function_arguments) ret fmode e do_display p = EBlock [],p else if fmode = FunMember && has_class_flag ctx.curclass CAbstract then - error "Function body or abstract modifier required" p + typing_error "Function body or abstract modifier required" p else - error "Function body required" p + typing_error "Function body required" p | Some e -> e in let is_position_debug = Meta.has (Meta.Custom ":debug.position") ctx.curfield.cf_meta in @@ -104,7 +104,6 @@ let type_function ctx (args : function_arguments) ret fmode e do_display p = can _not_ use type_iseq to avoid the Void check above because that would turn Dynamic returns to Void returns. *) | TMono t when not (has_return e) -> ignore(link t ret ctx.t.tvoid) - | _ when ctx.com.display.dms_error_policy = EPIgnore -> () | _ -> (try TypeloadCheck.return_flow ctx e with Exit -> ()) end; let rec loop e = @@ -137,7 +136,7 @@ let type_function ctx (args : function_arguments) ret fmode e do_display p = let e_super_call = mk (TCall(e_super,[])) ctx.t.tvoid e.epos in concat e_super_call e else begin - display_error ctx "Missing super constructor call" p; + display_error ctx.com "Missing super constructor call" p; e end with @@ -183,16 +182,13 @@ let type_function ctx args ret fmode e do_display p = let add_constructor ctx c force_constructor p = if c.cl_constructor <> None then () else - let constructor = try Some (Type.get_constructor_class c (List.map snd c.cl_params)) with Not_found -> None in + let constructor = try Some (Type.get_constructor_class c (extract_param_types c.cl_params)) with Not_found -> None in match constructor with | Some(cfsup,csup,cparams) when not (has_class_flag c CExtern) -> - let cf = { - cfsup with - cf_pos = p; - cf_meta = List.filter (fun (m,_,_) -> m = Meta.CompilerGenerated) cfsup.cf_meta; - cf_doc = None; - cf_expr = None; - } in + let cf = mk_field "new" cfsup.cf_type p null_pos in + cf.cf_kind <- cfsup.cf_kind; + cf.cf_params <- cfsup.cf_params; + cf.cf_meta <- List.filter (fun (m,_,_) -> m = Meta.CompilerGenerated) cfsup.cf_meta; let r = exc_protect ctx (fun r -> let t = mk_mono() in r := lazy_processing (fun() -> t); diff --git a/src/typing/typeloadModule.ml b/src/typing/typeloadModule.ml index 3ac50d12..c83c1abb 100644 --- a/src/typing/typeloadModule.ml +++ b/src/typing/typeloadModule.ml @@ -30,627 +30,372 @@ open Common open Typeload open Error -let get_policy ctx mpath = +let get_policy g mpath = let sl1 = full_dot_path2 mpath mpath in - List.fold_left (fun acc (sl2,policy,recursive) -> if match_path recursive sl1 sl2 then policy @ acc else acc) [] ctx.g.module_check_policies + List.fold_left (fun acc (sl2,policy,recursive) -> if match_path recursive sl1 sl2 then policy @ acc else acc) [] g.module_check_policies -let make_module ctx mpath file loadp = - let m = { - m_id = alloc_mid(); - m_path = mpath; - m_types = []; - m_statics = None; - m_extra = module_extra (Path.get_full_path file) (Define.get_signature ctx.com.defines) (file_time file) (if ctx.in_macro then MMacro else MCode) (get_policy ctx mpath); - } in - m - -let add_module ctx m p = - List.iter (TypeloadCheck.check_module_types ctx m p) m.m_types; - Hashtbl.add ctx.g.modules m.m_path m +let field_of_static_definition d p = + { + cff_name = d.d_name; + cff_doc = d.d_doc; + cff_pos = p; + cff_meta = d.d_meta; + cff_access = (AStatic,null_pos) :: d.d_flags; + cff_kind = d.d_data; + } -module StrictMeta = struct - let get_native_repr md pos = - let path, meta = match md with - | TClassDecl cl -> cl.cl_path, cl.cl_meta - | TEnumDecl e -> e.e_path, e.e_meta - | TTypeDecl t -> t.t_path, t.t_meta - | TAbstractDecl a -> a.a_path, a.a_meta - in - let rec loop acc = function - | (Meta.JavaCanonical,[EConst(String(pack,_)),_; EConst(String(name,_)),_],_) :: _ -> - ExtString.String.nsplit pack ".", name - | (Meta.Native,[EConst(String(name,_)),_],_) :: meta -> - loop (Ast.parse_path name) meta - | _ :: meta -> - loop acc meta - | [] -> - acc - in - let pack, name = loop path meta in - match pack with - | [] -> - (EConst(Ident(name)), pos) - | hd :: tl -> - let rec loop pack expr = match pack with - | hd :: tl -> - loop tl (EField(expr,hd),pos) - | [] -> - (EField(expr,name),pos) - in - loop tl (EConst(Ident(hd)),pos) +module ModuleLevel = struct + let make_module ctx mpath file loadp = + let m = { + m_id = alloc_mid(); + m_path = mpath; + m_types = []; + m_statics = None; + m_extra = module_extra (Path.get_full_path file) (Define.get_signature ctx.com.defines) (file_time file) (if ctx.com.is_macro_context then MMacro else MCode) (get_policy ctx.g mpath); + } in + m - let rec process_meta_argument ?(toplevel=true) ctx expr = match expr.eexpr with - | TField(e,f) -> - (EField(process_meta_argument ~toplevel:false ctx e,field_name f),expr.epos) - | TConst(TInt i) -> - (EConst(Int (Int32.to_string i)), expr.epos) - | TConst(TFloat f) -> - (EConst(Float f), expr.epos) - | TConst(TString s) -> - (EConst(String(s,SDoubleQuotes)), expr.epos) - | TConst TNull -> - (EConst(Ident "null"), expr.epos) - | TConst(TBool b) -> - (EConst(Ident (string_of_bool b)), expr.epos) - | TCast(e,_) | TMeta(_,e) | TParenthesis(e) -> - process_meta_argument ~toplevel ctx e - | TTypeExpr md when toplevel -> - let p = expr.epos in - if ctx.com.platform = Cs then - (ECall( (EConst(Ident "typeof"), p), [get_native_repr md expr.epos] ), p) - else - (EField(get_native_repr md expr.epos, "class"), p) - | TTypeExpr md -> - get_native_repr md expr.epos - | _ -> - display_error ctx "This expression is too complex to be a strict metadata argument" expr.epos; - (EConst(Ident "null"), expr.epos) + let add_module ctx m p = + List.iter (TypeloadCheck.check_module_types ctx m p) m.m_types; + ctx.com.module_lut#add m.m_path m - let handle_fields ctx fields_to_check with_type_expr = - List.map (fun ((name,_,_),expr) -> - let pos = snd expr in - let field = (EField(with_type_expr,name), pos) in - let fieldexpr = (EConst(Ident name),pos) in - let left_side = match ctx.com.platform with - | Cs -> field - | Java -> (ECall(field,[]),pos) - | _ -> die "" __LOC__ + (* + Build module structure : should be atomic - no type loading is possible + *) + let create_module_types ctx m tdecls loadp = + let com = ctx.com in + let decls = ref [] in + let statics = ref [] in + let check_name name meta also_statics p = + DeprecationCheck.check_is com name meta p; + let error prev_pos = + display_error ctx.com ("Name " ^ name ^ " is already defined in this module") p; + typing_error ~depth:1 (compl_msg "Previous declaration here") prev_pos; in - - let left = type_expr ctx left_side NoValue in - let right = type_expr ctx expr (WithType.with_type left.etype) in - unify ctx left.etype right.etype (snd expr); - (EBinop(Ast.OpAssign,fieldexpr,process_meta_argument ctx right), pos) - ) fields_to_check - - let make_meta ctx texpr extra = - match texpr.eexpr with - | TNew(c,_,el) -> - ECall(get_native_repr (TClassDecl c) texpr.epos, (List.map (process_meta_argument ctx) el) @ extra), texpr.epos - | TTypeExpr(md) -> - ECall(get_native_repr md texpr.epos, extra), texpr.epos - | _ -> - display_error ctx "Unexpected expression" texpr.epos; die "" __LOC__ - - let get_strict_meta ctx meta params pos = - let pf = ctx.com.platform in - let changed_expr, fields_to_check, ctype = match params with - | [ECall(ef, el),p] -> - (* check last argument *) - let el, fields = match List.rev el with - | (EObjectDecl(decl),_) :: el -> - List.rev el, decl - | _ -> - el, [] - in - let tpath = field_to_type_path ctx ef in - if pf = Cs then - (ENew((tpath,snd ef), el), p), fields, CTPath tpath - else - ef, fields, CTPath tpath - | [EConst(Ident i),p as expr] -> - let tpath = { tpackage=[]; tname=i; tparams=[]; tsub=None } in - if pf = Cs then - (ENew((tpath,p), []), p), [], CTPath tpath - else - expr, [], CTPath tpath - | [ (EField(_),p as field) ] -> - let tpath = field_to_type_path ctx field in - if pf = Cs then - (ENew((tpath,p), []), p), [], CTPath tpath - else - field, [], CTPath tpath - | _ -> - display_error ctx "A @:strict metadata must contain exactly one parameter. Please check the documentation for more information" pos; - raise Exit + List.iter (fun (t2,(_,p2)) -> + if snd (t_path t2) = name then error (t_infos t2).mt_name_pos + ) !decls; + if also_statics then + List.iter (fun (d,_) -> + if fst d.d_name = name then error (snd d.d_name) + ) !statics in - let texpr = type_expr ctx changed_expr NoValue in - let with_type_expr = (ECheckType( (EConst (Ident "null"), pos), (ctype,null_pos) ), pos) in - let extra = handle_fields ctx fields_to_check with_type_expr in - meta, [make_meta ctx texpr extra], pos - - let check_strict_meta ctx metas = - let pf = ctx.com.platform in - match pf with - | Cs | Java -> - let ret = ref [] in + let make_path name priv meta p = + check_name name meta true p; + if priv then (fst m.m_path @ ["_" ^ snd m.m_path], name) else (fst m.m_path, name) + in + let has_declaration = ref false in + let rec make_decl acc decl = + let p = snd decl in + let check_type_name type_name meta = + let module_name = snd m.m_path in + if type_name <> module_name && not (Meta.has Meta.Native meta) then Typecore.check_uppercase_identifier_name ctx type_name "type" p; + in + let acc = (match fst decl with + | EImport _ | EUsing _ -> + if !has_declaration then typing_error "import and using may not appear after a declaration" p; + acc + | EStatic d -> + check_name (fst d.d_name) d.d_meta false (snd d.d_name); + has_declaration := true; + statics := (d,p) :: !statics; + acc; + | EClass d -> + let name = fst d.d_name in + has_declaration := true; + let priv = List.mem HPrivate d.d_flags in + let path = make_path name priv d.d_meta (snd d.d_name) in + let c = mk_class m path p (pos d.d_name) in + (* we shouldn't load any other type until we propertly set cl_build *) + c.cl_build <- (fun() -> typing_error (s_type_path c.cl_path ^ " is not ready to be accessed, separate your type declarations in several files") p); + c.cl_module <- m; + c.cl_private <- priv; + c.cl_doc <- d.d_doc; + c.cl_meta <- d.d_meta; + if List.mem HAbstract d.d_flags then add_class_flag c CAbstract; List.iter (function - | Meta.AssemblyStrict,params,pos -> (try - ret := get_strict_meta ctx Meta.AssemblyMeta params pos :: !ret - with | Exit -> ()) - | Meta.Strict,params,pos -> (try - ret := get_strict_meta ctx Meta.Meta params pos :: !ret - with | Exit -> ()) + | HExtern -> add_class_flag c CExtern + | HInterface -> add_class_flag c CInterface + | HFinal -> add_class_flag c CFinal | _ -> () - ) metas; - !ret - | _ -> [] -end - -(* - Build module structure : should be atomic - no type loading is possible -*) -let module_pass_1 ctx m tdecls loadp = - let com = ctx.com in - let decls = ref [] in - let statics = ref [] in - let check_name name meta also_statics p = - DeprecationCheck.check_is com name meta p; - let error prev_pos = - display_error ctx ("Name " ^ name ^ " is already defined in this module") p; - error (compl_msg "Previous declaration here") prev_pos; - in - List.iter (fun (t2,(_,p2)) -> - if snd (t_path t2) = name then error (t_infos t2).mt_name_pos - ) !decls; - if also_statics then - List.iter (fun (d,_) -> - if fst d.d_name = name then error (snd d.d_name) - ) !statics - in - let make_path name priv meta p = - check_name name meta true p; - if priv then (fst m.m_path @ ["_" ^ snd m.m_path], name) else (fst m.m_path, name) - in - let has_declaration = ref false in - let rec make_decl acc decl = - let p = snd decl in - let check_type_name type_name meta = - let module_name = snd m.m_path in - if type_name <> module_name && not (Meta.has Meta.Native meta) then Typecore.check_uppercase_identifier_name ctx type_name "type" p; - in - let acc = (match fst decl with - | EImport _ | EUsing _ -> - if !has_declaration then error "import and using may not appear after a declaration" p; - acc - | EStatic d -> - check_name (fst d.d_name) d.d_meta false (snd d.d_name); - has_declaration := true; - statics := (d,p) :: !statics; - acc; - | EClass d -> - let name = fst d.d_name in - has_declaration := true; - let priv = List.mem HPrivate d.d_flags in - let path = make_path name priv d.d_meta (snd d.d_name) in - let c = mk_class m path p (pos d.d_name) in - (* we shouldn't load any other type until we propertly set cl_build *) - c.cl_build <- (fun() -> error (s_type_path c.cl_path ^ " is not ready to be accessed, separate your type declarations in several files") p); - c.cl_module <- m; - c.cl_private <- priv; - c.cl_doc <- d.d_doc; - c.cl_meta <- d.d_meta; - if List.mem HAbstract d.d_flags then add_class_flag c CAbstract; - List.iter (function - | HExtern -> add_class_flag c CExtern - | HInterface -> add_class_flag c CInterface - | HFinal -> add_class_flag c CFinal - | _ -> () - ) d.d_flags; - if not (has_class_flag c CExtern) then check_type_name name d.d_meta; - if has_class_flag c CAbstract then begin - if has_class_flag c CInterface then display_error ctx "An interface may not be abstract" c.cl_name_pos; - if has_class_flag c CFinal then display_error ctx "An abstract class may not be final" c.cl_name_pos; - end; - decls := (TClassDecl c, decl) :: !decls; - acc - | EEnum d -> - let name = fst d.d_name in - has_declaration := true; - let priv = List.mem EPrivate d.d_flags in - let path = make_path name priv d.d_meta p in - if Meta.has (Meta.Custom ":fakeEnum") d.d_meta then error "@:fakeEnum enums is no longer supported in Haxe 4, use extern enum abstract instead" p; - let e = { - e_path = path; - e_module = m; - e_pos = p; - e_name_pos = (pos d.d_name); - e_doc = d.d_doc; - e_meta = d.d_meta; - e_params = []; - e_using = []; - e_private = priv; - e_extern = List.mem EExtern d.d_flags; - e_constrs = PMap.empty; - e_names = []; - e_type = enum_module_type m path p; - } in - if not e.e_extern then check_type_name name d.d_meta; - decls := (TEnumDecl e, decl) :: !decls; - acc - | ETypedef d -> - let name = fst d.d_name in - check_type_name name d.d_meta; - has_declaration := true; - let priv = List.mem EPrivate d.d_flags in - let path = make_path name priv d.d_meta p in - let t = { - t_path = path; - t_module = m; - t_pos = p; - t_name_pos = pos d.d_name; - t_doc = d.d_doc; - t_private = priv; - t_params = []; - t_using = []; - t_type = mk_mono(); - t_meta = d.d_meta; - } in - (* failsafe in case the typedef is not initialized (see #3933) *) - delay ctx PBuildModule (fun () -> - match t.t_type with - | TMono r -> (match r.tm_type with None -> Monomorph.bind r com.basic.tvoid | _ -> ()) - | _ -> () - ); - decls := (TTypeDecl t, decl) :: !decls; - acc - | EAbstract d -> - let name = fst d.d_name in - check_type_name name d.d_meta; - let priv = List.mem AbPrivate d.d_flags in - let path = make_path name priv d.d_meta p in - let a = { - a_path = path; - a_private = priv; - a_module = m; - a_pos = p; - a_name_pos = pos d.d_name; - a_doc = d.d_doc; - a_params = []; - a_using = []; - a_meta = d.d_meta; - a_from = []; - a_to = []; - a_from_field = []; - a_to_field = []; - a_ops = []; - a_unops = []; - a_impl = None; - a_array = []; - a_this = mk_mono(); - a_read = None; - a_write = None; - a_enum = List.mem AbEnum d.d_flags || Meta.has Meta.Enum d.d_meta; - } in - if a.a_enum && not (Meta.has Meta.Enum a.a_meta) then a.a_meta <- (Meta.Enum,[],null_pos) :: a.a_meta; - decls := (TAbstractDecl a, decl) :: !decls; - match d.d_data with - | [] when Meta.has Meta.CoreType a.a_meta -> - a.a_this <- t_dynamic; + ) d.d_flags; + if not (has_class_flag c CExtern) then check_type_name name d.d_meta; + if has_class_flag c CAbstract then begin + if has_class_flag c CInterface then display_error ctx.com "An interface may not be abstract" c.cl_name_pos; + if has_class_flag c CFinal then display_error ctx.com "An abstract class may not be final" c.cl_name_pos; + end; + decls := (TClassDecl c, decl) :: !decls; acc - | fields -> - let a_t = - let params = List.map (fun t -> TPType (CTPath (mk_type_path ([],fst t.tp_name)),null_pos)) d.d_params in - CTPath (mk_type_path ~params ([],fst d.d_name)),null_pos - in - let rec loop = function - | [] -> a_t - | AbOver t :: _ -> t - | _ :: l -> loop l - in - let this_t = loop d.d_flags in - let fields = List.map (TypeloadFields.transform_abstract_field com this_t a_t a) fields in - let meta = ref [] in - if has_meta Meta.Dce a.a_meta then meta := (Meta.Dce,[],null_pos) :: !meta; - let acc = make_decl acc (EClass { d_name = (fst d.d_name) ^ "_Impl_",snd d.d_name; d_flags = [HPrivate]; d_data = fields; d_doc = None; d_params = []; d_meta = !meta },p) in + | EEnum d -> + let name = fst d.d_name in + has_declaration := true; + let priv = List.mem EPrivate d.d_flags in + let path = make_path name priv d.d_meta p in + if Meta.has (Meta.Custom ":fakeEnum") d.d_meta then typing_error "@:fakeEnum enums is no longer supported in Haxe 4, use extern enum abstract instead" p; + let e = { + e_path = path; + e_module = m; + e_pos = p; + e_name_pos = (pos d.d_name); + e_doc = d.d_doc; + e_meta = d.d_meta; + e_params = []; + e_using = []; + e_restore = (fun () -> ()); + e_private = priv; + e_extern = List.mem EExtern d.d_flags; + e_constrs = PMap.empty; + e_names = []; + e_type = enum_module_type m path p; + } in + if not e.e_extern then check_type_name name d.d_meta; + decls := (TEnumDecl e, decl) :: !decls; + acc + | ETypedef d -> + let name = fst d.d_name in + check_type_name name d.d_meta; + has_declaration := true; + let priv = List.mem EPrivate d.d_flags in + let path = make_path name priv d.d_meta p in + let t = {(mk_typedef m path p (pos d.d_name) (mk_mono())) with + t_doc = d.d_doc; + t_private = priv; + t_meta = d.d_meta; + } in + (* failsafe in case the typedef is not initialized (see #3933) *) + delay ctx PBuildModule (fun () -> + match t.t_type with + | TMono r -> (match r.tm_type with None -> Monomorph.bind r com.basic.tvoid | _ -> ()) + | _ -> () + ); + decls := (TTypeDecl t, decl) :: !decls; + acc + | EAbstract d -> + let name = fst d.d_name in + check_type_name name d.d_meta; + let priv = List.mem AbPrivate d.d_flags in + let path = make_path name priv d.d_meta p in + let a = { + a_path = path; + a_private = priv; + a_module = m; + a_pos = p; + a_name_pos = pos d.d_name; + a_doc = d.d_doc; + a_params = []; + a_using = []; + a_restore = (fun () -> ()); + a_meta = d.d_meta; + a_from = []; + a_to = []; + a_from_field = []; + a_to_field = []; + a_ops = []; + a_unops = []; + a_impl = None; + a_array = []; + a_this = mk_mono(); + a_read = None; + a_write = None; + a_call = None; + a_enum = List.mem AbEnum d.d_flags || Meta.has Meta.Enum d.d_meta; + } in + if a.a_enum && not (Meta.has Meta.Enum a.a_meta) then a.a_meta <- (Meta.Enum,[],null_pos) :: a.a_meta; + decls := (TAbstractDecl a, decl) :: !decls; + match d.d_data with + | [] when Meta.has Meta.CoreType a.a_meta -> + a.a_this <- t_dynamic; + acc + | fields -> + let a_t = + let params = List.map (fun t -> TPType (CTPath (mk_type_path ([],fst t.tp_name)),null_pos)) d.d_params in + CTPath (mk_type_path ~params ([],fst d.d_name)),null_pos + in + let rec loop = function + | [] -> a_t + | AbOver t :: _ -> t + | _ :: l -> loop l + in + let this_t = loop d.d_flags in + let fields = List.map (TypeloadFields.transform_abstract_field com this_t a_t a) fields in + let meta = ref [] in + if has_meta Meta.Dce a.a_meta then meta := (Meta.Dce,[],null_pos) :: !meta; + let acc = make_decl acc (EClass { d_name = (fst d.d_name) ^ "_Impl_",snd d.d_name; d_flags = [HPrivate]; d_data = fields; d_doc = None; d_params = []; d_meta = !meta },p) in + (match !decls with + | (TClassDecl c,_) :: _ -> + List.iter (fun m -> match m with + | ((Meta.Using | Meta.Build | Meta.CoreApi | Meta.Allow | Meta.Access | Meta.Enum | Meta.Dce | Meta.Native | Meta.HlNative | Meta.JsRequire | Meta.PythonImport | Meta.Expose | Meta.Deprecated | Meta.PhpGlobal | Meta.PublicFields),_,_) -> + c.cl_meta <- m :: c.cl_meta; + | _ -> + () + ) a.a_meta; + a.a_impl <- Some c; + c.cl_kind <- KAbstractImpl a; + add_class_flag c CFinal; + | _ -> die "" __LOC__); + acc + ) in + decl :: acc + in + let tdecls = List.fold_left make_decl [] tdecls in + let tdecls = + match !statics with + | [] -> + tdecls + | statics -> + let first_pos = ref null_pos in + let fields = List.map (fun (d,p) -> + first_pos := p; + field_of_static_definition d p; + ) statics in + let p = let p = !first_pos in { p with pmax = p.pmin } in + let c = EClass { + d_name = (snd m.m_path) ^ "_Fields_", null_pos; + d_flags = [HPrivate]; + d_data = List.rev fields; + d_doc = None; + d_params = []; + d_meta = [] + } in + let tdecls = make_decl tdecls (c,p) in (match !decls with | (TClassDecl c,_) :: _ -> - List.iter (fun m -> match m with - | ((Meta.Using | Meta.Build | Meta.CoreApi | Meta.Allow | Meta.Access | Meta.Enum | Meta.Dce | Meta.Native | Meta.HlNative | Meta.JsRequire | Meta.PythonImport | Meta.Expose | Meta.Deprecated | Meta.PhpGlobal),_,_) -> - c.cl_meta <- m :: c.cl_meta; - | _ -> - () - ) a.a_meta; - a.a_impl <- Some c; - c.cl_kind <- KAbstractImpl a; + assert (m.m_statics = None); + m.m_statics <- Some c; + c.cl_kind <- KModuleFields m; add_class_flag c CFinal; - | _ -> die "" __LOC__); - acc - ) in - decl :: acc - in - let tdecls = List.fold_left make_decl [] tdecls in - let tdecls = - match !statics with - | [] -> - tdecls - | statics -> - let first_pos = ref null_pos in - let fields = List.map (fun (d,p) -> - first_pos := p; - { - cff_name = d.d_name; - cff_doc = d.d_doc; - cff_pos = p; - cff_meta = d.d_meta; - cff_access = (AStatic,null_pos) :: d.d_flags; - cff_kind = d.d_data; - } - ) statics in - let p = let p = !first_pos in { p with pmax = p.pmin } in - let c = EClass { - d_name = (snd m.m_path) ^ "_Fields_", null_pos; - d_flags = [HPrivate]; - d_data = List.rev fields; - d_doc = None; - d_params = []; - d_meta = [] - } in - let tdecls = make_decl tdecls (c,p) in - (match !decls with - | (TClassDecl c,_) :: _ -> - assert (m.m_statics = None); - m.m_statics <- Some c; - c.cl_kind <- KModuleFields m; - add_class_flag c CFinal; - | _ -> assert false); - tdecls - - in - let decls = List.rev !decls in - decls, List.rev tdecls + | _ -> assert false); + tdecls -let load_enum_field ctx e et is_flat index c = - let p = c.ec_pos in - let params = ref [] in - params := type_type_params ~enum_constructor:true ctx ([],fst c.ec_name) (fun() -> !params) c.ec_pos c.ec_params; - let params = !params in - let ctx = { ctx with type_params = params @ ctx.type_params } in - let rt = (match c.ec_type with - | None -> et - | Some (t,pt) -> - let t = load_complex_type ctx true (t,pt) in - (match follow t with - | TEnum (te,_) when te == e -> - () - | _ -> - error "Explicit enum type must be of the same enum type" pt); - t - ) in - let t = (match c.ec_args with - | [] -> rt - | l -> - is_flat := false; - let pnames = ref PMap.empty in - TFun (List.map (fun (s,opt,(t,tp)) -> - (match t with CTPath({tpackage=[];tname="Void"}) -> error "Arguments of type Void are not allowed in enum constructors" tp | _ -> ()); - if PMap.mem s (!pnames) then error ("Duplicate argument `" ^ s ^ "` in enum constructor " ^ fst c.ec_name) p; - pnames := PMap.add s () (!pnames); - s, opt, load_type_hint ~opt ctx p (Some (t,tp)) - ) l, rt) - ) in - let f = { - ef_name = fst c.ec_name; - ef_type = t; - ef_pos = p; - ef_name_pos = snd c.ec_name; - ef_doc = c.ec_doc; - ef_index = !index; - ef_params = params; - ef_meta = c.ec_meta; - } in - DeprecationCheck.check_is ctx.com f.ef_name f.ef_meta f.ef_name_pos; - let cf = { - (mk_field f.ef_name f.ef_type p f.ef_name_pos) with - cf_kind = (match follow f.ef_type with - | TFun _ -> Method MethNormal - | _ -> Var { v_read = AccNormal; v_write = AccNo } - ); - cf_doc = f.ef_doc; - cf_params = f.ef_params; - } in - if ctx.is_display_file && DisplayPosition.display_position#enclosed_in f.ef_name_pos then - DisplayEmitter.display_enum_field ctx e f p; - f,cf + in + let decls = List.rev !decls in + decls, List.rev tdecls -(* - In this pass, we can access load and access other modules types, but we cannot follow them or access their structure - since they have not been setup. We also build a context_init list that will be evaluated the first time we evaluate - an expression into the context -*) -let init_module_type ctx context_init (decl,p) = - let get_type name = - try List.find (fun t -> snd (t_infos t).mt_path = name) ctx.m.curmod.m_types with Not_found -> die "" __LOC__ - in - let commit_import path mode p = - ctx.m.module_imports <- (path,mode) :: ctx.m.module_imports; - if Filename.basename p.pfile <> "import.hx" then ImportHandling.add_import_position ctx p path; - in - let check_path_display path p = - if DisplayPosition.display_position#is_in_file (ctx.com.file_keys#get p.pfile) then DisplayPath.handle_path_display ctx path p - in - let init_import path mode = - check_path_display path p; - let rec loop acc = function - | x :: l when is_lower_ident (fst x) -> loop (x::acc) l - | rest -> List.rev acc, rest + let handle_import_hx ctx m decls p = + let com = ctx.com in + let path_split = match List.rev (Path.get_path_parts (Path.UniqueKey.lazy_path m.m_extra.m_file)) with + | [] -> [] + | _ :: l -> l in - let pack, rest = loop [] path in - (match rest with - | [] -> - (match mode with - | IAll -> - ctx.m.wildcard_packages <- (List.map fst pack,p) :: ctx.m.wildcard_packages - | _ -> - (match List.rev path with - (* p spans `import |` (to the display position), so we take the pmax here *) - | [] -> DisplayException.raise_fields (DisplayToplevel.collect ctx TKType NoValue true) CRImport (DisplayTypes.make_subject None {p with pmin = p.pmax}) - | (_,p) :: _ -> error "Module name must start with an uppercase letter" p)) - | (tname,p2) :: rest -> - let p1 = (match pack with [] -> p2 | (_,p1) :: _ -> p1) in - let p_type = punion p1 p2 in - let md = ctx.g.do_load_module ctx (List.map fst pack,tname) p_type in - let types = md.m_types in - let no_private (t,_) = not (t_infos t).mt_private in - let error_private p = error "Importing private declarations from a module is not allowed" p in - let chk_private t p = if ctx.m.curmod != (t_infos t).mt_module && (t_infos t).mt_private then error_private p in - let has_name name t = snd (t_infos t).mt_path = name in - let get_type tname = - let t = (try List.find (has_name tname) types with Not_found -> error (StringError.string_error tname (List.map (fun mt -> snd (t_infos mt).mt_path) types) ("Module " ^ s_type_path md.m_path ^ " does not define type " ^ tname)) p_type) in - chk_private t p_type; - t - in - let rebind t name p = - if not (name.[0] >= 'A' && name.[0] <= 'Z') then - error "Type aliases must start with an uppercase letter" p; - let _, _, f = ctx.g.do_build_instance ctx t p_type in - (* create a temp private typedef, does not register it in module *) - let mt = TTypeDecl { - t_path = (fst md.m_path @ ["_" ^ snd md.m_path],name); - t_module = ctx.m.curmod; - t_pos = p; - t_name_pos = p; - t_private = true; - t_doc = None; - t_meta = []; - t_params = (t_infos t).mt_params; - t_using = []; - t_type = f (List.map snd (t_infos t).mt_params); - } in - if ctx.is_display_file && DisplayPosition.display_position#enclosed_in p then - DisplayEmitter.display_module_type ctx mt p; - mt + let join l = String.concat Path.path_sep (List.rev ("import.hx" :: l)) in + let rec loop path pack = match path,pack with + | _,[] -> [join path] + | (p :: path),(_ :: pack) -> (join (p :: path)) :: (loop path pack) + | _ -> [] + in + let candidates = loop path_split (fst m.m_path) in + let make_import_module path r = + com.parser_cache#add path r; + (* We use the file path as module name to make it unique. This may or may not be a good idea... *) + let m_import = make_module ctx ([],path) path p in + m_import.m_extra.m_kind <- MImport; + add_module ctx m_import p; + m_import + in + List.fold_left (fun acc path -> + let decls = try + let r = com.parser_cache#find path in + let mimport = com.module_lut#find ([],path) in + if mimport.m_extra.m_kind <> MFake then add_dependency m mimport; + r + with Not_found -> + if Sys.file_exists path then begin + let _,r = match !TypeloadParse.parse_hook com path p with + | ParseSuccess(data,_,_) -> data + | ParseError(_,(msg,p),_) -> Parser.error msg p + in + List.iter (fun (d,p) -> match d with EImport _ | EUsing _ -> () | _ -> typing_error "Only import and using is allowed in import.hx files" p) r; + add_dependency m (make_import_module path r); + r + end else begin + let r = [] in + (* Add empty decls so we don't check the file system all the time. *) + (make_import_module path r).m_extra.m_kind <- MFake; + r + end in - let add_static_init t name s = - let name = (match name with None -> s | Some (n,_) -> n) in - match resolve_typedef t with - | TClassDecl c | TAbstractDecl {a_impl = Some c} -> - ignore(c.cl_build()); - ignore(PMap.find s c.cl_statics); - ctx.m.module_globals <- PMap.add name (TClassDecl c,s,p) ctx.m.module_globals - | TEnumDecl e -> - ignore(PMap.find s e.e_constrs); - ctx.m.module_globals <- PMap.add name (TEnumDecl e,s,p) ctx.m.module_globals + decls @ acc + ) decls candidates + + let init_type_params ctx decls = + (* here is an additional PASS 1 phase, which define the type parameters for all module types. + Constraints are handled lazily (no other type is loaded) because they might be recursive anyway *) + List.iter (fun d -> + match d with + | (TClassDecl c, (EClass d, p)) -> + c.cl_params <- type_type_params ctx TPHType c.cl_path (fun() -> c.cl_params) p d.d_params; + if Meta.has Meta.Generic c.cl_meta && c.cl_params <> [] then c.cl_kind <- KGeneric; + if Meta.has Meta.GenericBuild c.cl_meta then begin + if ctx.com.is_macro_context then typing_error "@:genericBuild cannot be used in macros" c.cl_pos; + c.cl_kind <- KGenericBuild d.d_data; + end; + if c.cl_path = (["haxe";"macro"],"MacroType") then c.cl_kind <- KMacroType; + | (TEnumDecl e, (EEnum d, p)) -> + e.e_params <- type_type_params ctx TPHType e.e_path (fun() -> e.e_params) p d.d_params; + | (TTypeDecl t, (ETypedef d, p)) -> + t.t_params <- type_type_params ctx TPHType t.t_path (fun() -> t.t_params) p d.d_params; + | (TAbstractDecl a, (EAbstract d, p)) -> + a.a_params <- type_type_params ctx TPHType a.a_path (fun() -> a.a_params) p d.d_params; + | _ -> + die "" __LOC__ + ) decls +end + +module TypeLevel = struct + let load_enum_field ctx e et is_flat index c = + let p = c.ec_pos in + let params = ref [] in + params := type_type_params ctx TPHEnumConstructor ([],fst c.ec_name) (fun() -> !params) c.ec_pos c.ec_params; + let params = !params in + let ctx = { ctx with type_params = params @ ctx.type_params } in + let rt = (match c.ec_type with + | None -> et + | Some (t,pt) -> + let t = load_complex_type ctx true (t,pt) in + (match follow t with + | TEnum (te,_) when te == e -> + () | _ -> - raise Not_found - in - (match mode with - | INormal | IAsName _ -> - let name = (match mode with IAsName n -> Some n | _ -> None) in - (match rest with - | [] -> - (match name with - | None -> - ctx.m.module_types <- List.filter no_private (List.map (fun t -> t,p) types) @ ctx.m.module_types; - Option.may (fun c -> - context_init#add (fun () -> - ignore(c.cl_build()); - List.iter (fun cf -> - if has_class_field_flag cf CfPublic then - ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals - ) c.cl_ordered_statics - ); - ) md.m_statics - | Some(newname,pname) -> - ctx.m.module_types <- (rebind (get_type tname) newname pname,p) :: ctx.m.module_types); - | [tsub,p2] -> - let pu = punion p1 p2 in - (try - let tsub = List.find (has_name tsub) types in - chk_private tsub pu; - ctx.m.module_types <- ((match name with None -> tsub | Some(n,pname) -> rebind tsub n pname),p) :: ctx.m.module_types - with Not_found -> - (* this might be a static property, wait later to check *) - let find_main_type_static () = - let tmain = get_type tname in - try - add_static_init tmain name tsub - with Not_found -> - (* TODO: mention module-level declarations in the error message? *) - display_error ctx (s_type_path (t_infos tmain).mt_path ^ " has no field or subtype " ^ tsub) p - in - context_init#add (fun() -> - match md.m_statics with - | Some c -> - (try - ignore(c.cl_build()); - let rec loop fl = - match fl with - | [] -> raise Not_found - | cf :: rest -> - if cf.cf_name = tsub then - if not (has_class_field_flag cf CfPublic) then - error_private p - else - let imported_name = match name with None -> tsub | Some (n,pname) -> n in - ctx.m.module_globals <- PMap.add imported_name (TClassDecl c,tsub,p) ctx.m.module_globals; - else - loop rest - in - loop c.cl_ordered_statics - with Not_found -> - find_main_type_static ()) - | None -> - find_main_type_static () - ) - ) - | (tsub,p2) :: (fname,p3) :: rest -> - (match rest with - | [] -> () - | (n,p) :: _ -> error ("Unexpected " ^ n) p); - let tsub = get_type tsub in - context_init#add (fun() -> - try - add_static_init tsub name fname - with Not_found -> - display_error ctx (s_type_path (t_infos tsub).mt_path ^ " has no field " ^ fname) (punion p p3) - ); - ) - | IAll -> - let t = (match rest with - | [] -> get_type tname - | [tsub,_] -> get_type tsub - | _ :: (n,p) :: _ -> error ("Unexpected " ^ n) p - ) in - context_init#add (fun() -> - match resolve_typedef t with - | TClassDecl c - | TAbstractDecl {a_impl = Some c} -> - ignore(c.cl_build()); - PMap.iter (fun _ cf -> if not (has_meta Meta.NoImportGlobal cf.cf_meta) then ctx.m.module_globals <- PMap.add cf.cf_name (TClassDecl c,cf.cf_name,p) ctx.m.module_globals) c.cl_statics - | TEnumDecl e -> - PMap.iter (fun _ c -> if not (has_meta Meta.NoImportGlobal c.ef_meta) then ctx.m.module_globals <- PMap.add c.ef_name (TEnumDecl e,c.ef_name,p) ctx.m.module_globals) e.e_constrs - | _ -> - error "No statics to import from this type" p - ) - )) - in - match decl with - | EImport (path,mode) -> - begin try - init_import path mode; - commit_import path mode p; - with Error(err,p) -> - display_error ctx (Error.error_msg err) p - end - | EUsing path -> - check_path_display path p; - let types,filter_classes = handle_using ctx path p in - (* do the import first *) - ctx.m.module_types <- (List.map (fun t -> t,p) types) @ ctx.m.module_types; - context_init#add (fun() -> ctx.m.module_using <- filter_classes types @ ctx.m.module_using) - | EClass d -> - let c = (match get_type (fst d.d_name) with TClassDecl c -> c | _ -> die "" __LOC__) in + typing_error "Explicit enum type must be of the same enum type" pt); + t + ) in + let t = (match c.ec_args with + | [] -> rt + | l -> + is_flat := false; + let pnames = ref PMap.empty in + TFun (List.map (fun (s,opt,(t,tp)) -> + (match t with CTPath({tpackage=[];tname="Void"}) -> typing_error "Arguments of type Void are not allowed in enum constructors" tp | _ -> ()); + if PMap.mem s (!pnames) then typing_error ("Duplicate argument `" ^ s ^ "` in enum constructor " ^ fst c.ec_name) p; + pnames := PMap.add s () (!pnames); + s, opt, load_type_hint ~opt ctx p (Some (t,tp)) + ) l, rt) + ) in + let f = { + ef_name = fst c.ec_name; + ef_type = t; + ef_pos = p; + ef_name_pos = snd c.ec_name; + ef_doc = c.ec_doc; + ef_index = !index; + ef_params = params; + ef_meta = c.ec_meta; + } in + DeprecationCheck.check_is ctx.com f.ef_name f.ef_meta f.ef_name_pos; + let cf = { + (mk_field f.ef_name f.ef_type p f.ef_name_pos) with + cf_kind = (match follow f.ef_type with + | TFun _ -> Method MethNormal + | _ -> Var { v_read = AccNormal; v_write = AccNo } + ); + cf_doc = f.ef_doc; + cf_params = f.ef_params; + } in + if ctx.is_display_file && DisplayPosition.display_position#enclosed_in f.ef_name_pos then + DisplayEmitter.display_enum_field ctx e f p; + f,cf + + let init_class ctx context_init c d p = if ctx.is_display_file && DisplayPosition.display_position#enclosed_in (pos d.d_name) then DisplayEmitter.display_module_type ctx (match c.cl_kind with KAbstractImpl a -> TAbstractDecl a | _ -> TClassDecl c) (pos d.d_name); TypeloadCheck.check_global_metadata ctx c.cl_meta (fun m -> c.cl_meta <- m :: c.cl_meta) c.cl_module.m_path c.cl_path None; @@ -658,12 +403,11 @@ let init_module_type ctx context_init (decl,p) = List.iter (fun (m,_,p) -> if m = Meta.Final then begin add_class_flag c CFinal; - (* if p <> null_pos && not (Define.is_haxe3_compat ctx.com.defines) then - ctx.com.warning "`@:final class` is deprecated in favor of `final class`" p; *) end ) d.d_meta; let prev_build_count = ref (!build_count - 1) in let build() = + c.cl_build <- (fun()-> Building [c]); let fl = TypeloadCheck.Inheritance.set_heritance ctx c herits p in let rec build() = c.cl_build <- (fun()-> Building [c]); @@ -672,7 +416,7 @@ let init_module_type ctx context_init (decl,p) = TypeloadFields.init_class ctx c p context_init d.d_flags d.d_data; c.cl_build <- (fun()-> Built); incr build_count; - List.iter (fun (_,t) -> ignore(follow t)) c.cl_params; + List.iter (fun tp -> ignore(follow tp.ttp_type)) c.cl_params; Built; with TypeloadCheck.Build_canceled state -> c.cl_build <- make_pass ctx build; @@ -682,7 +426,7 @@ let init_module_type ctx context_init (decl,p) = (match state with | Built -> die "" __LOC__ | Building cl -> - if !build_count = !prev_build_count then error ("Loop in class building prevent compiler termination (" ^ String.concat "," (List.map (fun c -> s_type_path c.cl_path) cl) ^ ")") c.cl_pos; + if !build_count = !prev_build_count then typing_error ("Loop in class building prevent compiler termination (" ^ String.concat "," (List.map (fun c -> s_type_path c.cl_path) cl) ^ ")") c.cl_pos; prev_build_count := !build_count; rebuild(); Building (c :: cl) @@ -717,9 +461,9 @@ let init_module_type ctx context_init (decl,p) = match c.cl_constructor with | Some f -> run_field f | _ -> () - ); - | EEnum d -> - let e = (match get_type (fst d.d_name) with TEnumDecl e -> e | _ -> die "" __LOC__) in + ) + + let init_enum ctx context_init e d p = if ctx.is_display_file && DisplayPosition.display_position#enclosed_in (pos d.d_name) then DisplayEmitter.display_module_type ctx (TEnumDecl e) (pos d.d_name); let ctx = { ctx with type_params = e.e_params } in @@ -728,7 +472,7 @@ let init_module_type ctx context_init (decl,p) = (match h with | None -> () | Some (h,hcl) -> - Hashtbl.iter (fun _ _ -> error "Field type patch not supported for enums" e.e_pos) h; + Hashtbl.iter (fun _ _ -> typing_error "Field type patch not supported for enums" e.e_pos) h; e.e_meta <- e.e_meta @ hcl.tp_meta); let constructs = ref d.d_data in let get_constructs() = @@ -752,10 +496,10 @@ let init_module_type ctx context_init (decl,p) = let args, params, t = (match f.cff_kind with | FVar (t,None) -> [], [], t | FFun { f_params = pl; f_type = t; f_expr = (None|Some (EBlock [],_)); f_args = al } -> - let al = List.map (fun ((n,_),o,_,t,_) -> match t with None -> error "Missing function parameter type" f.cff_pos | Some t -> n,o,t) al in + let al = List.map (fun ((n,_),o,_,t,_) -> match t with None -> typing_error "Missing function parameter type" f.cff_pos | Some t -> n,o,t) al in al, pl, t | _ -> - error "Invalid enum constructor in @:build result" p + typing_error "Invalid enum constructor in @:build result" p ) in { ec_name = f.cff_name; @@ -767,15 +511,15 @@ let init_module_type ctx context_init (decl,p) = ec_type = t; } ) fields - | _ -> error "Enum build macro must return a single variable with anonymous object fields" p + | _ -> typing_error "Enum build macro must return a single variable with anonymous object fields" p ); - let et = TEnum (e,List.map snd e.e_params) in + let et = TEnum (e,extract_param_types e.e_params) in let names = ref [] in let index = ref 0 in let is_flat = ref true in let fields = ref PMap.empty in List.iter (fun c -> - if PMap.mem (fst c.ec_name) e.e_constrs then error ("Duplicate constructor " ^ fst c.ec_name) (pos c.ec_name); + if PMap.mem (fst c.ec_name) e.e_constrs then typing_error ("Duplicate constructor " ^ fst c.ec_name) (pos c.ec_name); let f,cf = load_enum_field ctx e et is_flat index c in e.e_constrs <- PMap.add f.ef_name f e.e_constrs; fields := PMap.add cf.cf_name cf !fields; @@ -799,9 +543,9 @@ let init_module_type ctx context_init (decl,p) = let metas = StrictMeta.check_strict_meta ctx ef.ef_meta in if metas <> [] then ef.ef_meta <- metas @ ef.ef_meta ) e.e_constrs - ); - | ETypedef d -> - let t = (match get_type (fst d.d_name) with TTypeDecl t -> t | _ -> die "" __LOC__) in + ) + + let init_typedef ctx context_init t d p = if ctx.is_display_file && DisplayPosition.display_position#enclosed_in (pos d.d_name) then DisplayEmitter.display_module_type ctx (TTypeDecl t) (pos d.d_name); TypeloadCheck.check_global_metadata ctx t.t_meta (fun m -> t.t_meta <- m :: t.t_meta) t.t_module.m_path t.t_path None; @@ -811,14 +555,14 @@ let init_module_type ctx context_init (decl,p) = | CTExtend _ -> tt | CTPath { tpackage = ["haxe";"macro"]; tname = "MacroType" } -> (* we need to follow MacroType immediately since it might define other module types that we will load afterwards *) - if t.t_type == follow tt then error "Recursive typedef is not allowed" p; + if t.t_type == follow tt then typing_error "Recursive typedef is not allowed" p; tt | _ -> if (Meta.has Meta.Eager d.d_meta) then follow tt else begin let rec check_rec tt = - if tt == t.t_type then error "Recursive typedef is not allowed" p; + if tt == t.t_type then typing_error "Recursive typedef is not allowed" p; match tt with | TMono r -> (match r.tm_type with @@ -827,8 +571,8 @@ let init_module_type ctx context_init (decl,p) = | TLazy f -> check_rec (lazy_type f); | TType (td,tl) -> - if td == t then error "Recursive typedef is not allowed" p; - check_rec (apply_params td.t_params tl td.t_type) + if td == t then typing_error "Recursive typedef is not allowed" p; + check_rec (apply_typedef td tl) | _ -> () in @@ -851,9 +595,9 @@ let init_module_type ctx context_init (decl,p) = delay ctx PTypeField (fun () -> let metas = StrictMeta.check_strict_meta ctx t.t_meta in if metas <> [] then t.t_meta <- metas @ t.t_meta; - ); - | EAbstract d -> - let a = (match get_type (fst d.d_name) with TAbstractDecl a -> a | _ -> die "" __LOC__) in + ) + + let init_abstract ctx context_init a d p = if ctx.is_display_file && DisplayPosition.display_position#enclosed_in (pos d.d_name) then DisplayEmitter.display_module_type ctx (TAbstractDecl a) (pos d.d_name); TypeloadCheck.check_global_metadata ctx a.a_meta (fun m -> a.a_meta <- m :: a.a_meta) a.a_module.m_path a.a_path None; @@ -866,15 +610,15 @@ let init_module_type ctx context_init (decl,p) = if !is_type then begin let r = exc_protect ctx (fun r -> r := lazy_processing (fun() -> t); - (try (if from then Type.unify t a.a_this else Type.unify a.a_this t) with Unify_error _ -> error "You can only declare from/to with compatible types" pos); + (try (if from then Type.unify t a.a_this else Type.unify a.a_this t) with Unify_error _ -> typing_error "You can only declare from/to with compatible types" pos); t ) "constraint" in TLazy r end else - error "Missing underlying type declaration or @:coreType declaration" p; + typing_error "Missing underlying type declaration or @:coreType declaration" p; end else begin if Meta.has Meta.Callable a.a_meta then - error "@:coreType abstracts cannot be @:callable" p; + typing_error "@:coreType abstracts cannot be @:callable" p; t end in t @@ -883,15 +627,15 @@ let init_module_type ctx context_init (decl,p) = | AbFrom t -> a.a_from <- (load_type t true) :: a.a_from | AbTo t -> a.a_to <- (load_type t false) :: a.a_to | AbOver t -> - if a.a_impl = None then error "Abstracts with underlying type must have an implementation" a.a_pos; - if Meta.has Meta.CoreType a.a_meta then error "@:coreType abstracts cannot have an underlying type" p; + if a.a_impl = None then typing_error "Abstracts with underlying type must have an implementation" a.a_pos; + if Meta.has Meta.CoreType a.a_meta then typing_error "@:coreType abstracts cannot have an underlying type" p; let at = load_complex_type ctx true t in delay ctx PForce (fun () -> let rec loop stack t = match follow t with | TAbstract(a,_) when not (Meta.has Meta.CoreType a.a_meta) -> if List.memq a stack then - error "Abstract underlying type cannot be recursive" a.a_pos + typing_error "Abstract underlying type cannot be recursive" a.a_pos else loop (a :: stack) a.a_this | _ -> () @@ -908,84 +652,86 @@ let init_module_type ctx context_init (decl,p) = a.a_to <- List.rev a.a_to; if not !is_type then begin if Meta.has Meta.CoreType a.a_meta then - a.a_this <- TAbstract(a,List.map snd a.a_params) + a.a_this <- TAbstract(a,extract_param_types a.a_params) else - error "Abstract is missing underlying type declaration" a.a_pos + typing_error "Abstract is missing underlying type declaration" a.a_pos end; if Meta.has Meta.InheritDoc a.a_meta then - delay ctx PConnectField (fun() -> InheritDoc.build_abstract_doc ctx a); - | EStatic _ -> - (* nothing to do here as module fields are collected into a special EClass *) - () + delay ctx PConnectField (fun() -> InheritDoc.build_abstract_doc ctx a) -let module_pass_2 ctx m decls tdecls p = - (* here is an additional PASS 1 phase, which define the type parameters for all module types. - Constraints are handled lazily (no other type is loaded) because they might be recursive anyway *) - List.iter (fun d -> - match d with - | (TClassDecl c, (EClass d, p)) -> - c.cl_params <- type_type_params ctx c.cl_path (fun() -> c.cl_params) p d.d_params; - if Meta.has Meta.Generic c.cl_meta && c.cl_params <> [] then c.cl_kind <- KGeneric; - if Meta.has Meta.GenericBuild c.cl_meta then begin - if ctx.in_macro then error "@:genericBuild cannot be used in macros" c.cl_pos; - c.cl_kind <- KGenericBuild d.d_data; - end; - if c.cl_path = (["haxe";"macro"],"MacroType") then c.cl_kind <- KMacroType; - | (TEnumDecl e, (EEnum d, p)) -> - e.e_params <- type_type_params ctx e.e_path (fun() -> e.e_params) p d.d_params; - | (TTypeDecl t, (ETypedef d, p)) -> - t.t_params <- type_type_params ctx t.t_path (fun() -> t.t_params) p d.d_params; - | (TAbstractDecl a, (EAbstract d, p)) -> - a.a_params <- type_type_params ctx a.a_path (fun() -> a.a_params) p d.d_params; - | _ -> - die "" __LOC__ - ) decls; - (* setup module types *) - let context_init = new TypeloadFields.context_init in - List.iter (init_module_type ctx context_init) tdecls; - (* Make sure that we actually init the context at some point (issue #9012) *) - delay ctx PConnectField (fun () -> context_init#run) + (* + In this pass, we can access load and access other modules types, but we cannot follow them or access their structure + since they have not been setup. We also build a context_init list that will be evaluated the first time we evaluate + an expression into the context + *) + let init_module_type ctx context_init (decl,p) = + let get_type name = + try List.find (fun t -> snd (t_infos t).mt_path = name) ctx.m.curmod.m_types with Not_found -> die "" __LOC__ + in + let check_path_display path p = + if DisplayPosition.display_position#is_in_file (ctx.com.file_keys#get p.pfile) then DisplayPath.handle_path_display ctx path p + in + match decl with + | EImport (path,mode) -> + begin try + check_path_display path p; + ImportHandling.init_import ctx context_init path mode p; + ImportHandling.commit_import ctx path mode p; + with Error(err,p,depth) -> + located_display_error ~depth ctx.com (Error.error_msg p err) + end + | EUsing path -> + check_path_display path p; + ImportHandling.init_using ctx context_init path p + | EClass d -> + let c = (match get_type (fst d.d_name) with TClassDecl c -> c | _ -> die "" __LOC__) in + init_class ctx context_init c d p + | EEnum d -> + let e = (match get_type (fst d.d_name) with TEnumDecl e -> e | _ -> die "" __LOC__) in + init_enum ctx context_init e d p + | ETypedef d -> + let t = (match get_type (fst d.d_name) with TTypeDecl t -> t | _ -> die "" __LOC__) in + init_typedef ctx context_init t d p + | EAbstract d -> + let a = (match get_type (fst d.d_name) with TAbstractDecl a -> a | _ -> die "" __LOC__) in + init_abstract ctx context_init a d p + | EStatic _ -> + (* nothing to do here as module fields are collected into a special EClass *) + () +end -(* - Creates a module context for [m] and types [tdecls] using it. -*) -let type_types_into_module ctx m tdecls p = - let decls, tdecls = module_pass_1 ctx m tdecls p in - let types = List.map fst decls in - List.iter (TypeloadCheck.check_module_types ctx m p) types; - m.m_types <- m.m_types @ types; - (* define the per-module context for the next pass *) - let ctx = { +let make_curmod ctx m = { + curmod = m; + module_imports = List.map (fun t -> t,null_pos) ctx.g.std.m_types; + module_using = []; + module_globals = PMap.empty; + wildcard_packages = []; + import_statements = []; +} + +let create_typer_context_for_module ctx m = { com = ctx.com; g = ctx.g; - t = ctx.t; - m = { - curmod = m; - module_types = List.map (fun t -> t,null_pos) ctx.g.std.m_types; - module_using = []; - module_globals = PMap.empty; - wildcard_packages = []; - module_imports = []; - }; + t = ctx.com.basic; + m = make_curmod ctx m; is_display_file = (ctx.com.display.dms_kind <> DMNone && DisplayPosition.display_position#is_in_file (Path.UniqueKey.lazy_key m.m_extra.m_file)); bypass_accessor = 0; meta = []; - this_stack = []; with_type_stack = []; call_argument_stack = []; pass = PBuildModule; get_build_infos = (fun() -> None); - on_error = (fun ctx msg p -> ctx.com.error msg p); - macro_depth = ctx.macro_depth; + macro_depth = 0; curclass = null_class; + allow_inline = true; + allow_transform = true; curfield = null_field; - tthis = ctx.tthis; - ret = ctx.ret; + tthis = mk_mono(); + ret = mk_mono(); locals = PMap.empty; type_params = []; curfun = FunStatic; untyped = false; - in_macro = ctx.in_macro; in_display = false; in_function = false; in_loop = false; @@ -998,75 +744,40 @@ let type_types_into_module ctx m tdecls p = }; vthis = None; memory_marker = Typecore.memory_marker; - } in + } + +(* + Creates a module context for [m] and types [tdecls] using it. +*) +let type_types_into_module ctx m tdecls p = + let ctx = create_typer_context_for_module ctx m in + let decls,tdecls = ModuleLevel.create_module_types ctx m tdecls p in + let types = List.map fst decls in + List.iter (TypeloadCheck.check_module_types ctx m p) types; + m.m_types <- m.m_types @ types; + (* define the per-module context for the next pass *) if ctx.g.std != null_module then begin add_dependency m ctx.g.std; (* this will ensure both String and (indirectly) Array which are basic types which might be referenced *) ignore(load_core_type ctx "String"); end; - module_pass_2 ctx m decls tdecls p; + ModuleLevel.init_type_params ctx decls; + (* setup module types *) + let context_init = new TypeloadFields.context_init in + List.iter (TypeLevel.init_module_type ctx context_init) tdecls; + (* Make sure that we actually init the context at some point (issue #9012) *) + delay ctx PConnectField (fun () -> context_init#run); ctx -let handle_import_hx ctx m decls p = - let path_split = match List.rev (Path.get_path_parts (Path.UniqueKey.lazy_path m.m_extra.m_file)) with - | [] -> [] - | _ :: l -> l - in - let join l = String.concat Path.path_sep (List.rev ("import.hx" :: l)) in - let rec loop path pack = match path,pack with - | _,[] -> [join path] - | (p :: path),(_ :: pack) -> (join (p :: path)) :: (loop path pack) - | _ -> [] - in - let candidates = loop path_split (fst m.m_path) in - let make_import_module path r = - Hashtbl.replace ctx.com.parser_cache path r; - (* We use the file path as module name to make it unique. This may or may not be a good idea... *) - let m_import = make_module ctx ([],path) path p in - m_import.m_extra.m_kind <- MImport; - add_module ctx m_import p; - m_import - in - List.fold_left (fun acc path -> - let decls = try - let r = Hashtbl.find ctx.com.parser_cache path in - let mimport = Hashtbl.find ctx.g.modules ([],path) in - if mimport.m_extra.m_kind <> MFake then add_dependency m mimport; - r - with Not_found -> - if Sys.file_exists path then begin - let _,r = match !TypeloadParse.parse_hook ctx.com path p with - | ParseSuccess(data,_,_) -> data - | ParseError(_,(msg,p),_) -> Parser.error msg p - in - List.iter (fun (d,p) -> match d with EImport _ | EUsing _ -> () | _ -> error "Only import and using is allowed in import.hx files" p) r; - add_dependency m (make_import_module path r); - r - end else begin - let r = [] in - (* Add empty decls so we don't check the file system all the time. *) - (make_import_module path r).m_extra.m_kind <- MFake; - r - end - in - decls @ acc - ) decls candidates - (* Creates a new module and types [tdecls] into it. *) let type_module ctx mpath file ?(dont_check_path=false) ?(is_extern=false) tdecls p = - let m = make_module ctx mpath file p in - Hashtbl.add ctx.g.modules m.m_path m; - let tdecls = handle_import_hx ctx m tdecls p in + let m = ModuleLevel.make_module ctx mpath file p in + ctx.com.module_lut#add m.m_path m; + let tdecls = ModuleLevel.handle_import_hx ctx m tdecls p in let ctx = type_types_into_module ctx m tdecls p in if is_extern then m.m_extra.m_kind <- MExtern else if not dont_check_path then Typecore.check_module_path ctx m.m_path p; - begin if ctx.is_display_file then match ctx.com.display.dms_kind with - | DMResolve s -> - DisplayPath.resolve_position_by_path ctx (mk_type_path ([],s)) p - | _ -> - () - end; m (* let type_module ctx mpath file ?(is_extern=false) tdecls p = @@ -1075,21 +786,31 @@ let type_module ctx mpath file ?(dont_check_path=false) ?(is_extern=false) tdecl let type_module_hook = ref (fun _ _ _ -> None) -let load_module ctx m p = - let m2 = (try - Hashtbl.find ctx.g.modules m - with - Not_found -> - match !type_module_hook ctx m p with - | Some m -> m - | None -> +let load_module' ctx g m p = + try + (* Check current context *) + ctx.com.module_lut#find m + with Not_found -> + (* Check cache *) + match !type_module_hook ctx m p with + | Some m -> + m + | None -> + let raise_not_found () = + raise (Error (Module_not_found m,p,0)) + in + if ctx.com.module_nonexistent_lut#mem m then raise_not_found(); + if ctx.g.load_only_cached_modules then raise_not_found(); let is_extern = ref false in - let file, decls = (try + let file, decls = try + (* Try parsing *) TypeloadParse.parse_module ctx m p with Not_found -> + (* Nothing to parse, try loading extern type *) let rec loop = function | [] -> - raise (Error (Module_not_found m,p)) + ctx.com.module_nonexistent_lut#add m true; + raise_not_found() | (file,load) :: l -> match load m p with | None -> loop l @@ -1097,14 +818,16 @@ let load_module ctx m p = in is_extern := true; loop ctx.com.load_extern_type - ) in + in let is_extern = !is_extern in try type_module ctx m file ~is_extern decls p with Forbid_package (inf,pl,pf) when p <> null_pos -> raise (Forbid_package (inf,p::pl,pf)) - ) in - add_dependency ctx.m.curmod m2; + +let load_module ctx m p = + let m2 = load_module' ctx ctx.g m p in + add_dependency ~skip_postprocess:true ctx.m.curmod m2; if ctx.pass = PTypeField then flush_pass ctx PConnectField "load_module"; m2 diff --git a/src/typing/typeloadParse.ml b/src/typing/typeloadParse.ml index a181a1d3..fcc19238 100644 --- a/src/typing/typeloadParse.ml +++ b/src/typing/typeloadParse.ml @@ -22,7 +22,6 @@ open Globals open Ast open Parser -open DisplayTypes.DiagnosticsSeverity open DisplayTypes.DisplayMode open Common open Type @@ -40,7 +39,7 @@ let parse_file_from_lexbuf com file p lexbuf = with | Sedlexing.MalFormed -> t(); - error "Malformed file. Source files must be encoded with UTF-8." {pfile = file; pmin = 0; pmax = 0} + typing_error "Malformed file. Source files must be encoded with UTF-8." {pfile = file; pmin = 0; pmax = 0} | e -> t(); raise e @@ -77,7 +76,7 @@ let parse_file com file p = in parse_file_from_string com file p s else - let ch = try open_in_bin file with _ -> error ("Could not open " ^ file) p in + let ch = try open_in_bin file with _ -> typing_error ("Could not open " ^ file) p in Std.finally (fun() -> close_in ch) (parse_file_from_lexbuf com file p) (Sedlexing.Utf8.from_channel ch) let parse_hook = ref parse_file @@ -139,10 +138,10 @@ let resolve_module_file com m remap p = let resolve_module_file com m remap p = try - Hashtbl.find com.module_to_file m + com.module_to_file#find m with Not_found -> let file = resolve_module_file com m remap p in - Hashtbl.add com.module_to_file m file; + com.module_to_file#add m file; file (* let resolve_module_file com m remap p = @@ -251,7 +250,7 @@ module PdiHandler = struct raise DisplayInMacroBlock; begin match com.display.dms_kind with | DMHover -> - raise (DisplayException.DisplayException(DisplayHover None)) + raise (DisplayException.DisplayException(DisplayNoResult)) | _ -> () end; @@ -270,9 +269,11 @@ let handle_parser_result com p result = let handle_parser_error msg p = let msg = Parser.error_msg msg in match com.display.dms_error_policy with - | EPShow -> error msg p - | EPIgnore -> () - | EPCollect -> add_diagnostics_message com msg p DKParserError Error + | EPShow -> + if is_diagnostics com then add_diagnostics_message com (located msg p) DKParserError Error + else typing_error msg p + | EPIgnore -> + com.has_error <- true in match result with | ParseSuccess(data,is_display_file,pdi) -> @@ -302,9 +303,9 @@ let parse_module ctx m p = if pack <> !remap then begin let spack m = if m = [] then "`package;`" else "`package " ^ (String.concat "." m) ^ ";`" in if p == null_pos then - display_error ctx ("Invalid commandline class : " ^ s_type_path m ^ " should be " ^ s_type_path (pack,snd m)) p + display_error ctx.com ("Invalid commandline class : " ^ s_type_path m ^ " should be " ^ s_type_path (pack,snd m)) p else - display_error ctx (spack pack ^ " in " ^ file ^ " should be " ^ spack (fst m)) {p with pmax = p.pmin} + display_error ctx.com (spack pack ^ " in " ^ file ^ " should be " ^ spack (fst m)) {p with pmax = p.pmin} end; file, if !remap <> fst m then (* build typedefs to redirect to real package *) diff --git a/src/typing/typer.ml b/src/typing/typer.ml index 6ea226fb..7deb8815 100644 --- a/src/typing/typer.ml +++ b/src/typing/typer.ml @@ -36,6 +36,12 @@ open Operators (* ---------------------------------------------------------------------- *) (* TOOLS *) +let mono_or_dynamic ctx with_type p = match with_type with + | WithType.NoValue -> + t_dynamic + | Value _ | WithType _ -> + spawn_monomorph ctx p + let get_iterator_param t = match follow t with | TAnon a -> @@ -75,7 +81,7 @@ let maybe_type_against_enum ctx f with_type iscall p = ) c.cl_ordered_statics in false,a.a_path,fields,TAbstractDecl a | TAbstract (a,pl) when not (Meta.has Meta.CoreType a.a_meta) -> - begin match get_abstract_froms a pl with + begin match get_abstract_froms ctx a pl with | [t2] -> if (List.exists (shallow_eq t) stack) then raise Exit; loop (t :: stack) t2 @@ -91,7 +97,7 @@ let maybe_type_against_enum ctx f with_type iscall p = let e = try f() with - | Error (Unknown_ident n,_) -> + | Error (Unknown_ident n,_,_) -> restore(); raise_or_display_message ctx (StringError.string_error n fields ("Identifier '" ^ n ^ "' is not part of " ^ s_type_path path)) p; AKExpr (mk (TConst TNull) (mk_mono()) p) @@ -118,12 +124,6 @@ let maybe_type_against_enum ctx f with_type iscall p = with Exit -> f() -let check_error ctx err p = match err with - | Module_not_found ([],name) when Diagnostics.is_diagnostics_run ctx.com p -> - DisplayToplevel.handle_unresolved_identifier ctx name p true - | _ -> - display_error ctx (error_msg err) p - (* ---------------------------------------------------------------------- *) (* PASS 3 : type expression & check structure *) @@ -243,13 +243,13 @@ let rec unify_min_raise ctx (el:texpr list) : t = | UnifyMinOk t -> t | UnifyMinError(l,index) -> - raise_error (Unify l) (List.nth el index).epos + raise_typing_error (Unify l) (List.nth el index).epos end let unify_min ctx el = try unify_min_raise ctx el - with Error (Unify l,p) -> - if not ctx.untyped then display_error ctx (error_msg (Unify l)) p; + with Error (Unify l,p,depth) -> + if not ctx.untyped then located_display_error ~depth ctx.com (error_msg p (Unify l)); (List.hd el).etype let unify_min_for_type_source ctx el src = @@ -263,40 +263,89 @@ let rec type_ident_raise ctx i p mode with_type = let is_set = match mode with MSet _ -> true | _ -> false in match i with | "true" -> + let acc = AKExpr (mk (TConst (TBool true)) ctx.t.tbool p) in if mode = MGet then - AKExpr (mk (TConst (TBool true)) ctx.t.tbool p) + acc else - AKNo i + AKNo(acc,p) | "false" -> + let acc = AKExpr (mk (TConst (TBool false)) ctx.t.tbool p) in if mode = MGet then - AKExpr (mk (TConst (TBool false)) ctx.t.tbool p) + acc else - AKNo i + AKNo(acc,p) | "this" -> - if is_set then add_class_field_flag ctx.curfield CfModifiesThis; - (match mode, ctx.curclass.cl_kind with - | MSet _, KAbstractImpl _ -> - if not (assign_to_this_is_allowed ctx) then - error "Abstract 'this' value can only be modified inside an inline function" p; - AKExpr (get_this ctx p) - | (MCall _, KAbstractImpl _) | (MGet, _)-> AKExpr(get_this ctx p) - | _ -> AKNo i) + let acc = AKExpr(get_this ctx p) in + begin match mode with + | MSet _ -> + add_class_field_flag ctx.curfield CfModifiesThis; + begin match ctx.curclass.cl_kind with + | KAbstractImpl _ -> + if not (assign_to_this_is_allowed ctx) then + typing_error "Abstract 'this' value can only be modified inside an inline function" p; + acc + | _ -> + AKNo(acc,p) + end + | MCall _ -> + begin match ctx.curclass.cl_kind with + | KAbstractImpl _ -> + acc + | _ -> + AKNo(acc,p) + end + | MGet -> + acc + end; + | "abstract" -> + begin match mode, ctx.curclass.cl_kind with + | MSet _, KAbstractImpl ab -> typing_error "Property 'abstract' is read-only" p; + | (MGet, KAbstractImpl ab) + | (MCall _, KAbstractImpl ab) -> + let tl = extract_param_types ab.a_params in + let e = get_this ctx p in + let e = {e with etype = TAbstract (ab,tl)} in + AKExpr e + | _ -> + typing_error "Property 'abstract' is reserved and only available in abstracts" p + end | "super" -> let t = (match ctx.curclass.cl_super with - | None -> error "Current class does not have a superclass" p + | None -> typing_error "Current class does not have a superclass" p | Some (c,params) -> TInst(c,params) ) in (match ctx.curfun with | FunMember | FunConstructor -> () - | FunMemberAbstract -> error "Cannot access super inside an abstract function" p - | FunStatic -> error "Cannot access super inside a static function" p; - | FunMemberClassLocal | FunMemberAbstractLocal -> error "Cannot access super inside a local function" p); + | FunMemberAbstract -> typing_error "Cannot access super inside an abstract function" p + | FunStatic -> typing_error "Cannot access super inside a static function" p; + | FunMemberClassLocal | FunMemberAbstractLocal -> typing_error "Cannot access super inside a local function" p); AKExpr (mk (TConst TSuper) t p) | "null" -> - if mode = MGet then - AKExpr (null (spawn_monomorph ctx p) p) - else - AKNo i + let acc = + (* Hack for #10787 *) + if ctx.com.platform = Cs then + AKExpr (null (spawn_monomorph ctx p) p) + else begin + let tnull () = ctx.t.tnull (spawn_monomorph ctx p) in + let t = match with_type with + | WithType.WithType(t,_) -> + begin match follow t with + | TMono r -> + (* If our expected type is a monomorph, bind it to Null. *) + Monomorph.do_bind r (tnull()) + | _ -> + (* Otherwise there's no need to create a monomorph, we can just type the null literal + the way we expect it. *) + () + end; + t + | _ -> + tnull() + in + AKExpr (null t p) + end + in + if mode = MGet then acc else AKNo(acc,p) | _ -> try let v = PMap.find i ctx.locals in @@ -307,8 +356,8 @@ let rec type_ident_raise ctx i p mode with_type = (match e with | Some ({ eexpr = TFunction f } as e) when ctx.com.display.dms_inline -> begin match mode with - | MSet _ -> error "Cannot set inline closure" p - | MGet -> error "Cannot create closure on inline closure" p + | MSet _ -> typing_error "Cannot set inline closure" p + | MGet -> typing_error "Cannot create closure on inline closure" p | MCall _ -> (* create a fake class with a fake field to emulate inlining *) let c = mk_class ctx.m.curmod (["local"],v.v_name) e.epos null_pos in @@ -325,7 +374,7 @@ let rec type_ident_raise ctx i p mode with_type = with Not_found -> try (* member variable lookup *) if ctx.curfun = FunStatic then raise Not_found; - let c , t , f = class_field ctx ctx.curclass (List.map snd ctx.curclass.cl_params) i p in + let c , t , f = class_field ctx ctx.curclass (extract_param_types ctx.curclass.cl_params) i p in field_access ctx mode f (match c with None -> FHAnon | Some (c,tl) -> FHInstance (c,tl)) (get_this ctx p) p with Not_found -> try (* static variable lookup *) @@ -333,10 +382,10 @@ let rec type_ident_raise ctx i p mode with_type = let is_impl = has_class_field_flag f CfImpl in let is_enum = has_class_field_flag f CfEnum in if is_impl && not (has_class_field_flag ctx.curfield CfImpl) && not is_enum then - error (Printf.sprintf "Cannot access non-static field %s from static method" f.cf_name) p; + typing_error (Printf.sprintf "Cannot access non-static field %s from static method" f.cf_name) p; let e,fa = match ctx.curclass.cl_kind with | KAbstractImpl a when is_impl && not is_enum -> - let tl = List.map snd a.a_params in + let tl = extract_param_types a.a_params in let e = get_this ctx p in let e = {e with etype = TAbstract(a,tl)} in e,FHAbstract(a,tl,ctx.curclass) @@ -355,10 +404,12 @@ let rec type_ident_raise ctx i p mode with_type = field_access ctx mode f (FHStatic c) e p ) with Not_found -> try - let wrap e = if is_set then - AKNo i + let wrap e = + let acc = AKExpr e in + if is_set then + AKNo(acc,p) else - AKExpr e + acc in (* lookup imported enums *) let rec loop l = @@ -377,7 +428,7 @@ let rec type_ident_raise ctx i p mode with_type = | Var {v_read = AccInline} -> true | _ -> false in - let fa = FieldAccess.create et cf (FHAbstract(a,List.map snd a.a_params,c)) inline p in + let fa = FieldAccess.create et cf (FHAbstract(a,extract_param_types a.a_params,c)) inline p in ImportHandling.mark_import_position ctx pt; AKField fa end @@ -400,7 +451,7 @@ let rec type_ident_raise ctx i p mode with_type = with Not_found -> loop l in - (try loop (List.rev_map (fun t -> t,null_pos) ctx.m.curmod.m_types) with Not_found -> loop ctx.m.module_types) + (try loop (List.rev_map (fun t -> t,null_pos) ctx.m.curmod.m_types) with Not_found -> loop ctx.m.module_imports) with Not_found -> (* lookup imported globals *) let t, name, pi = PMap.find i ctx.m.module_globals in @@ -414,17 +465,17 @@ and type_ident ctx i p mode with_type = with Not_found -> try (* lookup type *) if is_lower_ident i p then raise Not_found; - let e = (try type_type ctx ([],i) p with Error (Module_not_found ([],name),_) when name = i -> raise Not_found) in + let e = (try type_type ctx ([],i) p with Error (Module_not_found ([],name),_,_) when name = i -> raise Not_found) in AKExpr e with Not_found -> let resolved_to_type_parameter = ref false in try - let t = List.find (fun (i2,_) -> i2 = i) ctx.type_params in + let t = List.find (fun tp -> tp.ttp_name = i) ctx.type_params in resolved_to_type_parameter := true; - let c = match follow (snd t) with TInst(c,_) -> c | _ -> die "" __LOC__ in + let c = match follow (extract_param_type t) with TInst(c,_) -> c | _ -> die "" __LOC__ in if TypeloadCheck.is_generic_parameter ctx c && Meta.has Meta.Const c.cl_meta then begin let e = type_module_type ctx (TClassDecl c) None p in - AKExpr {e with etype = (snd t)} + AKExpr {e with etype = (extract_param_type t)} end else raise Not_found with Not_found -> @@ -435,25 +486,25 @@ and type_ident ctx i p mode with_type = let t = mk_mono() in AKExpr ((mk (TIdent i)) t p) end else begin - if ctx.curfun = FunStatic && PMap.mem i ctx.curclass.cl_fields then error ("Cannot access " ^ i ^ " in static function") p; + if ctx.curfun = FunStatic && PMap.mem i ctx.curclass.cl_fields then typing_error ("Cannot access " ^ i ^ " in static function") p; if !resolved_to_type_parameter then begin - display_error ctx ("Only @:const type parameters on @:generic classes can be used as value") p; + display_error ctx.com ("Only @:const type parameters on @:generic classes can be used as value") p; AKExpr (mk (TConst TNull) t_dynamic p) end else begin let err = Unknown_ident i in if ctx.in_display then begin - raise (Error (err,p)) + raise (Error (err,p,0)) end; - match ctx.com.display.dms_kind with + if Diagnostics.error_in_diagnostics_run ctx.com p then begin + DisplayToplevel.handle_unresolved_identifier ctx i p false; + DisplayFields.handle_missing_ident ctx i mode with_type p; + let t = mk_mono() in + AKExpr (mk (TIdent i) t p) + end else match ctx.com.display.dms_kind with | DMNone -> - raise (Error(err,p)) - | DMDiagnostics _ -> - DisplayToplevel.handle_unresolved_identifier ctx i p false; - DisplayFields.handle_missing_ident ctx i mode with_type p; - let t = mk_mono() in - AKExpr (mk (TIdent i) t p) + raise (Error(err,p,0)) | _ -> - display_error ctx (error_msg err) p; + located_display_error ctx.com (error_msg p err); let t = mk_mono() in (* Add a fake local for #8751. *) if !ServerConfig.legacy_completion then @@ -466,7 +517,7 @@ and handle_efield ctx e p0 mode with_type = let open TyperDotPath in let dot_path first pnext = - let name,_,p = first in + let {name = name; pos = p} = first in try (* first, try to resolve the first ident in the chain and access its fields. this doesn't support untyped identifiers yet, because we want to check fully-qualified @@ -482,18 +533,18 @@ and handle_efield ctx e p0 mode with_type = try (* TODO: we don't really want to do full type_ident again, just the second part of it *) field_chain ctx pnext (type_ident ctx name p MGet WithType.value) - with Error (Unknown_ident _,p2) as e when p = p2 -> + with Error (Unknown_ident _,p2,_) as e when p = p2 -> try (* try raising a more sensible error if there was an uppercase-first (module name) part *) begin (* TODO: we should pass the actual resolution error from resolve_dot_path instead of Not_found *) let rec loop pack_acc first_uppercase path = match path with - | (name,PLowercase,_) :: rest -> + | {name = name; case = PLowercase} :: rest -> (match first_uppercase with | None -> loop (name :: pack_acc) None rest | Some (n,p) -> List.rev pack_acc, n, None, p) - | (name,PUppercase,p) :: rest -> + | {name = name; case = PUppercase; pos = p} :: rest -> (match first_uppercase with | None -> loop pack_acc (Some (name,p)) rest | Some (n,_) -> List.rev pack_acc, n, Some name, p) @@ -504,16 +555,16 @@ and handle_efield ctx e p0 mode with_type = in let pack,name,sub,p = loop [] None path in let mpath = (pack,name) in - if Hashtbl.mem ctx.g.modules mpath then + if ctx.com.module_lut#mem mpath then let tname = Option.default name sub in - raise (Error (Type_not_found (mpath,tname,Not_defined),p)) + raise (Error (Type_not_found (mpath,tname,Not_defined),p,0)) else - raise (Error (Module_not_found mpath,p)) + raise (Error (Module_not_found mpath,p,0)) end with Not_found -> (* if there was no module name part, last guess is that we're trying to get package completion *) if ctx.in_display then begin - let sl = List.map (fun (n,_,_) -> n) path in + let sl = List.map (fun part -> part.name) path in if is_legacy_completion ctx.com then raise (Parser.TypePath (sl,None,false,p)) else @@ -523,35 +574,60 @@ and handle_efield ctx e p0 mode with_type = in (* loop through the given EField expression to figure out whether it's a dot-path that we have to resolve, - or a simple field access chain *) + or a field access chain *) let rec loop dot_path_acc (e,p) = match e with - | EField (e,s) -> + | EField (e,s,EFNormal) -> (* field access - accumulate and check further *) loop ((mk_dot_path_part s p) :: dot_path_acc) e | EConst (Ident i) -> (* it's a dot-path, so it might be either fully-qualified access (pack.Class.field) or normal field access of a local/global/field identifier, proceed figuring this out *) - dot_path (mk_dot_path_part i p) dot_path_acc + dot_path (mk_dot_path_part i p) dot_path_acc mode with_type + | EField ((eobj,pobj),s,EFSafe) -> + (* safe navigation field access - definitely NOT a fully-qualified access, + create safe navigation chain from the object expression *) + let acc_obj = type_access ctx eobj pobj MGet WithType.value in + let eobj = acc_get ctx acc_obj in + let eobj, tempvar = match (Texpr.skip eobj).eexpr with + | TLocal _ | TTypeExpr _ | TConst _ -> + eobj, None + | _ -> + let v = alloc_var VGenerated "tmp" eobj.etype eobj.epos in + let temp_var = mk (TVar(v, Some eobj)) ctx.t.tvoid v.v_pos in + let eobj = mk (TLocal v) v.v_type v.v_pos in + eobj, Some temp_var + in + let access = field_chain ctx ((mk_dot_path_part s p) :: dot_path_acc) (AKExpr eobj) mode with_type in + AKSafeNav { + sn_pos = p; + sn_base = eobj; + sn_temp_var = tempvar; + sn_access = access; + } | _ -> (* non-ident expr occured: definitely NOT a fully-qualified access, resolve the field chain against this expression *) - let e = type_access ctx e p MGet WithType.value in - field_chain ctx dot_path_acc e + (match (type_access ctx e p MGet WithType.value) with + | AKSafeNav sn -> + (* further field access continues the safe navigation chain (after a non-field access inside the chain) *) + AKSafeNav { sn with sn_access = field_chain ctx dot_path_acc sn.sn_access mode with_type } + | e -> + field_chain ctx dot_path_acc e mode with_type) in - loop [] (e,p0) mode with_type + loop [] (e,p0) and type_access ctx e p mode with_type = match e with | EConst (Ident s) -> type_ident ctx s p mode with_type - | EField (e1,"new") -> + | EField (e1,"new",efk_todo) -> let e1 = type_expr ctx e1 WithType.value in begin match e1.eexpr with | TTypeExpr (TClassDecl c) -> begin match mode with - | MSet _ -> error "Cannot set constructor" p; - | MCall _ -> error ("Cannot call constructor like this, use 'new " ^ (s_type_path c.cl_path) ^ "()' instead") p; + | MSet _ -> typing_error "Cannot set constructor" p; + | MCall _ -> typing_error ("Cannot call constructor like this, use 'new " ^ (s_type_path c.cl_path) ^ "()' instead") p; | MGet -> () end; let monos = Monomorph.spawn_constrained_monos (fun t -> t) (match c.cl_kind with KAbstractImpl a -> a.a_params | _ -> c.cl_params) in @@ -580,21 +656,31 @@ and type_access ctx e p mode with_type = tf_type = t; tf_expr = mk (TReturn (Some ec)) t p; }) (TFun ((List.map (fun v -> v.v_name,false,v.v_type) vl),t)) p) - | _ -> error "Binding new is only allowed on class types" p + | _ -> typing_error "Binding new is only allowed on class types" p end; | EField _ -> handle_efield ctx e p mode with_type | EArray (e1,e2) -> type_array_access ctx e1 e2 p mode + | ECall (e, el) -> + type_call_access ctx e el mode with_type None p | EDisplay (e,dk) -> - AKExpr (TyperDisplay.handle_edisplay ctx e dk mode WithType.value) + AKExpr (TyperDisplay.handle_edisplay ctx e dk mode with_type) | _ -> - AKExpr (type_expr ~mode ctx (e,p) WithType.value) + AKExpr (type_expr ~mode ctx (e,p) with_type) and type_array_access ctx e1 e2 p mode = - let e1 = type_expr ctx e1 WithType.value in + let e1, p1 = e1 in + let a1 = type_access ctx e1 p1 MGet WithType.value in let e2 = type_expr ctx e2 WithType.value in - Calls.array_access ctx e1 e2 mode p + match a1 with + | AKSafeNav sn -> + (* pack the array access inside the safe navigation chain *) + let e1 = acc_get ctx sn.sn_access in + AKSafeNav { sn with sn_access = Calls.array_access ctx e1 e2 mode p } + | _ -> + let e1 = acc_get ctx a1 in + Calls.array_access ctx e1 e2 mode p and type_vars ctx vl p = let vl = List.map (fun ev -> @@ -606,29 +692,35 @@ and type_vars ctx vl p = let e = (match ev.ev_expr with | None -> None | Some e -> - let e = type_expr ctx e (WithType.with_type t) in + let old_in_loop = ctx.in_loop in + if ev.ev_static then ctx.in_loop <- false; + let e = Std.finally (fun () -> ctx.in_loop <- old_in_loop) (type_expr ctx e) (WithType.with_type t) in let e = AbstractCast.cast_or_unify ctx t e p in Some e ) in - let v = add_local_with_origin ctx TVOLocalVariable n t pv in + let v = if Meta.has Meta.This ev.ev_meta then + add_local ctx VAbstractThis n t pv + else + add_local_with_origin ctx TVOLocalVariable n t pv + in v.v_meta <- ev.ev_meta; + DisplayEmitter.check_display_metadata ctx v.v_meta; if ev.ev_final then add_var_flag v VFinal; + if ev.ev_static then add_var_flag v VStatic; if ctx.in_display && DisplayPosition.display_position#enclosed_in pv then DisplayEmitter.display_variable ctx v pv; v,e with - Error (e,p) -> - check_error ctx e p; + Error (e,p,depth) -> + check_error ctx e p depth; add_local ctx VGenerated n t_dynamic pv, None (* TODO: What to do with this... *) ) vl in - delay ctx PTypeField (fun() -> - List.iter - (fun (v,_) -> - if ExtType.is_void (follow v.v_type) then - error "Variables of type Void are not allowed" v.v_pos - ) - vl - ); + List.iter (fun (v,_) -> + delay_if_mono ctx PTypeField v.v_type (fun() -> + if ExtType.is_void (follow v.v_type) then + typing_error "Variables of type Void are not allowed" v.v_pos + ) + ) vl; match vl with | [v,eo] -> mk (TVar (v,eo)) ctx.t.tvoid p @@ -700,7 +792,7 @@ and format_string ctx s p = if i = len then match groups with | [] -> die "" __LOC__ - | g :: _ -> error ("Unclosed " ^ gname) { p with pmin = !pmin + g + 1; pmax = !pmin + g + 2 } + | g :: _ -> typing_error ("Unclosed " ^ gname) { p with pmin = !pmin + g + 1; pmax = !pmin + g + 2 } else let c = String.unsafe_get s i in if c = gopen then @@ -719,8 +811,8 @@ and format_string ctx s p = let e = let ep = { p with pmin = !pmin + pos + 2; pmax = !pmin + send + 1 } in let error msg pos = - if Lexer.string_is_whitespace scode then error "Expression cannot be empty" ep - else error msg pos + if Lexer.string_is_whitespace scode then typing_error "Expression cannot be empty" ep + else typing_error msg pos in match ParserEntry.parse_expr_string ctx.com.defines scode ep error true with | ParseSuccess(data,_,_) -> data @@ -746,7 +838,7 @@ and type_block ctx el with_type p = let rec loop acc = function | [] -> List.rev acc | e :: l -> - let acc = try merge acc (type_expr ctx e (if l = [] then with_type else WithType.no_value)) with Error (e,p) -> check_error ctx e p; acc in + let acc = try merge acc (type_expr ctx e (if l = [] then with_type else WithType.no_value)) with Error (e,p,depth) -> check_error ctx e p depth; acc in loop acc l in let l = loop [] el in @@ -767,12 +859,14 @@ and type_object_decl ctx fl with_type p = | TAbstract (a,pl) as t when not (Meta.has Meta.CoreType a.a_meta) && not (List.exists (fun t' -> shallow_eq t t') seen) -> - let froms = get_abstract_froms a pl - and fold = fun acc t' -> match loop (t :: seen) t' with ODKPlain -> acc | t -> t :: acc in - (match List.fold_left fold [] froms with - | [t] -> t - | _ -> ODKPlain) - | TDynamic t when (follow t != t_dynamic) -> + let froms = get_abstract_froms ctx a pl in + let fold = fun acc t' -> match loop (t :: seen) t' with ODKPlain -> acc | t -> t :: acc in + begin match List.fold_left fold [] froms with + | [] -> ODKPlain (* If the abstract has no casts in the first place, we can assume plain typing (issue #10730) *) + | [t] -> t + | _ -> ODKFailed + end + | TDynamic (Some t) -> dynamic_parameter := Some t; ODKWithStructure { a_status = ref Closed; @@ -792,7 +886,7 @@ and type_object_decl ctx fl with_type p = let extra_fields = ref [] in let fl = List.map (fun ((n,pn,qs),e) -> let is_valid = Lexer.is_valid_identifier n in - if PMap.mem n !fields then error ("Duplicate field in object declaration : " ^ n) p; + if PMap.mem n !fields then typing_error ("Duplicate field in object declaration : " ^ n) pn; let is_final = ref false in let e = try let t = match !dynamic_parameter with @@ -813,7 +907,7 @@ and type_object_decl ctx fl with_type p = type_expr ctx e WithType.value in if is_valid then begin - if starts_with n '$' then error "Field names starting with a dollar are not allowed" p; + if starts_with n '$' then typing_error "Field names starting with a dollar are not allowed" p; let cf = mk_field n e.etype (punion pn e.epos) pn in if !is_final then add_class_field_flag cf CfFinal; fields := PMap.add n cf !fields; @@ -825,7 +919,7 @@ and type_object_decl ctx fl with_type p = (match PMap.foldi (fun n cf acc -> if not (Meta.has Meta.Optional cf.cf_meta) && not (PMap.mem n !fields) then n :: acc else acc) field_map [] with | [] -> () | [n] -> raise_or_display ctx [Unify_custom ("Object requires field " ^ n)] p - | nl -> raise_or_display ctx [Unify_custom ("Object requires fields: " ^ (String.concat ", " nl))] p); + | depth -> raise_or_display ctx [Unify_custom ("Object requires fields: " ^ (String.concat ", " depth))] p); (match !extra_fields with | [] -> () | _ -> raise_or_display ctx (List.map (fun n -> has_extra_field t n) !extra_fields) p); @@ -835,13 +929,13 @@ and type_object_decl ctx fl with_type p = let type_plain_fields () = let rec loop (l,acc) ((f,pf,qs),e) = let is_valid = Lexer.is_valid_identifier f in - if PMap.mem f acc then error ("Duplicate field in object declaration : " ^ f) p; + if PMap.mem f acc then typing_error ("Duplicate field in object declaration : " ^ f) pf; let e = type_expr ctx e (WithType.named_structure_field f) in - (match follow e.etype with TAbstract({a_path=[],"Void"},_) -> error "Fields of type Void are not allowed in structures" e.epos | _ -> ()); + (match follow e.etype with TAbstract({a_path=[],"Void"},_) -> typing_error "Fields of type Void are not allowed in structures" e.epos | _ -> ()); let cf = mk_field f e.etype (punion pf e.epos) pf in if ctx.in_display && DisplayPosition.display_position#enclosed_in pf then DisplayEmitter.display_field ctx Unknown CFSMember cf pf; (((f,pf,qs),e) :: l, if is_valid then begin - if starts_with f '$' then error "Field names starting with a dollar are not allowed" p; + if starts_with f '$' then typing_error "Field names starting with a dollar are not allowed" p; PMap.add f cf acc end else acc) in @@ -851,7 +945,7 @@ and type_object_decl ctx fl with_type p = mk (TObjectDecl (List.rev fields)) (mk_anon ~fields:types x) p in (match a with - | ODKPlain -> type_plain_fields() + | ODKPlain | ODKFailed -> type_plain_fields() | ODKWithStructure a when PMap.is_empty a.a_fields && !dynamic_parameter = None -> type_plain_fields() | ODKWithStructure a -> let t, fl = type_fields a.a_fields in @@ -860,12 +954,26 @@ and type_object_decl ctx fl with_type p = let fa = FieldAccess.get_constructor_access c tl p in let ctor = fa.fa_field in let args = match follow (FieldAccess.get_map_function fa ctor.cf_type) with - | TFun(args,_) -> args + | TFun(args,_) -> + begin match ctor.cf_expr with + | Some {eexpr = TFunction tf} -> + let rec loop acc args vl = match args,vl with + | arg :: args,(v,_) :: vl -> + loop ((arg,v.v_pos) :: acc) args vl + | [],_ -> + List.rev acc + | arg :: args,[] -> + loop ((arg,ctor.cf_name_pos) :: acc) args [] + in + loop [] args tf.tf_args + | _ -> + List.map (fun args -> (args,ctor.cf_name_pos)) args + end | _ -> die "" __LOC__ in - let fields = List.fold_left (fun acc (n,opt,t) -> - let f = mk_field n t ctor.cf_pos ctor.cf_name_pos in - if opt then f.cf_meta <- [(Meta.Optional,[],ctor.cf_pos)]; + let fields = List.fold_left (fun acc ((n,opt,t),parg) -> + let f = mk_field n t parg parg in + if opt then f.cf_meta <- [(Meta.Optional,[],null_pos)]; PMap.add n f acc ) PMap.empty args in let t,fl = type_fields fields in @@ -883,7 +991,7 @@ and type_object_decl ctx fl with_type p = evars,(s,e) :: elocs,OptimizerTexpr.has_side_effect e end ) ([],[],false) (List.rev fl) in - let el = List.map (fun (n,_,t) -> + let el = List.map (fun ((n,_,t),parg) -> try Expr.field_assoc n fl with Not_found -> try @@ -929,13 +1037,16 @@ and type_new ctx path el with_type force_inline p = let fcc = unify_field_call ctx fa [] el p fa.fa_inline in check_constructor_access ctx c fcc.fc_field p; fcc - with Error (e,p) -> - error (error_msg e) p; + with Error (e,p,depth) -> + located_typing_error ~depth (error_msg p e); + in + let display_position_in_el () = + List.exists (fun e -> DisplayPosition.display_position#enclosed_in (pos e)) el in let t = if (fst path).tparams <> [] then begin try Typeload.load_instance ctx path false - with Error _ as exc when ctx.com.display.dms_display -> + with Error _ as exc when display_position_in_el() -> (* If we fail for some reason, process the arguments in case we want to display them (#7650). *) List.iter (fun e -> ignore(type_expr ctx e WithType.value)) el; raise exc @@ -946,7 +1057,7 @@ and type_new ctx path el with_type force_inline p = ctx.call_argument_stack <- List.tl ctx.call_argument_stack; (* Try to properly build @:generic classes here (issue #2016) *) begin match t_follow with - | TInst({cl_kind = KGeneric } as c,tl) -> follow (Generic.build_generic ctx c p tl) + | TInst({cl_kind = KGeneric } as c,tl) -> follow (Generic.build_generic_class ctx c p tl) | _ -> t end with @@ -959,7 +1070,7 @@ and type_new ctx path el with_type force_inline p = no_abstract_constructor c p; ignore (unify_constructor_call c fa); begin try - Generic.build_generic ctx c p monos + Generic.build_generic_class ctx c p monos with Generic.Generic_Exception _ as exc -> (* If we have an expected type, just use that (issue #3804) *) begin match with_type with @@ -973,9 +1084,9 @@ and type_new ctx path el with_type force_inline p = end end | mt -> - error ((s_type_path (t_infos mt).mt_path) ^ " cannot be constructed") p + typing_error ((s_type_path (t_infos mt).mt_path) ^ " cannot be constructed") p end - | Error _ as exc when ctx.com.display.dms_display -> + | Error _ as exc when display_position_in_el() -> List.iter (fun e -> ignore(type_expr ctx e WithType.value)) el; raise exc in @@ -987,17 +1098,17 @@ and type_new ctx path el with_type force_inline p = let cf = fa.fa_field in no_abstract_constructor c p; begin match cf.cf_kind with - | Var { v_read = AccRequire (r,msg) } -> (match msg with Some msg -> error msg p | None -> error_require r p) + | Var { v_read = AccRequire (r,msg) } -> (match msg with Some msg -> typing_error msg p | None -> error_require r p) | _ -> () end; unify_constructor_call c fa in try begin match Abstract.follow_with_forward_ctor t with | TInst ({cl_kind = KTypeParameter tl} as c,params) -> - if not (TypeloadCheck.is_generic_parameter ctx c) then error "Only generic type parameters can be constructed" p; + if not (TypeloadCheck.is_generic_parameter ctx c) then typing_error "Only generic type parameters can be constructed" p; begin match get_constructible_constraint ctx tl p with | None -> - raise_error (No_constructor (TClassDecl c)) p + raise_typing_error (No_constructor (TClassDecl c)) p | Some(tl,tr) -> let el,_ = unify_call_args ctx el tl tr p false false false in mk (TNew (c,params,el)) t p @@ -1010,9 +1121,9 @@ and type_new ctx path el with_type force_inline p = let el = fcc.fc_args in mk (TNew (c,params,el)) t p | _ -> - error (s_type (print_context()) t ^ " cannot be constructed") p - end with Error(No_constructor _ as err,p) when ctx.com.display.dms_kind <> DMNone -> - display_error ctx (error_msg err) p; + typing_error (s_type (print_context()) t ^ " cannot be constructed") p + end with Error(No_constructor _ as err,p,depth) when ctx.com.display.dms_kind <> DMNone -> + located_display_error ~depth ctx.com (error_msg p err); Diagnostics.secure_generated_code ctx (mk (TConst TNull) t p) and type_try ctx e1 catches with_type p = @@ -1020,9 +1131,9 @@ and type_try ctx e1 catches with_type p = let rec check_unreachable cases t p = match cases with | (v,e) :: cases -> let unreachable () = - display_error ctx "This block is unreachable" p; + display_error ctx.com "This block is unreachable" p; let st = s_type (print_context()) in - display_error ctx (Printf.sprintf "%s can be caught to %s, which is handled here" (st t) (st v.v_type)) e.epos + display_error ctx.com (Printf.sprintf "%s can be caught to %s, which is handled here" (st t) (st v.v_type)) e.epos in begin try begin match follow t,follow v.v_type with @@ -1045,7 +1156,7 @@ and type_try ctx e1 catches with_type p = in let check_catch_type_params params p = List.iter (fun pt -> - if Abstract.follow_with_abstracts pt != t_dynamic then error "Catch class parameter must be Dynamic" p; + if Abstract.follow_with_abstracts pt != t_dynamic then typing_error "Catch class parameter must be Dynamic" p; ) params in let catches,el = List.fold_left (fun (acc1,acc2) ((v,pv),t,e_ast,pc) -> @@ -1053,7 +1164,7 @@ and type_try ctx e1 catches with_type p = let t = Typeload.load_complex_type ctx true th in let rec loop t = match follow t with | TInst ({ cl_kind = KTypeParameter _} as c,_) when not (TypeloadCheck.is_generic_parameter ctx c) -> - error "Cannot catch non-generic type parameter" p + typing_error "Cannot catch non-generic type parameter" p | TInst (_,params) | TEnum (_,params) -> check_catch_type_params params (snd th); t @@ -1063,7 +1174,7 @@ and type_try ctx e1 catches with_type p = | TAbstract(a,tl) when not (Meta.has Meta.CoreType a.a_meta) -> loop (Abstract.get_underlying_type a tl) | TDynamic _ -> t - | _ -> error "Catch type must be a class, an enum or Dynamic" (pos e_ast) + | _ -> typing_error "Catch type must be a class, an enum or Dynamic" (pos e_ast) in let t2 = loop t in check_unreachable acc1 t2 (pos e_ast); @@ -1110,8 +1221,8 @@ and type_map_declaration ctx e1 el with_type p = let check_key e_key = try let p = Hashtbl.find keys e_key.eexpr in - display_error ctx "Duplicate key" e_key.epos; - error (compl_msg "Previously defined here") p + display_error ctx.com "Duplicate key" e_key.epos; + typing_error ~depth:1 (compl_msg "Previously defined here") p with Not_found -> begin match e_key.eexpr with | TConst _ -> Hashtbl.add keys e_key.eexpr e_key.epos; @@ -1123,8 +1234,8 @@ and type_map_declaration ctx e1 el with_type p = | EBinop(OpArrow,e1,e2) -> e1,e2 | EDisplay _ -> ignore(type_expr ctx e (WithType.with_type tkey)); - error "Expected a => b" (pos e) - | _ -> error "Expected a => b" (pos e) + typing_error "Expected a => b" (pos e) + | _ -> typing_error "Expected a => b" (pos e) ) el in let el_k,el_v,tkey,tval = if has_type then begin let el_k,el_v = List.fold_left (fun (el_k,el_v) (e1,e2) -> @@ -1167,8 +1278,8 @@ and type_local_function ctx kind f with_type p = let name,inline = match kind with FKNamed (name,inline) -> Some name,inline | _ -> None,false in let params = TypeloadFunction.type_function_params ctx f (match name with None -> "localfun" | Some (n,_) -> n) p in if params <> [] then begin - if name = None then display_error ctx "Type parameters not supported in unnamed local functions" p; - if with_type <> WithType.NoValue then error "Type parameters are not supported for rvalue functions" p + if name = None then display_error ctx.com "Type parameters not supported in unnamed local functions" p; + if with_type <> WithType.NoValue then typing_error "Type parameters are not supported for rvalue functions" p end; let v,pname = (match name with | None -> None,p @@ -1178,33 +1289,95 @@ and type_local_function ctx kind f with_type p = ctx.type_params <- params @ ctx.type_params; if not inline then ctx.in_loop <- false; let rt = Typeload.load_type_hint ctx p f.f_type in - let type_arg opt t p = Typeload.load_type_hint ~opt ctx p t in + let type_arg _ opt t p = Typeload.load_type_hint ~opt ctx p t in let args = new FunctionArguments.function_arguments ctx type_arg false ctx.in_display None f.f_args in let targs = args#for_type in + let maybe_unify_arg t1 t2 = + match follow t1 with + | TMono _ -> unify ctx t2 t1 p + | _ -> () + in + let maybe_unify_ret tr = match follow tr,follow rt with + | TAbstract({a_path = [],"Void"},_),_ when kind <> FKArrow -> () + | _,TMono _ -> unify ctx rt tr p + | _ -> () + in + (* The idea here is: If we have multiple `from Function`, we can + 1. ignore any that have a different argument arity, and + 2. still top-down infer any argument or return type that is equal across all candidates. + *) + let handle_abstract_matrix l = + let arity = List.length targs in + let m = new unification_matrix (arity + 1) in + let rec loop l = match l with + | t :: l -> + begin match follow t with + | TFun(args,ret) when List.length args = arity -> + List.iteri (fun i (_,_,t) -> + (* We don't want to bind monomorphs because we want the widest type *) + let t = dynamify_monos t in + m#join t (i + 1); + ) args; + let ret = dynamify_monos ret in + m#join ret 0; + | t -> + () + end; + loop l + | [] -> + () + in + loop l; + List.iteri (fun i (_,_,t1) -> + match m#get_type (i + 1) with + | Some t2 -> + maybe_unify_arg t1 t2 + | None -> + () + ) targs; + begin match m#get_type 0 with + | Some tr -> + maybe_unify_ret tr + | None -> + () + end + in (match with_type with | WithType.WithType(t,_) -> - let rec loop t = + let rec loop stack t = (match follow t with | TFun (args2,tr) when List.length args2 = List.length targs -> List.iter2 (fun (_,_,t1) (_,_,t2) -> - match follow t1 with - | TMono _ -> unify ctx t2 t1 p - | _ -> () + maybe_unify_arg t1 t2 ) targs args2; (* unify for top-down inference unless we are expecting Void *) - begin - match follow tr,follow rt with - | TAbstract({a_path = [],"Void"},_),_ when kind <> FKArrow -> () - | _,TMono _ -> unify ctx rt tr p - | _ -> () - end + maybe_unify_ret tr | TAbstract(a,tl) -> - loop (Abstract.get_underlying_type a tl) + begin match get_abstract_froms ctx a tl with + | [t2] -> + if not (List.exists (shallow_eq t) stack) then loop (t :: stack) t2 + | l -> + (* For cases like nested EitherType, we want a flat list of all possible candidates. + This might be controversial because those could be considered transitive casts, + but it's unclear if that's a bad thing for this kind of inference (issue #10982). *) + let rec loop stack acc l = match l with + | t :: l -> + begin match follow t with + | TAbstract(a,tl) as t when not (List.exists (shallow_eq t) stack) -> + loop (t :: stack) acc (l @ get_abstract_froms ctx a tl) + | _ -> + loop stack (t :: acc) l + end + | [] -> + List.rev acc + in + handle_abstract_matrix (loop [] [] l) + end | _ -> ()) in - loop t + loop [] t | WithType.NoValue -> - if name = None then display_error ctx "Unnamed lvalue functions are not supported" p + if name = None then display_error ctx.com "Unnamed lvalue functions are not supported" p | _ -> ()); let ft = TFun (targs,rt) in @@ -1250,11 +1423,11 @@ and type_local_function ctx kind f with_type p = in let exprs = if is_rec then begin - if inline then display_error ctx "Inline function cannot be recursive" e.epos; + if inline then display_error ctx.com "Inline function cannot be recursive" e.epos; (mk (TVar (v,Some (mk (TConst TNull) ft p))) ctx.t.tvoid p) :: (mk (TBinop (OpAssign,mk (TLocal v) ft p,e)) ft p) :: exprs - end else if inline && not ctx.com.display.dms_display then + end else if inline && not ctx.is_display_file then (mk (TBlock []) ctx.t.tvoid p) :: exprs (* do not add variable since it will be inlined *) else (mk (TVar (v,Some e)) ctx.t.tvoid p) :: exprs @@ -1290,7 +1463,7 @@ and type_array_decl ctx el with_type p = | Some t -> t :: acc ) [] - (get_abstract_froms a pl) + (get_abstract_froms ctx a pl) in (match types with | [t] -> Some t @@ -1312,24 +1485,26 @@ and type_array_decl ctx el with_type p = let el = List.map (fun e -> type_expr ctx e WithType.value) el in let t = try unify_min_raise ctx el - with Error (Unify l,p) -> - if !allow_array_dynamic || ctx.untyped || ctx.com.display.dms_error_policy = EPIgnore then + with Error (Unify l,p,n) -> + if !allow_array_dynamic || ctx.untyped || ignore_error ctx.com then t_dynamic else begin - display_error ctx "Arrays of mixed types are only allowed if the type is forced to Array" p; - raise (Error (Unify l, p)) + display_error ctx.com "Arrays of mixed types are only allowed if the type is forced to Array" p; + raise (Error (Unify l, p,n)) end in mk (TArrayDecl el) (ctx.t.tarray t) p | Some t -> let el = List.map (fun e -> let e = type_expr ctx e (WithType.with_type t) in - AbstractCast.cast_or_unify ctx t e p; + AbstractCast.cast_or_unify ctx t e e.epos; ) el in mk (TArrayDecl el) (ctx.t.tarray t) p) and type_array_comprehension ctx e with_type p = let v = gen_local ctx (spawn_monomorph ctx p) p in + let ev = mk (TLocal v) v.v_type p in + let e_ref = snd (store_typed_expr ctx.com ev p) in let et = ref (EConst(Ident "null"),p) in let comprehension_pos = p in let rec map_compr (e,p) = @@ -1347,10 +1522,10 @@ and type_array_comprehension ctx e with_type p = | EParenthesis e2 -> (EParenthesis (map_compr e2),p) | EBinop(OpArrow,a,b) -> et := (ENew(({tpackage=["haxe";"ds"];tname="Map";tparams=[];tsub=None},null_pos),[]),comprehension_pos); - (ECall ((EField ((EConst (Ident v.v_name),p),"set"),p),[a;b]),p) + (ECall ((efield (e_ref,"set"),p),[a;b]),p) | _ -> et := (EArrayDecl [],comprehension_pos); - (ECall ((EField ((EConst (Ident v.v_name),p),"push"),p),[(e,p)]),p) + (ECall ((efield (e_ref,"push"),p),[(e,p)]),p) in let e = map_compr e in let ea = type_expr ctx !et with_type in @@ -1359,7 +1534,7 @@ and type_array_comprehension ctx e with_type p = mk (TBlock [ mk (TVar (v,Some ea)) ctx.t.tvoid p; efor; - mk (TLocal v) v.v_type p; + ev; ]) v.v_type p and type_return ?(implicit=false) ctx e with_type p = @@ -1367,7 +1542,7 @@ and type_return ?(implicit=false) ctx e with_type p = match e with | None when is_abstract_ctor -> let e_cast = mk (TCast(get_this ctx p,None)) ctx.ret p in - mk (TReturn (Some e_cast)) t_dynamic p + mk (TReturn (Some e_cast)) (mono_or_dynamic ctx with_type p) p | None -> let v = ctx.t.tvoid in unify ctx v ctx.ret p; @@ -1376,12 +1551,12 @@ and type_return ?(implicit=false) ctx e with_type p = | WithType.Value (Some ImplicitReturn) -> true | _ -> false in - mk (TReturn None) (if expect_void then v else t_dynamic) p + mk (TReturn None) (if expect_void then v else (mono_or_dynamic ctx with_type p)) p | Some e -> if is_abstract_ctor then begin match fst e with | ECast((EConst(Ident "this"),_),None) -> () - | _ -> display_error ctx "Cannot return a value from constructor" p + | _ -> display_error ctx.com "Cannot return a value from constructor" p end; try let with_expected_type = @@ -1398,22 +1573,23 @@ and type_return ?(implicit=false) ctx e with_type p = match follow e.etype with | TAbstract({a_path=[],"Void"},_) -> begin match (Texpr.skip e).eexpr with - | TConst TNull -> error "Cannot return `null` from Void-function" p + | TConst TNull -> typing_error "Cannot return `null` from Void-function" p | _ -> () end; (* if we get a Void expression (e.g. from inlining) we don't want to return it (issue #4323) *) + let t = mono_or_dynamic ctx with_type p in mk (TBlock [ e; - mk (TReturn None) t_dynamic p - ]) t_dynamic e.epos; + mk (TReturn None) t p + ]) t e.epos; | _ -> - mk (TReturn (Some e)) t_dynamic p - with Error(err,p) -> - check_error ctx err p; + mk (TReturn (Some e)) (mono_or_dynamic ctx with_type p) p + with Error(err,p,depth) -> + check_error ctx err p depth; (* If we have a bad return, let's generate a return null expression at least. This surpresses various follow-up errors that come from the fact that the function no longer has a return expression (issue #6445). *) let e_null = mk (TConst TNull) (mk_mono()) p in - mk (TReturn (Some e_null)) t_dynamic p + mk (TReturn (Some e_null)) (mono_or_dynamic ctx with_type p) p and type_cast ctx e t p = let tpos = pos t in @@ -1421,14 +1597,14 @@ and type_cast ctx e t p = let check_param pt = match follow pt with | TMono _ -> () (* This probably means that Dynamic wasn't bound (issue #4675). *) | t when t == t_dynamic -> () - | _ -> error "Cast type parameters must be Dynamic" tpos + | _ -> typing_error "Cast type parameters must be Dynamic" tpos in let rec loop t = match follow t with | TInst (_,params) | TEnum (_,params) -> List.iter check_param params; (match follow t with | TInst (c,_) -> - (match c.cl_kind with KTypeParameter _ -> error "Can't cast to a type parameter" tpos | _ -> ()); + (match c.cl_kind with KTypeParameter _ -> typing_error "Can't cast to a type parameter" tpos | _ -> ()); TClassDecl c | TEnum (e,_) -> TEnumDecl e | _ -> die "" __LOC__); @@ -1438,31 +1614,38 @@ and type_cast ctx e t p = | TAbstract (a,params) -> loop (Abstract.get_underlying_type a params) | _ -> - error "Cast type must be a class or an enum" tpos + typing_error "Cast type must be a class or an enum" tpos in let texpr = loop t in mk (TCast (type_expr ctx e WithType.value,Some texpr)) t p -and type_if ctx e e1 e2 with_type p = +and make_if_then_else ctx e0 e1 e2 with_type p = + let e1,e2,t = match with_type with + | WithType.NoValue -> e1,e2,ctx.t.tvoid + | WithType.Value _ -> e1,e2,unify_min ctx [e1; e2] + | WithType.WithType(t,src) when (match follow t with TMono _ -> true | t -> ExtType.is_void t) -> + e1,e2,unify_min_for_type_source ctx [e1; e2] src + | WithType.WithType(t,_) -> + let e1 = AbstractCast.cast_or_unify ctx t e1 e1.epos in + let e2 = AbstractCast.cast_or_unify ctx t e2 e2.epos in + e1,e2,t + in + mk (TIf (e0,e1,Some e2)) t p + +and type_if ctx e e1 e2 with_type is_ternary p = let e = type_expr ctx e WithType.value in + if is_ternary then begin match e.eexpr with + | TConst TNull -> typing_error "Cannot use null as ternary condition" e.epos + | _ -> () + end; let e = AbstractCast.cast_or_unify ctx ctx.t.tbool e p in let e1 = type_expr ctx (Expr.ensure_block e1) with_type in - (match e2 with + match e2 with | None -> mk (TIf (e,e1,None)) ctx.t.tvoid p | Some e2 -> let e2 = type_expr ctx (Expr.ensure_block e2) with_type in - let e1,e2,t = match with_type with - | WithType.NoValue -> e1,e2,ctx.t.tvoid - | WithType.Value _ -> e1,e2,unify_min ctx [e1; e2] - | WithType.WithType(t,src) when (match follow t with TMono _ -> true | t -> ExtType.is_void t) -> - e1,e2,unify_min_for_type_source ctx [e1; e2] src - | WithType.WithType(t,_) -> - let e1 = AbstractCast.cast_or_unify ctx t e1 e1.epos in - let e2 = AbstractCast.cast_or_unify ctx t e2 e2.epos in - e1,e2,t - in - mk (TIf (e,e1,Some e2)) t p) + make_if_then_else ctx e e1 e2 with_type p and type_meta ?(mode=MGet) ctx m e1 with_type p = if ctx.is_display_file then DisplayEmitter.check_display_metadata ctx [m]; @@ -1476,17 +1659,7 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p = | TAbstract({a_impl = Some c},_) when PMap.mem "toString" c.cl_statics -> call_to_string ctx e | _ -> e) | (Meta.Markup,_,_) -> - error "Markup literals must be processed by a macro" p - | (Meta.This,_,_) -> - let e = match ctx.this_stack with - | [] -> error "Cannot type @:this this here" p - | e :: _ -> e - in - let rec loop e = match e.eexpr with - | TConst TThis -> get_this ctx e.epos - | _ -> Type.map_expr loop e - in - loop e + typing_error "Markup literals must be processed by a macro" p | (Meta.Analyzer,_,_) -> let e = e() in {e with eexpr = TMeta(m,e)} @@ -1512,17 +1685,17 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p = let old_counter = ctx.bypass_accessor in ctx.bypass_accessor <- old_counter + 1; let e = e () in - (if ctx.bypass_accessor > old_counter then display_error ctx "Field access expression expected after @:bypassAccessor metadata" p); + (if ctx.bypass_accessor > old_counter then display_error ctx.com "Field access expression expected after @:bypassAccessor metadata" p); e - | (Meta.Inline,_,_) -> + | (Meta.Inline,_,pinline) -> begin match fst e1 with | ECall(e1,el) -> - type_call ctx e1 el WithType.value true p + acc_get ctx (type_call_access ctx e1 el MGet WithType.value (Some pinline) p) | ENew (t,el) -> let e = type_new ctx t el with_type true p in {e with eexpr = TMeta((Meta.Inline,[],null_pos),e)} | _ -> - display_error ctx "Call or function expected after inline keyword" p; + display_error ctx.com "Call or function expected after inline keyword" p; e(); end | (Meta.ImplicitReturn,_,_) -> @@ -1530,36 +1703,56 @@ and type_meta ?(mode=MGet) ctx m e1 with_type p = | (EReturn e, p) -> type_return ~implicit:true ctx e with_type p | _ -> e() end - | _ -> e() + | (Meta.Dollar s,_,p) -> + display_error ctx.com (Printf.sprintf "Reification $%s is not allowed outside of `macro` expression" s) p; + e() + | _ -> + if ctx.g.retain_meta then + let e = e() in + {e with eexpr = TMeta(m,e)} + else + e() in ctx.meta <- old; e -and type_call_target ctx e el with_type inline p = - let e = maybe_type_against_enum ctx (fun () -> type_access ctx (fst e) (snd e) (MCall el) with_type) with_type true p in - let check_inline cf = - if (has_class_field_flag cf CfAbstract) then display_error ctx "Cannot force inline on abstract method" p +and type_call_target ctx e el with_type p_inline = + let p = (pos e) in + let e = maybe_type_against_enum ctx (fun () -> type_access ctx (fst e) (snd e) (MCall el) WithType.value) with_type true p in + let check_inline cf p = + if (has_class_field_flag cf CfAbstract) then display_error ctx.com "Cannot force inline on abstract method" p in - if not inline then + match p_inline with + | None -> e - else match e with - | AKField fa -> - check_inline fa.fa_field; - AKField({fa with fa_inline = true}) - | AKUsingField sea -> - check_inline sea.se_access.fa_field; - AKUsingField {sea with se_access = {sea.se_access with fa_inline = true}} - | AKExpr {eexpr = TLocal _} -> - display_error ctx "Cannot force inline on local functions" p; - e - | _ -> - e + | Some pinline -> + let rec loop e = + match e with + | AKSafeNav sn -> + AKSafeNav { sn with sn_access = loop sn.sn_access } + | AKField fa -> + check_inline fa.fa_field pinline; + AKField({fa with fa_inline = true}) + | AKUsingField sea -> + check_inline sea.se_access.fa_field pinline; + AKUsingField {sea with se_access = {sea.se_access with fa_inline = true}} + | AKExpr {eexpr = TLocal _} -> + display_error ctx.com "Cannot force inline on local functions" pinline; + e + | _ -> + e + in + loop e -and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p = - let def () = - let e = type_call_target ctx e el with_type inline p in - build_call ~mode ctx e el with_type p; - in +and type_call_access ctx e el mode with_type p_inline p = + try + let e = type_call_builtin ctx e el mode with_type p in + AKExpr e + with Exit -> + let acc = type_call_target ctx e el with_type p_inline in + build_call_access ctx acc el mode with_type p + +and type_call_builtin ctx e el mode with_type p = match e, el with | (EConst (Ident "trace"),p) , e :: el -> if Common.defined ctx.com Define.NoTraces then @@ -1580,25 +1773,27 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p = let e_trace = mk (TIdent "`trace") t_dynamic p in mk (TCall (e_trace,[e;infos])) ctx.t.tvoid p else - type_expr ctx (ECall ((EField ((EField ((EConst (Ident "haxe"),p),"Log"),p),"trace"),p),[mk_to_string_meta e;infos]),p) WithType.NoValue - | (EField ((EConst (Ident "super"),_),_),_), _ -> - (match def() with - | { eexpr = TCall ({ eexpr = TField (_, FInstance(_, _, { cf_kind = Method MethDynamic; cf_name = name })); epos = p }, _) } as e -> - ctx.com.error ("Cannot call super." ^ name ^ " since it's a dynamic method") p; - e - | e -> e - ) - | (EField (e,"bind"),p), args -> + type_expr ctx (ECall ((efield ((efield ((EConst (Ident "haxe"),p),"Log"),p),"trace"),p),[mk_to_string_meta e;infos]),p) WithType.NoValue + | (EField ((EConst (Ident "super"),_),_,_),_), _ -> + (* no builtins can be applied to super as it can't be a value *) + raise Exit + | (EField (e,"bind",efk_todo),p), args -> let e = type_expr ctx e WithType.value in (match follow e.etype with | TFun signature -> type_bind ctx e signature args p - | _ -> def ()) + | _ -> raise Exit) | (EConst (Ident "$type"),_) , [e] -> - let e = type_expr ctx e WithType.value in - ctx.com.warning (s_type (print_context()) e.etype) e.epos; - let e = Diagnostics.secure_generated_code ctx e in - e - | (EField(e,"match"),p), [epat] -> + begin match fst e with + | EConst (Ident "_") -> + warning ctx WInfo (WithType.to_string with_type) p; + mk (TConst TNull) t_dynamic p + | _ -> + let e = type_expr ctx e WithType.value in + warning ctx WInfo (s_type (print_context()) e.etype) e.epos; + let e = Diagnostics.secure_generated_code ctx e in + e + end + | (EField(e,"match",efk_todo),p), [epat] -> let et = type_expr ctx e WithType.value in let rec has_enum_match t = match follow t with | TEnum _ -> true @@ -1611,7 +1806,7 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p = if has_enum_match et.etype then Matcher.Match.match_expr ctx e [[epat],None,Some (EConst(Ident "true"),p),p] (Some (Some (EConst(Ident "false"),p),p)) (WithType.with_type ctx.t.tbool) true p else - def () + raise Exit | (EConst (Ident "__unprotect__"),_) , [(EConst (String _),_) as e] -> let e = type_expr ctx e WithType.value in if Common.platform ctx.com Flash then @@ -1623,15 +1818,15 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p = | (EDisplay((EConst (Ident "super"),_ as e1),dk),_),_ -> TyperDisplay.handle_display ctx (ECall(e1,el),p) dk mode with_type | (EConst (Ident "super"),sp) , el -> - if ctx.curfun <> FunConstructor then error "Cannot call super constructor outside class constructor" p; + if ctx.curfun <> FunConstructor then typing_error "Cannot call super constructor outside class constructor" p; let el, t = (match ctx.curclass.cl_super with - | None -> error "Current class does not have a super" p + | None -> typing_error "Current class does not have a super" p | Some (c,params) -> let fa = FieldAccess.get_constructor_access c params p in let cf = fa.fa_field in let t = TInst (c,params) in let e = mk (TConst TSuper) t sp in - if (Meta.has Meta.CompilerGenerated cf.cf_meta) then display_error ctx (error_msg (No_constructor (TClassDecl c))) p; + if (Meta.has Meta.CompilerGenerated cf.cf_meta) then located_display_error ctx.com (error_msg p (No_constructor (TClassDecl c))); let fa = FieldAccess.create e cf (FHInstance(c,params)) false p in let fcc = unify_field_call ctx fa [] el p false in let el = fcc.fc_args in @@ -1639,22 +1834,23 @@ and type_call ?(mode=MGet) ctx e el (with_type:WithType.t) inline p = ) in mk (TCall (mk (TConst TSuper) t sp,el)) ctx.t.tvoid p | _ -> - def () + raise Exit and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = match e with - | EField ((EConst (String(s,_)),ps),"code") -> - if UTF8.length s <> 1 then error "String must be a single UTF8 char" ps; + | EField ((EConst (String(s,_)),ps),"code",EFNormal) -> + if UTF8.length s <> 1 then typing_error "String must be a single UTF8 char" ps; mk (TConst (TInt (Int32.of_int (UCharExt.code (UTF8.get s 0))))) ctx.t.tint p - | EField(_,n) when starts_with n '$' -> - error "Field names starting with $ are not allowed" p + | EField(_,n,_) when starts_with n '$' -> + typing_error "Field names starting with $ are not allowed" p | EConst (Ident s) -> - if s = "super" && with_type <> WithType.NoValue && not ctx.in_display then error "Cannot use super as value" p; + if s = "super" && with_type <> WithType.NoValue && not ctx.in_display then typing_error "Cannot use super as value" p; let e = maybe_type_against_enum ctx (fun () -> type_ident ctx s p mode with_type) with_type false p in - acc_get ctx e p + acc_get ctx e | EField _ - | EArray _ -> - acc_get ctx (type_access ctx e p mode with_type) p + | EArray _ + | ECall _ -> + acc_get ctx (type_access ctx e p mode with_type) | EConst (Regexp (r,opt)) -> let str = mk (TConst (TString r)) ctx.t.tstring p in let opt = mk (TConst (TString opt)) ctx.t.tstring p in @@ -1662,8 +1858,59 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = mk (TNew ((match t with TInst (c,[]) -> c | _ -> die "" __LOC__),[],[str;opt])) t p | EConst (String(s,SSingleQuotes)) when s <> "" -> type_expr ctx (format_string ctx s p) with_type + | EConst (Int (s, Some suffix)) -> + (match suffix with + | "i32" -> + (try mk (TConst (TInt (Int32.of_string s))) ctx.com.basic.tint p + with _ -> typing_error ("Cannot represent " ^ s ^ " with a 32 bit integer") p) + | "i64" -> + if String.length s > 18 && String.sub s 0 2 = "0x" then typing_error "Invalid hexadecimal integer" p; + + let i64 = Int64.of_string s in + let high = Int64.to_int32 (Int64.shift_right i64 32) in + let low = Int64.to_int32 i64 in + + let ident = EConst (Ident "haxe"), p in + let field = efield ((efield (ident, "Int64"), p), "make"), p in + + let arg_high = EConst (Int (Int32.to_string high, None)), p in + let arg_low = EConst (Int (Int32.to_string low, None)), p in + let call = ECall (field, [ arg_high; arg_low ]), p in + type_expr ctx call with_type + | "u32" -> + let check = ECheckType ((EConst (Int (s, None)), p), (CTPath (mk_type_path ([],"UInt")), p)), p in + type_expr ctx check with_type + | other -> typing_error (other ^ " is not a valid integer suffix") p) + | EConst (Float (s, Some suffix) as c) -> + (match suffix with + | "f64" -> Texpr.type_constant ctx.com.basic c p + | other -> typing_error (other ^ " is not a valid float suffix") p) | EConst c -> Texpr.type_constant ctx.com.basic c p + | EBinop (OpNullCoal,e1,e2) -> + let vr = new value_reference ctx in + let e1 = type_expr ctx (Expr.ensure_block e1) with_type in + let e2 = type_expr ctx (Expr.ensure_block e2) (WithType.with_type e1.etype) in + let e1 = vr#as_var "tmp" {e1 with etype = ctx.t.tnull e1.etype} in + let e_null = Builder.make_null e1.etype e1.epos in + let e_cond = mk (TBinop(OpNotEq,e1,e_null)) ctx.t.tbool e1.epos in + + let follow_null_once t = + match t with + | TAbstract({a_path = [],"Null"},[t]) -> t + | _ -> t + in + let iftype = if DeadEnd.has_dead_end e2 then + WithType.with_type (follow_null_once e1.etype) + else + WithType.WithType(e2.etype,None) + in + let e_if = make_if_then_else ctx e_cond e1 e2 iftype p in + vr#to_texpr e_if + | EBinop (OpAssignOp OpNullCoal,e1,e2) -> + let e_cond = EBinop(OpNotEq,e1,(EConst(Ident "null"), p)) in + let e_if = EIf ((e_cond, p),e1,Some e2) in + type_assign ctx e1 (e_if, p) with_type p | EBinop (op,e1,e2) -> type_binop ctx op e1 e2 false with_type p | EBlock [] when (match with_type with @@ -1715,9 +1962,9 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = | EFor (it,e2) -> ForLoop.type_for_loop ctx TyperDisplay.handle_display it e2 p | ETernary (e1,e2,e3) -> - type_expr ctx (EIf (e1,e2,Some e3),p) with_type + type_if ctx e1 e2 (Some e3) with_type true p | EIf (e,e1,e2) -> - type_if ctx e e1 e2 with_type p + type_if ctx e e1 e2 with_type false p | EWhile (cond,e,NormalWhile) -> let old_loop = ctx.in_loop in let cond = type_expr ctx cond WithType.value in @@ -1740,10 +1987,10 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = wrap e | EReturn e -> if not ctx.in_function then begin - display_error ctx "Return outside function" p; + display_error ctx.com "Return outside function" p; match e with | None -> - Texpr.Builder.make_null t_dynamic p + Texpr.Builder.make_null (mono_or_dynamic ctx with_type p) p | Some e -> (* type the return expression to see if there are more errors as well as use its type as if there was no `return`, since @@ -1752,20 +1999,23 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = end else type_return ctx e with_type p | EBreak -> - if not ctx.in_loop then display_error ctx "Break outside loop" p; - mk TBreak t_dynamic p + if not ctx.in_loop then display_error ctx.com "Break outside loop" p; + mk TBreak (mono_or_dynamic ctx with_type p) p | EContinue -> - if not ctx.in_loop then display_error ctx "Continue outside loop" p; - mk TContinue t_dynamic p + if not ctx.in_loop then display_error ctx.com "Continue outside loop" p; + mk TContinue (mono_or_dynamic ctx with_type p) p | ETry (e1,[]) -> type_expr ctx e1 with_type | ETry (e1,catches) -> type_try ctx e1 catches with_type p | EThrow e -> - let e = type_expr ctx e WithType.value in - mk (TThrow e) (spawn_monomorph ctx p) p - | ECall (e,el) -> - type_call ~mode ctx e el with_type false p + let e = try + type_expr ctx e WithType.value + with Error(e,p',depth) -> + check_error ctx e p' depth; + Texpr.Builder.make_null t_dynamic p + in + mk (TThrow e) (mono_or_dynamic ctx with_type p) p | ENew (t,el) -> type_new ctx t el with_type false p | EUnop (op,flag,e) -> @@ -1790,8 +2040,6 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = type_cast ctx e t p | EDisplay (e,dk) -> TyperDisplay.handle_edisplay ctx e dk mode with_type - | EDisplayNew t -> - die "" __LOC__ | ECheckType (e,t) -> let t = Typeload.load_complex_type ctx true t in let e = type_expr ctx e (WithType.with_type t) in @@ -1802,7 +2050,7 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = | EIs (e,(t,p_t)) -> match t with | CTPath tp -> - if tp.tparams <> [] then display_error ctx "Type parameters are not supported for the `is` operator" p_t; + if tp.tparams <> [] then display_error ctx.com "Type parameters are not supported for the `is` operator" p_t; let e = type_expr ctx e WithType.value in let mt = Typeload.load_type_def ctx p_t tp in if ctx.in_display && DisplayPosition.display_position#enclosed_in p_t then @@ -1820,7 +2068,7 @@ and type_expr ?(mode=MGet) ctx (e,p) (with_type:WithType.t) = in mk (TCall (e_Std_isOfType, [e; e_t])) ctx.com.basic.tbool p | _ -> - display_error ctx "Unsupported type for `is` operator" p_t; + display_error ctx.com "Unsupported type for `is` operator" p_t; Texpr.Builder.make_bool ctx.com.basic false p (* ---------------------------------------------------------------------- *) @@ -1833,44 +2081,40 @@ let rec create com = g = { core_api = None; macros = None; - modules = Hashtbl.create 0; - types_module = Hashtbl.create 0; type_patches = Hashtbl.create 0; global_metadata = []; module_check_policies = []; delayed = []; debug_delayed = []; doinline = com.display.dms_inline && not (Common.defined com Define.NoInline); - hook_generate = []; + retain_meta = Common.defined com Define.RetainUntypedMeta; std = null_module; global_using = []; complete = false; type_hints = []; + load_only_cached_modules = false; + functional_interface_lut = new pmap_lookup; do_inherit = MagicTypes.on_inherit; do_create = create; do_macro = MacroContext.type_macro; do_load_macro = MacroContext.load_macro'; do_load_module = TypeloadModule.load_module; do_load_type_def = Typeload.load_type_def; - do_optimize = Optimizer.reduce_expression; do_build_instance = InstanceBuilder.build_instance; do_format_string = format_string; - do_finalize = Finalization.finalize; - do_generate = Finalization.generate; do_load_core_class = Typeload.load_core_class; }; m = { curmod = null_module; - module_types = []; + module_imports = []; module_using = []; module_globals = PMap.empty; wildcard_packages = []; - module_imports = []; + import_statements = []; }; is_display_file = false; bypass_accessor = 0; meta = []; - this_stack = []; with_type_stack = []; call_argument_stack = []; pass = PBuildModule; @@ -1880,8 +2124,9 @@ let rec create com = in_function = false; in_loop = false; in_display = false; + allow_inline = true; + allow_transform = true; get_build_infos = (fun() -> None); - in_macro = Common.defined com Define.Macro; ret = mk_mono(); locals = PMap.empty; type_params = []; @@ -1896,21 +2141,20 @@ let rec create com = monomorphs = { perfunction = []; }; - on_error = (fun ctx msg p -> ctx.com.error msg p); memory_marker = Typecore.memory_marker; } in ctx.g.std <- (try TypeloadModule.load_module ctx ([],"StdTypes") null_pos with - Error (Module_not_found ([],"StdTypes"),_) -> + Error (Module_not_found ([],"StdTypes"),_,_) -> try let std_path = Sys.getenv "HAXE_STD_PATH" in - error ("Standard library not found. Please check your `HAXE_STD_PATH` environment variable (current value: \"" ^ std_path ^ "\")") null_pos + typing_error ("Standard library not found. Please check your `HAXE_STD_PATH` environment variable (current value: \"" ^ std_path ^ "\")") null_pos with Not_found -> - error "Standard library not found. You may need to set your `HAXE_STD_PATH` environment variable" null_pos + typing_error "Standard library not found. You may need to set your `HAXE_STD_PATH` environment variable" null_pos ); (* We always want core types to be available so we add them as default imports (issue #1904 and #3131). *) - ctx.m.module_types <- List.map (fun t -> t,null_pos) ctx.g.std.m_types; + ctx.m.module_imports <- List.map (fun t -> t,null_pos) ctx.g.std.m_types; List.iter (fun t -> match t with | TAbstractDecl a -> @@ -1919,7 +2163,7 @@ let rec create com = | "Float" -> ctx.t.tfloat <- TAbstract (a,[]); | "Int" -> ctx.t.tint <- TAbstract (a,[]) | "Bool" -> ctx.t.tbool <- TAbstract (a,[]) - | "Dynamic" -> t_dynamic_def := TAbstract(a,List.map snd a.a_params); + | "Dynamic" -> t_dynamic_def := TAbstract(a,extract_param_types a.a_params); | "Null" -> let mk_null t = try @@ -1974,4 +2218,4 @@ unify_min_for_type_source_ref := unify_min_for_type_source; make_call_ref := make_call; type_call_target_ref := type_call_target; type_access_ref := type_access; -type_block_ref := type_block +type_block_ref := type_block; diff --git a/src/typing/typerBase.ml b/src/typing/typerBase.ml index 55bfeb75..bfd6903a 100644 --- a/src/typing/typerBase.ml +++ b/src/typing/typerBase.ml @@ -6,9 +6,11 @@ open Error type access_kind = (* Access is not possible or allowed. *) - | AKNo of string + | AKNo of access_kind * pos (* Access on arbitrary expression. *) | AKExpr of texpr + (* Safe navigation access chain *) + | AKSafeNav of safe_nav_access (* Access on non-property field. *) | AKField of field_access (* Access on property field. The field is the property, not the accessor. *) @@ -23,22 +25,133 @@ type access_kind = (* Access on abstract via resolve method. *) | AKResolve of static_extension_access * string +and safe_nav_access = { + (* position of the safe navigation chain start (the initial ?.field expression) *) + sn_pos : pos; + (* starting value to be checked for null *) + sn_base : texpr; + (* temp var declaration to store complex base expression *) + sn_temp_var : texpr option; + (* safe navigation access to be done if the base value is not null *) + sn_access : access_kind; +} + type object_decl_kind = | ODKWithStructure of tanon | ODKWithClass of tclass * tparams | ODKPlain + | ODKFailed + +module MetaConfig = struct + + let as_bool e = match fst e with + | EConst (Ident "true") -> true + | EConst (Ident "false") -> false + | _ -> raise (Invalid_argument "bool") + + let read_arg_config meta f l = + List.iter (fun (meta',el,_) -> + if meta' = meta then + List.iter (fun e -> match fst e with + | EConst (Ident s) -> + f (s,((EConst (Ident "true"),null_pos))) + | EBinop(OpAssign,(EConst (Ident s),_),e2) -> + f (s, e2) + | _ -> + () + ) el + ) l +end + +module AbstractFromConfig = struct + type t = { + mutable ignored_by_inference : bool; + } + + let make () = { + ignored_by_inference = false; + } + + let update_config_from_meta config ml = + MetaConfig.read_arg_config Meta.From (fun (s,e) -> match s with + | "ignoredByInference" -> + begin try + config.ignored_by_inference <- MetaConfig.as_bool e + with Invalid_argument _ -> + () + end + | _ -> + () + ) ml; + config +end -let type_call_target_ref : (typer -> expr -> expr list -> WithType.t -> bool -> pos -> access_kind) ref = ref (fun _ _ _ _ _ -> die "" __LOC__) +let type_call_target_ref : (typer -> expr -> expr list -> WithType.t -> pos option -> access_kind) ref = ref (fun _ _ _ _ -> die "" __LOC__) let type_access_ref : (typer -> expr_def -> pos -> access_mode -> WithType.t -> access_kind) ref = ref (fun _ _ _ _ _ -> assert false) +class value_reference (ctx : typer) = + +object(self) + val vars = DynArray.create () + + method get_vars = DynArray.to_list vars + + method as_var name e = + let v = alloc_var VGenerated name e.etype e.epos in + DynArray.add vars (v,e); + mk (TLocal v) v.v_type v.v_pos + + method private get_expr_aux depth name e = + let rec loop depth name e = match (Texpr.skip e).eexpr with + | TLocal _ | TTypeExpr _ | TConst _ -> + e + | TField(ef,fa) when depth = 0 -> + let ef = loop (depth + 1) "fh" ef in + {e with eexpr = TField(ef,fa)} + | TArray(e1,e2) when depth = 0 -> + let e1 = loop (depth + 1) "base" e1 in + let e2 = loop (depth + 1) "index" e2 in + {e with eexpr = TArray(e1,e2)} + | _ -> + self#as_var name e + in + loop depth name e + + method get_expr name e = + self#get_expr_aux 0 name e + + method get_expr_part name e = + self#get_expr_aux 1 name e + + method to_texpr e = + begin match self#get_vars with + | [] -> + e + | vl -> + let el = List.map (fun (v,e) -> + mk (TVar(v,Some e)) ctx.t.tvoid v.v_pos + ) vl in + let e = mk (TBlock (el @ [e])) e.etype e.epos in + {e with eexpr = TMeta((Meta.MergeBlock,[],null_pos),e)} + end + + method to_texpr_el el e = + let vl = self#get_vars in + let el_vars = List.map (fun (v,e) -> + mk (TVar(v,Some e)) ctx.t.tvoid v.v_pos + ) vl in + let e = mk (TBlock (el_vars @ el @ [e])) e.etype e.epos in + {e with eexpr = TMeta((Meta.MergeBlock,[],null_pos),e)} +end + let is_lower_ident s p = try Ast.is_lower_ident s - with Invalid_argument msg -> error msg p + with Invalid_argument msg -> typing_error msg p let get_this ctx p = match ctx.curfun with | FunStatic -> - error "Cannot access this from a static function" p + typing_error "Cannot access this from a static function" p | FunMemberClassLocal | FunMemberAbstractLocal -> let v = match ctx.vthis with | None -> @@ -55,7 +168,7 @@ let get_this ctx p = in mk (TLocal v) ctx.tthis p | FunMemberAbstract -> - let v = (try PMap.find "this" ctx.locals with Not_found -> die "" __LOC__) in + let v = (try PMap.find "this" ctx.locals with Not_found -> typing_error "Cannot reference this abstract here" p) in mk (TLocal v) v.v_type p | FunConstructor | FunMember -> mk (TConst TThis) ctx.tthis p @@ -79,7 +192,7 @@ let rec type_module_type ctx t tparams p = module_type_of_type t with Exit -> if follow t == t_dynamic then Typeload.load_type_def ctx p (mk_type_path ([],"Dynamic")) - else error "Invalid module type" p + else typing_error "Invalid module type" p in type_module_type ctx mt None p | TClassDecl c -> @@ -89,7 +202,7 @@ let rec type_module_type ctx t tparams p = let types = (match tparams with None -> Monomorph.spawn_constrained_monos (fun t -> t) e.e_params | Some l -> l) in mk (TTypeExpr (TEnumDecl e)) (TType (e.e_type,types)) p | TTypeDecl s -> - let t = apply_params s.t_params (List.map (fun _ -> spawn_monomorph ctx p) s.t_params) s.t_type in + let t = apply_typedef s (List.map (fun _ -> spawn_monomorph ctx p) s.t_params) in DeprecationCheck.check_typedef ctx.com s p; (match follow t with | TEnum (e,params) -> @@ -99,11 +212,11 @@ let rec type_module_type ctx t tparams p = | TAbstract (a,params) -> type_module_type ctx (TAbstractDecl a) (Some params) p | _ -> - error (s_type_path s.t_path ^ " is not a value") p) + typing_error (s_type_path s.t_path ^ " is not a value") p) | TAbstractDecl { a_impl = Some c } -> type_module_type ctx (TClassDecl c) tparams p | TAbstractDecl a -> - if not (Meta.has Meta.RuntimeValue a.a_meta) then error (s_type_path a.a_path ^ " is not a value") p; + if not (Meta.has Meta.RuntimeValue a.a_meta) then typing_error (s_type_path a.a_path ^ " is not a value") p; let t_tmp = abstract_module_type a [] in mk (TTypeExpr (TAbstractDecl a)) (TType (t_tmp,[])) p @@ -126,7 +239,8 @@ let s_field_access tabs fa = "fa_on",se fa.fa_on; "fa_field",fa.fa_field.cf_name; "fa_host",sfa fa.fa_host; - "fa_inline",string_of_bool fa.fa_inline + "fa_inline",string_of_bool fa.fa_inline; + "fa_pos",(Printf.sprintf "%s(%i-%i)" fa.fa_pos.pfile fa.fa_pos.pmin fa.fa_pos.pmax); ] let s_static_extension_access sea = @@ -135,12 +249,13 @@ let s_static_extension_access sea = "se_access",s_field_access "\t" sea.se_access ] -let s_access_kind acc = +let rec s_access_kind acc = let st = s_type (print_context()) in let se = s_expr_pretty true "" false st in match acc with - | AKNo s -> "AKNo " ^ s + | AKNo(acc,_) -> "AKNo " ^ (s_access_kind acc) | AKExpr e -> "AKExpr " ^ (se e) + | AKSafeNav sn -> Printf.sprintf "AKSafeNav(%s)" (s_safe_nav_access sn) | AKField fa -> Printf.sprintf "AKField(%s)" (s_field_access "" fa) | AKAccessor fa -> Printf.sprintf "AKAccessor(%s)" (s_field_access "" fa) | AKUsingField sea -> Printf.sprintf "AKUsingField(%s)" (s_static_extension_access sea) @@ -148,10 +263,26 @@ let s_access_kind acc = | AKAccess(a,tl,c,e1,e2) -> Printf.sprintf "AKAccess(%s, [%s], %s, %s, %s)" (s_type_path a.a_path) (String.concat ", " (List.map st tl)) (s_type_path c.cl_path) (se e1) (se e2) | AKResolve(_) -> "" +and s_safe_nav_access sn = + let st = s_type (print_context()) in + let se = s_expr_pretty true "" false st in + Printer.s_record_fields "" [ + "sn_base",se sn.sn_base; + "sn_temp_var",Option.map_default (fun e -> "Some " ^ (se e)) "None" sn.sn_temp_var; + "sn_access",s_access_kind sn.sn_access + ] + +let s_dot_path_part part = + Printer.s_record_fields "" [ + "name",part.name; + "case",(match part.case with PUppercase -> "PUppercase" | PLowercase -> "PLowercase"); + "pos",(Printf.sprintf "%s(%i-%i)" part.pos.pfile part.pos.pmin part.pos.pmax); + ] + let get_constructible_constraint ctx tl p = let extract_function t = match follow t with | TFun(tl,tr) -> tl,tr - | _ -> error "Constructible type parameter should be function" p + | _ -> typing_error "Constructible type parameter should be function" p in let rec loop tl = match tl with | [] -> None @@ -191,10 +322,15 @@ let unify_static_extension ctx e t p = e end -let get_abstract_froms a pl = +let get_abstract_froms ctx a pl = let l = List.map (apply_params a.a_params pl) a.a_from in List.fold_left (fun acc (t,f) -> - match follow (Type.field_type f) with + (* We never want to use the @:from we're currently in because that's recursive (see #10604) *) + if f == ctx.curfield then + acc + else if (AbstractFromConfig.update_config_from_meta (AbstractFromConfig.make ()) f.cf_meta).ignored_by_inference then + acc + else match follow (Type.field_type f) with | TFun ([_,_,v],t) -> (try ignore(type_eq EqStrict t (TAbstract(a,List.map duplicate pl))); (* unify fields monomorphs *) diff --git a/src/typing/typerDisplay.ml b/src/typing/typerDisplay.ml index aa5d2f5a..20fa84fd 100644 --- a/src/typing/typerDisplay.ml +++ b/src/typing/typerDisplay.ml @@ -48,10 +48,21 @@ let completion_item_of_expr ctx e = let t = tpair e.etype in make_ci_expr e t in + let was_probably_literal_abstract v t = match follow t with + | TAbstract(a,tl) -> + type_iseq (Abstract.get_underlying_type a tl) v.v_type + | _ -> + false + in let rec loop e = match e.eexpr with + | TLocal ({v_kind = VAbstractThis} as v) when was_probably_literal_abstract v e.etype -> + (* `abstract` is typed as ((this : Underlying) : Abstract), so we detect it like that. + This could lead to false positives if somebody really tries, but even in that case + the displayed type is not wrong. *) + make_ci_literal "abstract" (tpair e.etype) | TLocal v | TVar(v,_) -> make_ci_local v (tpair ~values:(get_value_meta v.v_meta) v.v_type) | TField(e1,FStatic(c,cf)) -> - let te,c,cf = DisplayToplevel.maybe_resolve_macro_field ctx e.etype c cf in + let te,cf = DisplayToplevel.maybe_resolve_macro_field ctx e.etype c cf in Display.merge_core_doc ctx (TClassDecl c); let decl = decl_of_class c in let origin = match c.cl_kind,e1.eexpr with @@ -65,7 +76,7 @@ let completion_item_of_expr ctx e = in of_field {e with etype = te} origin cf CFSStatic make_ci | TField(e1,(FInstance(c,_,cf) | FClosure(Some(c,_),cf))) -> - let te,c,cf = DisplayToplevel.maybe_resolve_macro_field ctx e.etype c cf in + let te,cf = DisplayToplevel.maybe_resolve_macro_field ctx e.etype c cf in Display.merge_core_doc ctx (TClassDecl c); let origin = match follow e1.etype with | TInst(c',_) when c != c' -> @@ -87,7 +98,7 @@ let completion_item_of_expr ctx e = end | TTypeExpr (TClassDecl {cl_kind = KAbstractImpl a}) -> Display.merge_core_doc ctx (TAbstractDecl a); - let t = TType(abstract_module_type a (List.map snd a.a_params),[]) in + let t = TType(abstract_module_type a (extract_param_types a.a_params),[]) in let t = tpair t in make_ci_type (CompletionModuleType.of_module_type (TAbstractDecl a)) ImportStatus.Imported (Some t) | TTypeExpr mt -> @@ -179,7 +190,7 @@ let display_dollar_type ctx p make_type = | DMDefinition | DMTypeDefinition -> raise_positions [] | _ -> - error "Unsupported method" p + typing_error "Unsupported method" p end let rec handle_signature_display ctx e_ast with_type = @@ -189,7 +200,7 @@ let rec handle_signature_display ctx e_ast with_type = let rec follow_with_callable (t,doc,values) = match follow t with | TAbstract(a,tl) when Meta.has Meta.Callable a.a_meta -> follow_with_callable (Abstract.get_underlying_type a tl,doc,values) | TFun(args,ret) -> ((args,ret),doc,values) - | _ -> error ("Not a callable type: " ^ (s_type (print_context()) t)) p + | _ -> typing_error ("Not a callable type: " ^ (s_type (print_context()) t)) p in let tl = List.map follow_with_callable tl in let rec loop i acc el = match el with @@ -212,7 +223,7 @@ let rec handle_signature_display ctx e_ast with_type = let _ = unify_call_args ctx el args r p false false false in true with - | Error(Call_error (Not_enough_arguments _),_) -> true + | Error(Call_error (Not_enough_arguments _),_,_) -> true | _ -> false end in @@ -224,10 +235,25 @@ let rec handle_signature_display ctx e_ast with_type = let overloads = List.map (fun (t,doc,values) -> (convert_function_signature ctx values t,doc)) overloads in raise_signatures overloads 0 (* ? *) display_arg SKCall in + let process_overloads stat co (map,cf,t) = + let is_wacky_overload = not (has_class_field_flag cf CfOverload) in + let can_access cf = match co with + | Some c -> can_access ctx c cf stat + | None -> true + in + let l = (t,cf) :: List.rev_map (fun cf -> map cf.cf_type,cf) cf.cf_overloads in + let l = List.filter (fun (_,cf) -> can_access cf) l in + let l = List.map (fun (t,cf') -> + (* Ghetto overloads have their documentation on the main field. *) + let doc = if is_wacky_overload then cf.cf_doc else cf'.cf_doc in + (t,doc,get_value_meta cf.cf_meta) + ) l in + l + in let find_constructor_types t = match follow t with | TInst ({cl_kind = KTypeParameter tl} as c,_) -> let rec loop tl = match tl with - | [] -> raise_error (No_constructor (TClassDecl c)) p + | [] -> raise_typing_error (No_constructor (TClassDecl c)) p | t :: tl -> match follow t with | TAbstract({a_path = ["haxe"],"Constructible"},[t]) -> t | _ -> loop tl @@ -236,14 +262,9 @@ let rec handle_signature_display ctx e_ast with_type = | TInst (c,tl) | TAbstract({a_impl = Some c},tl) -> Display.merge_core_doc ctx (TClassDecl c); let fa = get_constructor_access c tl p in - let is_wacky_overload = not (has_class_field_flag fa.fa_field CfOverload) in let map = FieldAccess.get_map_function fa in - let map_cf cf = - (* Ghetto overloads have their documentation on the main field. *) - let doc = if is_wacky_overload then fa.fa_field.cf_doc else cf.cf_doc in - map cf.cf_type,doc,get_value_meta cf.cf_meta - in - List.map map_cf (fa.fa_field :: fa.fa_field.cf_overloads) + let cf = fa.fa_field in + process_overloads false (Some c) (map,cf,cf.cf_type) | _ -> [] in @@ -251,16 +272,16 @@ let rec handle_signature_display ctx e_ast with_type = | ECall(e1,el) -> let def () = try - acc_get ctx (!type_call_target_ref ctx e1 el with_type false (pos e1)) (pos e1) + acc_get ctx (!type_call_target_ref ctx e1 el with_type None) with - | Error (Unknown_ident "trace",_) -> + | Error (Unknown_ident "trace",_,_) -> let e = expr_of_type_path (["haxe";"Log"],"trace") p in type_expr ctx e WithType.value - | Error (Unknown_ident "$type",p) -> + | Error (Unknown_ident "$type",p,_) -> display_dollar_type ctx p (fun t -> t,(CompletionType.from_type (get_import_status ctx) t)) in let e1 = match e1 with - | (EField (e,"bind"),p) -> + | (EField (e,"bind",_),p) -> let e = type_expr ctx e WithType.value in (match follow e.etype with | TFun signature -> e @@ -269,13 +290,17 @@ let rec handle_signature_display ctx e_ast with_type = in let tl = match e1.eexpr with | TField(_,fa) -> - let f (t,_,cf) = - (t,cf.cf_doc,get_value_meta cf.cf_meta) :: List.rev_map (fun cf' -> cf'.cf_type,cf.cf_doc,get_value_meta cf'.cf_meta) cf.cf_overloads - in begin match fa with - | FStatic(c,cf) | FInstance(c,_,cf) -> f (DisplayToplevel.maybe_resolve_macro_field ctx e1.etype c cf) - | FAnon cf | FClosure(_,cf) -> f (e1.etype,null_class,cf) - | _ -> [e1.etype,None,PMap.empty] + | FStatic(c,cf) -> + let t,cf = DisplayToplevel.maybe_resolve_macro_field ctx e1.etype c cf in + process_overloads true (Some c) ((fun t -> t),cf,t) + | FInstance(c,tl,cf) | FClosure(Some(c,tl),cf) -> + let t,cf = DisplayToplevel.maybe_resolve_macro_field ctx e1.etype c cf in + process_overloads false (Some c) (TClass.get_map_function c tl,cf,t) + | FAnon cf | FClosure(None,cf) -> + process_overloads false None ((fun t -> t),cf,e1.etype) + | _ -> + [e1.etype,None,PMap.empty] end; | TConst TSuper -> find_constructor_types e1.etype @@ -312,11 +337,11 @@ let rec handle_signature_display ctx e_ast with_type = | _ -> raise_signatures [] 0 0 SKArrayAccess end - | _ -> error "Call expected" p + | _ -> typing_error "Call expected" p and display_expr ctx e_ast e dk mode with_type p = let get_super_constructor () = match ctx.curclass.cl_super with - | None -> error "Current class does not have a super" p + | None -> typing_error "Current class does not have a super" p | Some (c,params) -> let fa = get_constructor_access c params p in fa.fa_field,c @@ -338,7 +363,7 @@ and display_expr ctx e_ast e dk mode with_type p = | _ -> e in match ctx.com.display.dms_kind with - | DMResolve _ | DMPackage -> + | DMPackage -> die "" __LOC__ | DMSignature -> handle_signature_display ctx e_ast with_type @@ -352,7 +377,7 @@ and display_expr ctx e_ast e dk mode with_type p = | TField(_,(FAnon cf | FClosure (None,cf))) -> Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField (cf,None)); | TField(_,(FInstance (c,_,cf) | FStatic (c,cf) | FClosure (Some (c,_),cf))) -> - Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField (cf,Some c.cl_path)); + Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField (cf,Some c)); | TLocal v | TVar(v,_) -> Display.ReferencePosition.set (v.v_name,v.v_pos,SKVariable v); | TTypeExpr mt -> @@ -369,7 +394,7 @@ and display_expr ctx e_ast e dk mode with_type p = | TCall({eexpr = TConst TSuper},_) -> begin try let cf,c = get_super_constructor() in - Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField (cf,Some c.cl_path)); + Display.ReferencePosition.set (cf.cf_name,cf.cf_name_pos,SKField (cf,Some c)); with Not_found -> () end @@ -450,7 +475,7 @@ and display_expr ctx e_ast e dk mode with_type p = raise_fields fields (CRField(item,e1.epos,None,None)) (make_subject so ~start_pos:(Some (pos e_ast)) {e.epos with pmin = e.epos.pmax - l;}) in begin match fst e_ast,e.eexpr with - | EField(e1,s),TField(e2,_) -> + | EField(e1,s,_),TField(e2,_) -> display_fields e1 e2 (Some s) | EObjectDecl [(name,pn,_),(EConst (Ident "null"),pe)],_ when pe.pmin = -1 -> (* This is what the parser emits for #8651. Bit of a dodgy heuristic but should be fine. *) @@ -465,7 +490,7 @@ and display_expr ctx e_ast e dk mode with_type p = raise_toplevel ctx dk with_type (name,p) end end - | DMDefault | DMNone | DMModuleSymbols _ | DMDiagnostics _ | DMStatistics -> + | DMDefault | DMNone | DMModuleSymbols _ -> let fields = DisplayFields.collect ctx e_ast e dk with_type p in let item = completion_item_of_expr ctx e in let iterator = try @@ -516,23 +541,23 @@ let handle_display ctx e_ast dk mode with_type = | DMDefinition | DMTypeDefinition -> raise_positions [] | _ -> - error "Unsupported method" p + typing_error "Unsupported method" p end | (EConst (Ident "_"),p),WithType.WithType(t,_) -> mk (TConst TNull) t p (* This is "probably" a bind skip, let's just use the expected type *) | (_,p),_ -> try type_expr ~mode ctx e_ast with_type - with Error (Unknown_ident n,_) when ctx.com.display.dms_kind = DMDefault -> + with Error (Unknown_ident n,_,_) when ctx.com.display.dms_kind = DMDefault -> if dk = DKDot && is_legacy_completion ctx.com then raise (Parser.TypePath ([n],None,false,p)) else raise_toplevel ctx dk with_type (n,p) - | Error ((Type_not_found (path,_,_) | Module_not_found path),_) as err when ctx.com.display.dms_kind = DMDefault -> + | Error ((Type_not_found (path,_,_) | Module_not_found path),_,_) as err when ctx.com.display.dms_kind = DMDefault -> if is_legacy_completion ctx.com then begin try raise_fields (DisplayFields.get_submodule_fields ctx path) (CRField((make_ci_module path),p,None,None)) (make_subject None (pos e_ast)) with Not_found -> raise err end else raise_toplevel ctx dk with_type (s_type_path path,p) - | DisplayException(DisplayFields Some({fkind = CRTypeHint} as r)) when (match fst e_ast with ENew _ -> true | _ -> false) -> + | DisplayException(DisplayFields ({fkind = CRTypeHint} as r)) when (match fst e_ast with ENew _ -> true | _ -> false) -> let timer = Timer.timer ["display";"toplevel";"filter ctors"] in ctx.pass <- PBuildClass; let l = List.filter (fun item -> @@ -569,7 +594,7 @@ let handle_display ctx e_ast dk mode with_type = let mt = ctx.g.do_load_type_def ctx null_pos {tpackage=mt.pack;tname=mt.module_name;tsub=Some mt.name;tparams=[]} in begin match resolve_typedef mt with | TClassDecl c -> has_constructor c - | TAbstractDecl a -> (match Abstract.follow_with_forward_ctor ~build:true (TAbstract(a,List.map snd a.a_params)) with + | TAbstractDecl a -> (match Abstract.follow_with_forward_ctor ~build:true (TAbstract(a,extract_param_types a.a_params)) with | TInst(c,_) -> has_constructor c | TAbstract({a_impl = Some c},_) -> PMap.mem "_new" c.cl_statics | _ -> false) @@ -588,7 +613,7 @@ let handle_display ctx e_ast dk mode with_type = in let e = match e_ast, e.eexpr with | _, TField(e1,FDynamic "bind") when (match follow e1.etype with TFun _ -> true | _ -> false) -> e1 - | (EField(_,"new"),_), TFunction { tf_expr = { eexpr = TReturn (Some ({ eexpr = TNew _ } as e1))} } -> e1 + | (EField(_,"new",_),_), TFunction { tf_expr = { eexpr = TReturn (Some ({ eexpr = TNew _ } as e1))} } -> e1 | _ -> e in let is_display_debug = Meta.has (Meta.Custom ":debug.display") ctx.curfield.cf_meta in @@ -601,7 +626,7 @@ let handle_display ctx e_ast dk mode with_type = | WithType.WithType(t,_) -> (* We don't want to actually use the transformed expression which may have inserted implicit cast calls. It only matters that unification takes place. *) - (try ignore(AbstractCast.cast_or_unify_raise ctx t e e.epos) with Error (Unify l,p) -> ()); + (try ignore(AbstractCast.cast_or_unify_raise ctx t e e.epos) with Error (Unify l,p,_) -> ()); | _ -> () end; @@ -637,18 +662,24 @@ let handle_structure_display ctx e fields origin = let rec loop subj fl = match fl with | [] -> subj | ((n,p,_),e) :: fl -> + let wt () = + try + let cf = List.find (fun { cf_name = name } -> name = n) !fields in + WithType.with_type cf.cf_type + with Not_found -> WithType.value + in let subj = if DisplayPosition.display_position#enclosed_in p then Some(n,p) else begin - if DisplayPosition.display_position#enclosed_in ({ (pos e) with pmin = p.pmax + 1 }) then begin - let e = fst e, { (pos e) with pmin = p.pmax + 1 } in - let wt = - try - let cf = List.find (fun { cf_name = name } -> name = n) !fields in - WithType.with_type cf.cf_type - with Not_found -> WithType.value - in - ignore(handle_display ctx e DKMarked MGet wt) + if DisplayPosition.display_position#enclosed_in (pos e) then + ignore(handle_display ctx e DKMarked MGet (wt())) + else begin + (* If we are between the : and the expression, we don't want to use the actual expression as a filter string (issue #10414) *) + let p_between = { p with pmin = p.pmax + 1; pmax = (pos e).pmin - 1} in + if DisplayPosition.display_position#enclosed_in p_between then begin + let e = (EConst(Ident "null"),p_between) in + ignore(handle_display ctx e DKMarked MGet (wt())) + end; end; fields := List.filter (fun cf -> cf.cf_name <> n) !fields; subj @@ -669,7 +700,7 @@ let handle_structure_display ctx e fields origin = let pinsert = DisplayPosition.display_position#with_pos (pos e) in raise_fields fields CRStructureField (make_subject None pinsert) | _ -> - error "Expected object expression" p + typing_error "Expected object expression" p let handle_edisplay ctx e dk mode with_type = let handle_display ctx e dk with_type = @@ -698,7 +729,7 @@ let handle_edisplay ctx e dk mode with_type = | DKPattern outermost,DMDefault -> begin try handle_display ctx e dk with_type - with DisplayException(DisplayFields Some({fkind = CRToplevel _} as r)) -> + with DisplayException(DisplayFields ({fkind = CRToplevel _} as r)) -> raise_fields r.fitems (CRPattern ((get_expected_type ctx with_type),outermost)) r.fsubject end - | _ -> handle_display ctx e dk with_type \ No newline at end of file + | _ -> handle_display ctx e dk with_type diff --git a/src/typing/typerDotPath.ml b/src/typing/typerDotPath.ml index e5b8c462..e42c3221 100644 --- a/src/typing/typerDotPath.ml +++ b/src/typing/typerDotPath.ml @@ -26,15 +26,13 @@ open Fields open TFunctions open Error -type dot_path_part_case = - | PUppercase - | PLowercase - -type dot_path_part = (string * dot_path_part_case * pos) - let mk_dot_path_part s p : dot_path_part = let case = if is_lower_ident s p then PLowercase else PUppercase in - (s,case,p) + { + name = s; + case = case; + pos = p + } let s_dot_path parts = String.concat "." (List.map (fun (s,_,_) -> s) parts) @@ -44,7 +42,7 @@ let resolve_module_field ctx m path p mode with_type = match path, m.m_statics with | [], _ | _, None -> raise Not_found - | (name,_,p) :: path_rest, Some c -> + | {name = name; pos = p} :: path_rest, Some c -> let f = PMap.find name c.cl_statics in (* raises Not_found *) let e = type_module_type ctx (TClassDecl c) None p in field_access ctx mode f (FHStatic c) e p, path_rest @@ -61,7 +59,7 @@ let resolve_in_module ctx m path p mode with_type = (* if there was no module fields, resolve *) let mname = snd m.m_path in match path with - | (sname,PUppercase,sp) :: path_rest -> + | {name = sname; case = PUppercase; pos = sp} :: path_rest -> begin try resolve_module_type ctx m sname sp, path_rest @@ -76,7 +74,7 @@ let resolve_qualified ctx pack name next_path p mode with_type = try let m = Typeload.load_module ctx (pack,name) p in resolve_in_module ctx m next_path p mode with_type - with Error (Module_not_found mpath,_) when mpath = (pack,name) -> + with Error (Module_not_found mpath,_,_) when mpath = (pack,name) -> (* might be an instance of https://github.com/HaxeFoundation/haxe/issues/9150 so let's also check (pack,name) of a TYPE in the current module context ¯\_(ツ)_/¯ *) let t = Typeload.find_type_in_current_module_context ctx pack name in (* raises Not_found *) @@ -94,7 +92,7 @@ let resolve_unqualified ctx name next_path p mode with_type = so we try resolving a field first and fall back to find_in_unqualified_modules *) match next_path with - | (field,_,pfield) :: next_path -> + | {name = field; pos = pfield} :: next_path -> let e = type_module_type ctx t None p in let access = type_field (TypeFieldConfig.create true) ctx e field pfield mode with_type in access, next_path @@ -110,11 +108,11 @@ let resolve_unqualified ctx name next_path p mode with_type = let resolve_dot_path ctx (path_parts : dot_path_part list) mode with_type = let rec loop pack_acc path = match path with - | (_,PLowercase,_) as x :: path -> + | {case = PLowercase} as x :: path -> (* part starts with lowercase - it's a package part, add it the accumulator and proceed *) loop (x :: pack_acc) path - | (name,PUppercase,p) :: path -> + | {name = name; case = PUppercase; pos = p} :: path -> (* If this is the last part we want to use the actual mode. *) let mode,with_type = match path with | [] | [_] -> mode,with_type @@ -123,7 +121,7 @@ let resolve_dot_path ctx (path_parts : dot_path_part list) mode with_type = (* part starts with uppercase - it's a module name - try resolving *) let accessor, path_rest = if pack_acc <> [] then - let pack = List.rev_map (fun (x,_,_) -> x) pack_acc in + let pack = List.rev_map (fun part -> part.name) pack_acc in resolve_qualified ctx pack name path p mode with_type else resolve_unqualified ctx name path p mode with_type diff --git a/std/Any.hx b/std/Any.hx index 36991709..171cbb3c 100644 --- a/std/Any.hx +++ b/std/Any.hx @@ -32,13 +32,10 @@ to another type. **/ @:forward.variance -abstract Any(Dynamic) { +abstract Any(Dynamic) from Dynamic { @:noCompletion @:to extern inline function __promote():T return this; - @:noCompletion @:from extern inline static function __cast(value:T):Any - return cast value; - @:noCompletion extern inline function toString():String return Std.string(this); } diff --git a/std/Math.hx b/std/Math.hx index 4dd349c0..38721677 100644 --- a/std/Math.hx +++ b/std/Math.hx @@ -276,10 +276,10 @@ extern class Math { static function isFinite(f:Float):Bool; /** - Tells if `f` is not a valid number. + Tells if `f` is `Math.NaN`. If `f` is `NaN`, the result is `true`, otherwise the result is `false`. - In particular, both `POSITIVE_INFINITY` and `NEGATIVE_INFINITY` are + In particular, `null`, `POSITIVE_INFINITY` and `NEGATIVE_INFINITY` are not considered `NaN`. **/ static function isNaN(f:Float):Bool; diff --git a/std/Std.hx b/std/Std.hx index 9d7dd90e..98a07a9b 100644 --- a/std/Std.hx +++ b/std/Std.hx @@ -97,21 +97,22 @@ extern class Std { Leading whitespaces are ignored. - If `x` starts with 0x or 0X, hexadecimal notation is recognized where the following digits may - contain 0-9 and A-F. + `x` may optionally start with a + or - to denote a postive or negative value respectively. - Otherwise `x` is read as decimal number with 0-9 being allowed characters. `x` may also start with - a - to denote a negative value. + If the optional sign is followed 0x or 0X, hexadecimal notation is recognized where the following + digits may contain 0-9 and A-F. Both the prefix and digits are case insensitive. - In decimal mode, parsing continues until an invalid character is detected, in which case the - result up to that point is returned. For hexadecimal notation, the effect of invalid characters - is unspecified. + Otherwise `x` is read as decimal number with 0-9 being allowed characters. Octal and binary + notations are not supported. + + Parsing continues until an invalid character is detected, in which case the result up to + that point is returned. Scientific notation is not supported. That is `Std.parseInt('10e2')` produces `10`. - Leading 0s that are not part of the 0x/0X hexadecimal notation are ignored, which means octal - notation is not supported. + If `x` is `null`, the result is `null`. + If `x` cannot be parsed as integer or is empty, the result is `null`. - If `x` is null, the result is unspecified. - If `x` cannot be parsed as integer, the result is `null`. + If `x` starts with a hexadecimal prefix which is not followed by at least one valid hexadecimal + digit, the result is unspecified. **/ static function parseInt(x:String):Null; @@ -119,9 +120,12 @@ extern class Std { Converts a `String` to a `Float`. The parsing rules for `parseInt` apply here as well, with the exception of invalid input - resulting in a `NaN` value instead of null. + resulting in a `NaN` value instead of `null`. Also, hexadecimal support is **not** specified. Additionally, decimal notation may contain a single `.` to denote the start of the fractions. + + It may also end with `e` or `E` followed by optional minus or plus sign and a sequence of + digits (defines exponent to base 10). **/ static function parseFloat(x:String):Float; diff --git a/std/StringTools.hx b/std/StringTools.hx index ce1a79fb..1369b56b 100644 --- a/std/StringTools.hx +++ b/std/StringTools.hx @@ -211,7 +211,7 @@ class StringTools { public static inline function contains(s:String, value:String):Bool { #if (js && js_es >= 6) return (cast s).includes(value); - #else + #else return s.indexOf(value) != -1; #end } @@ -234,6 +234,8 @@ class StringTools { return python.NativeStringTools.startswith(s, start); #elseif (js && js_es >= 6) return (cast s).startsWith(start); + #elseif lua + return untyped __lua__("{0}:sub(1, #{1}) == {1}", s, start); #else return (s.length >= start.length && s.lastIndexOf(start, 0) == 0); #end @@ -259,6 +261,8 @@ class StringTools { return python.NativeStringTools.endswith(s, end); #elseif (js && js_es >= 6) return (cast s).endsWith(end); + #elseif lua + return end == "" || untyped __lua__("{0}:sub(-#{1}) == {1}", s, end); #else var elen = end.length; var slen = s.length; diff --git a/std/Sys.hx b/std/Sys.hx index fe378142..3683b272 100644 --- a/std/Sys.hx +++ b/std/Sys.hx @@ -57,12 +57,24 @@ extern class Sys { /** Sets the value of the given environment variable. + If `v` is `null`, the environment variable is removed. + (java) This functionality is not available on Java; calling this function will throw. **/ - static function putEnv(s:String, v:String):Void; + static function putEnv(s:String, v:Null):Void; /** - Returns all environment variables. + Returns a map of the current environment variables and their values + as of the invocation of the function. + + (python) On Windows, the variable names are always in upper case. + + (cpp)(hl)(neko) On Windows, the variable names match the last capitalization used when modifying + the variable if the variable has been modified, otherwise they match their capitalization at + the start of the process. + + On Windows on remaining targets, variable name capitalization matches however they were capitalized + at the start of the process or at the moment of their creation. **/ static function environment():Map; diff --git a/std/UnicodeString.hx b/std/UnicodeString.hx index 7ac030e0..0b5a0a98 100644 --- a/std/UnicodeString.hx +++ b/std/UnicodeString.hx @@ -26,9 +26,14 @@ import haxe.iterators.StringIteratorUnicode; import haxe.iterators.StringKeyValueIteratorUnicode; /** - This abstract provides consistent cross-target unicode support. + This abstract provides consistent cross-target unicode support for characters of any width. - @see https://haxe.org/manual/std-UnicodeString.html + Due to differing internal representations of strings across targets, only the basic + multilingual plane (BMP) is supported consistently by `String` class. + + This abstract provides API to consistently handle all characters even beyond BMP. + + @see https://haxe.org/manual/std-String-unicode.html **/ @:forward @:access(StringTools) diff --git a/std/cpp/Int64.hx b/std/cpp/Int64.hx index 299f70c3..0b76d3c4 100644 --- a/std/cpp/Int64.hx +++ b/std/cpp/Int64.hx @@ -22,7 +22,21 @@ package cpp; -@:coreType @:notNull @:runtimeValue abstract Int64 from Int to Int { +@:coreType @:notNull @:runtimeValue abstract Int64 from Int { + + /** + Destructively cast to Int + **/ + public inline function toInt():Int { + return cast this; + } + + @:to + @:deprecated("Implicit cast from Int64 to Int (32 bits) is deprecated. Use .toInt() or explicitly cast instead.") + inline function implicitToInt(): Int { + return toInt(); + } + @:to #if !cppia inline #end function toInt64():haxe.Int64 { return cast this; diff --git a/std/cpp/Int64Map.hx b/std/cpp/Int64Map.hx new file mode 100644 index 00000000..62e4b64b --- /dev/null +++ b/std/cpp/Int64Map.hx @@ -0,0 +1,154 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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. + */ + +package cpp; + +import haxe.Int64; + +@:headerClassCode(" + inline void set(cpp::Int64 key, ::null value) { __int64_hash_set(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, bool value) { __int64_hash_set(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, char value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, unsigned char value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, signed char value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, short value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, unsigned short value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, int value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, unsigned int value) { __int64_hash_set_int(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, float value) { __int64_hash_set_float(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, double value) { __int64_hash_set_float(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, ::String value) { __int64_hash_set_string(HX_MAP_THIS,key,value); } + inline void set(cpp::Int64 key, cpp::Int64 value) { __int64_hash_set_int64(HX_MAP_THIS,key,value); } + + template + inline void set(cpp::Int64 key, const ::cpp::Struct &value) {__int64_hash_set(HX_MAP_THIS,key,value); } + template + inline void set(cpp::Int64 key, const ::cpp::Function &value) {__int64_hash_set(HX_MAP_THIS,key,value); } + template + inline void set(cpp::Int64 key, const ::cpp::Pointer &value) {__int64_hash_set(HX_MAP_THIS,key,(Dynamic)value ); } + + template + inline void set(Dynamic &key, const VALUE &value) { set( (cpp::Int64)key, value ); } + + inline bool get_bool(cpp::Int64 key) { return __int64_hash_get_bool(h,key); } + inline int get_int(cpp::Int64 key) { return __int64_hash_get_int(h,key); } + inline Float get_float(cpp::Int64 key) { return __int64_hash_get_float(h,key); } + inline String get_string(cpp::Int64 key) { return __int64_hash_get_string(h,key); } + inline cpp::Int64 get_int64(cpp::Int64 key) { return __int64_hash_get_int64(h,key); } +") +@:coreApi class Int64Map implements haxe.Constraints.IMap { + @:ifFeature("cpp.Int64Map.*") + private var h:Dynamic; + + public function new():Void {} + + public function set(key:Int64, value:T):Void { + untyped __global__.__int64_hash_set(__cpp__("HX_MAP_THIS"), key, value); + } + + public function get(key:Int64):Null { + return untyped __global__.__int64_hash_get(h, key); + } + + public function exists(key:Int64):Bool { + return untyped __global__.__int64_hash_exists(h, key); + } + + public function remove(key:Int64):Bool { + return untyped __global__.__int64_hash_remove(h, key); + } + + public function keys():Iterator { + var a:Array = untyped __global__.__int64_hash_keys(h); + return a.iterator(); + } + + public function iterator():Iterator { + var a:Array = untyped __global__.__int64_hash_values(h); + return a.iterator(); + } + + @:runtime public inline function keyValueIterator():KeyValueIterator { + return new haxe.iterators.MapKeyValueIterator(this); + } + + public function copy():Int64Map { + var copied = new Int64Map(); + for (key in keys()) + copied.set(key, get(key)); + return copied; + } + + public function toString():String { + return untyped __global__.__int64_hash_to_string(h); + } + + public function clear():Void { + #if (hxcpp_api_level >= 400) + return untyped __global__.__int64_hash_clear(h); + #else + h = null; + #end + } + + #if (scriptable) + private function setString(key:Int64, val:String):Void { + untyped __int64_hash_set_string(__cpp__("HX_MAP_THIS"), key, val); + } + + private function setInt(key:Int64, val:Int):Void { + untyped __int64_hash_set_int(__cpp__("HX_MAP_THIS"), key, val); + } + + private function setBool(key:Int64, val:Bool):Void { + untyped __int64_hash_set_int(__cpp__("HX_MAP_THIS"), key, val); + } + + private function setFloat(key:Int64, val:Float):Void { + untyped __int64_hash_set_float(__cpp__("HX_MAP_THIS"), key, val); + } + + private function setInt64(key:Int64, val:Int64):Void { + untyped __int64_hash_set_int64(__cpp__("HX_MAP_THIS"), key, val); + } + + private function getString(key:Int64):String { + return untyped __int64_hash_get_string(h, key); + } + + private function getInt(key:Int64):Int { + return untyped __int64_hash_get_int(h, key); + } + + private function getBool(key:Int64):Bool { + return untyped __int64_hash_get_bool(h, key); + } + + private function getFloat(key:Int64):Float { + return untyped __int64_hash_get_float(h, key); + } + + private function getInt64(key:Int64):Int64 { + return untyped __int64_hash_get_int64(h, key); + } + #end +} diff --git a/std/cpp/Lib.hx b/std/cpp/Lib.hx index 75940177..a81d402a 100644 --- a/std/cpp/Lib.hx +++ b/std/cpp/Lib.hx @@ -79,7 +79,7 @@ class Lib { } @:noDebug @:native("HX_STACK_DO_RETHROW") - extern static function do_rethrow(inExp:Dynamic); + extern static function do_rethrow(inExp:Dynamic):Void; @:noDebug #if (!cppia) inline #end public static function rethrow(inExp:Dynamic) { diff --git a/std/cpp/NativeProcess.hx b/std/cpp/NativeProcess.hx index 4ad46547..3add92c9 100644 --- a/std/cpp/NativeProcess.hx +++ b/std/cpp/NativeProcess.hx @@ -43,7 +43,7 @@ extern class NativeProcess { static function process_stdin_close(handle:Dynamic):Void; @:native("_hx_std_process_exit") - static function process_exit(handle:Dynamic):Int; + static function process_exit(handle:Dynamic, block:Bool):Dynamic; @:native("_hx_std_process_pid") static function process_pid(handle:Dynamic):Int; diff --git a/std/cpp/NativeSys.hx b/std/cpp/NativeSys.hx index cd5851ec..5390568d 100644 --- a/std/cpp/NativeSys.hx +++ b/std/cpp/NativeSys.hx @@ -34,7 +34,7 @@ extern class NativeSys { extern static function get_env(v:String):String; @:native("_hx_std_put_env") - extern static function put_env(e:String, v:String):Void; + extern static function put_env(e:String, v:Null):Void; @:native("_hx_std_sys_sleep") extern static function sys_sleep(f:Float):Void; diff --git a/std/cpp/NativeXml.hx b/std/cpp/NativeXml.hx index a45aa888..ee3ddc2c 100644 --- a/std/cpp/NativeXml.hx +++ b/std/cpp/NativeXml.hx @@ -221,7 +221,7 @@ class Xml { function new():Void {} @:native("parse_xml") - extern static function parse_xml(str:String, state:NativeXmlState); + extern static function parse_xml(str:String, state:NativeXmlState):String; public static function parse(str:String):Xml { var x = new Xml(); diff --git a/std/cpp/UInt64.hx b/std/cpp/UInt64.hx index 8a160e58..8392a94b 100644 --- a/std/cpp/UInt64.hx +++ b/std/cpp/UInt64.hx @@ -22,4 +22,19 @@ package cpp; -@:coreType @:notNull @:runtimeValue abstract UInt64 from Int to Int {} +@:coreType @:notNull @:runtimeValue abstract UInt64 from Int { + + /** + Destructively cast to Int + **/ + public inline function toInt():Int { + return cast this; + } + + @:to + @:deprecated("Implicit cast from UInt64 to Int (32 bits) is deprecated. Use .toInt() or explicitly cast instead.") + inline function implicitToInt(): Int { + return toInt(); + } + +} diff --git a/std/cpp/_std/Sys.hx b/std/cpp/_std/Sys.hx index 0d2e77ab..3ee99b5e 100644 --- a/std/cpp/_std/Sys.hx +++ b/std/cpp/_std/Sys.hx @@ -63,7 +63,7 @@ import haxe.SysTools; return v; } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { NativeSys.put_env(s, v); } diff --git a/std/cpp/_std/haxe/Int64.hx b/std/cpp/_std/haxe/Int64.hx index 1ab500c7..fe9f1c91 100644 --- a/std/cpp/_std/haxe/Int64.hx +++ b/std/cpp/_std/haxe/Int64.hx @@ -22,13 +22,8 @@ package haxe; -import haxe.Int64Helper; - -@:notNull @:include("cpp/Int64.h") -@:native("cpp::Int64Struct") -private extern class ___Int64 { - +private extern class NativeInt64Helper { @:native("_hx_int64_make") static function make(high:Int32, low:Int32):__Int64; @@ -132,21 +127,21 @@ private extern class ___Int64 { static function low(a:__Int64):Int32; } -private typedef __Int64 = ___Int64; +private typedef __Int64 = cpp.Int64; @:coreApi @:transitive -abstract Int64(__Int64) from __Int64 to __Int64 { +@:notNull +abstract Int64(__Int64) from __Int64 from Int to __Int64 { public #if !cppia inline #end function copy():Int64 return this; public static #if !cppia inline #end function make(high:Int32, low:Int32):Int64 { - return __Int64.make(high, low); + return NativeInt64Helper.make(high, low); } - @:from public static #if !cppia inline #end function ofInt(x:Int):Int64 { - return __Int64.ofInt(x); + return x; } public static #if !cppia inline #end function toInt(x:Int64):Int { @@ -158,11 +153,11 @@ abstract Int64(__Int64) from __Int64 to __Int64 { @:deprecated('haxe.Int64.is() is deprecated. Use haxe.Int64.isInt64() instead') inline public static function is(val:Dynamic):Bool { - return isInt64(val); + return val is cpp.Int64; } public static #if !cppia inline #end function isInt64(val:Dynamic):Bool - return __Int64.isInt64(val); + return val is cpp.Int64; @:deprecated("Use high instead") public static #if !cppia inline #end function getHigh(x:Int64):Int32 @@ -173,22 +168,22 @@ abstract Int64(__Int64) from __Int64 to __Int64 { return x.low; public static #if !cppia inline #end function isNeg(x:Int64):Bool - return __Int64.isNeg(x); + return NativeInt64Helper.isNeg(x); public static #if !cppia inline #end function isZero(x:Int64):Bool - return __Int64.isZero(x); + return NativeInt64Helper.isZero(x); public static #if !cppia inline #end function compare(a:Int64, b:Int64):Int - return __Int64.compare(a, b); + return NativeInt64Helper.compare(a, b); public static #if !cppia inline #end function ucompare(a:Int64, b:Int64):Int - return __Int64.ucompare(a, b); + return NativeInt64Helper.ucompare(a, b); public static #if !cppia inline #end function toStr(x:Int64):String - return x.toString(); + return cast x.val; private #if !cppia inline #end function toString():String - return __Int64.toString(this); + return cast this; public static function parseString(sParam:String):Int64 { return Int64Helper.parseString(sParam); @@ -201,7 +196,7 @@ abstract Int64(__Int64) from __Int64 to __Int64 { public static function divMod(dividend:Int64, divisor:Int64):{quotient:Int64, modulus:Int64} { var q = dividend / divisor; - if (isZero(divisor)) + if (divisor == 0) throw "divide by zero"; var m = dividend - q * divisor; @@ -209,16 +204,16 @@ abstract Int64(__Int64) from __Int64 to __Int64 { return {quotient: q, modulus: m}; } - @:op(-A) +@:op(-A) public static #if !cppia inline #end function neg(x:Int64):Int64 - return __Int64.neg(x); + return NativeInt64Helper.neg(x); @:op(++A) private inline function preIncrement():Int64 { #if cppia this = this + make(0, 1); return this; #else - return __Int64.preIncrement(this); + return NativeInt64Helper.preIncrement(this); #end } @@ -228,7 +223,7 @@ abstract Int64(__Int64) from __Int64 to __Int64 { this = this + make(0, 1); return result; #else - return __Int64.postIncrement(this); + return NativeInt64Helper.postIncrement(this); #end } @@ -237,7 +232,7 @@ abstract Int64(__Int64) from __Int64 to __Int64 { untyped this = this - make(0, 1); return this; #else - return __Int64.preDecrement(this); + return NativeInt64Helper.preDecrement(this); #end } @@ -247,35 +242,35 @@ abstract Int64(__Int64) from __Int64 to __Int64 { this = this - make(0, 1); return result; #else - return __Int64.postDecrement(this); + return NativeInt64Helper.postDecrement(this); #end } @:op(A + B) public static #if !cppia inline #end function add(a:Int64, b:Int64):Int64 - return __Int64.add(a, b); + return NativeInt64Helper.add(a, b); @:op(A + B) @:commutative private static #if !cppia inline #end function addInt(a:Int64, b:Int):Int64 - return __Int64.addInt(a, b); + return NativeInt64Helper.addInt(a, b); @:op(A - B) public static #if !cppia inline #end function sub(a:Int64, b:Int64):Int64 { - return __Int64.sub(a, b); + return NativeInt64Helper.sub(a, b); } @:op(A - B) private static #if !cppia inline #end function subInt(a:Int64, b:Int):Int64 - return __Int64.subInt(a, b); + return NativeInt64Helper.subInt(a, b); @:op(A - B) private static #if !cppia inline #end function intSub(a:Int, b:Int64):Int64 - return __Int64.intSub(a, b); + return NativeInt64Helper.intSub(a, b); @:op(A * B) public static #if !cppia inline #end function mul(a:Int64, b:Int64):Int64 - return __Int64.mul(a, b); + return NativeInt64Helper.mul(a, b); @:op(A * B) @:commutative @@ -284,9 +279,9 @@ abstract Int64(__Int64) from __Int64 to __Int64 { @:op(A / B) public static #if !cppia inline #end function div(a:Int64, b:Int64):Int64 { - if (__Int64.isZero(b)) + if (NativeInt64Helper.isZero(b)) throw "divide by zero"; - return __Int64.div(a, b); + return NativeInt64Helper.div(a, b); } @:op(A / B) @@ -299,9 +294,9 @@ abstract Int64(__Int64) from __Int64 to __Int64 { @:op(A % B) public static #if !cppia inline #end function mod(a:Int64, b:Int64):Int64 { - if (__Int64.isZero(b)) + if (NativeInt64Helper.isZero(b)) throw "divide by zero"; - return __Int64.mod(a, b); + return NativeInt64Helper.mod(a, b); } @:op(A % B) @@ -314,16 +309,16 @@ abstract Int64(__Int64) from __Int64 to __Int64 { @:op(A == B) public static #if !cppia inline #end function eq(a:Int64, b:Int64):Bool - return __Int64.eq(a, b); + return NativeInt64Helper.eq(a, b); @:op(A == B) @:commutative private static #if !cppia inline #end function eqInt(a:Int64, b:Int):Bool - return __Int64.eqInt(a, b); + return NativeInt64Helper.eqInt(a, b); @:op(A != B) public static #if !cppia inline #end function neq(a:Int64, b:Int64):Bool - return __Int64.neq(a, b); + return NativeInt64Helper.neq(a, b); @:op(A != B) @:commutative @@ -380,39 +375,44 @@ abstract Int64(__Int64) from __Int64 to __Int64 { @:op(~A) private static #if !cppia inline #end function complement(a:Int64):Int64 - return __Int64.complement(a); + return NativeInt64Helper.complement(a); @:op(A & B) public static #if !cppia inline #end function and(a:Int64, b:Int64):Int64 - return __Int64.bitAnd(a, b); + return NativeInt64Helper.bitAnd(a, b); @:op(A | B) public static #if !cppia inline #end function or(a:Int64, b:Int64):Int64 - return __Int64.bitOr(a, b); + return NativeInt64Helper.bitOr(a, b); @:op(A ^ B) public static #if !cppia inline #end function xor(a:Int64, b:Int64):Int64 - return __Int64.bitXor(a, b); + return NativeInt64Helper.bitXor(a, b); @:op(A << B) public static #if !cppia inline #end function shl(a:Int64, b:Int):Int64 - return __Int64.shl(a, b); + return NativeInt64Helper.shl(a, b); @:op(A >> B) public static #if !cppia inline #end function shr(a:Int64, b:Int):Int64 - return __Int64.shr(a, b); + return NativeInt64Helper.shr(a, b); @:op(A >>> B) public static #if !cppia inline #end function ushr(a:Int64, b:Int):Int64 - return __Int64.ushr(a, b); + return NativeInt64Helper.ushr(a, b); public var high(get, never):Int32; private #if !cppia inline #end function get_high():Int32 - return __Int64.high(this); + return NativeInt64Helper.high(this); public var low(get, never):Int32; private #if !cppia inline #end function get_low():Int32 - return __Int64.low(this); + return NativeInt64Helper.low(this); + + private var val(get, never):__Int64; + + private #if !cppia inline #end function get_val():__Int64 + return this; } diff --git a/std/cpp/_std/haxe/atomic/AtomicInt.hx b/std/cpp/_std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..4e56dd44 --- /dev/null +++ b/std/cpp/_std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,46 @@ +package haxe.atomic; + +#if cppia +extern +#end +abstract AtomicInt(cpp.Pointer) { + public #if !(scriptable || cppia) inline #end function new(value:Int) { + this = cpp.Pointer.ofArray([value]); + } + + public #if !(scriptable || cppia) inline #end function add(b:Int):Int { + return untyped __cpp__("_hx_atomic_add({0}, {1})", this, b); + } + + public #if !(scriptable || cppia) inline #end function sub(b:Int):Int { + return untyped __cpp__("_hx_atomic_sub({0}, {1})", this, b); + } + + public #if !(scriptable || cppia) inline #end function and(b:Int):Int { + return untyped __cpp__("_hx_atomic_and({0}, {1})", this, b); + } + + public #if !(scriptable || cppia) inline #end function or(b:Int):Int { + return untyped __cpp__("_hx_atomic_or({0}, {1})", this, b); + } + + public #if !(scriptable || cppia) inline #end function xor(b:Int):Int { + return untyped __cpp__("_hx_atomic_xor({0}, {1})", this, b); + } + + public #if !(scriptable || cppia) inline #end function compareExchange(expected:Int, replacement:Int):Int { + return untyped __cpp__("_hx_atomic_compare_exchange({0}, {1}, {2})", this, expected, replacement); + } + + public #if !(scriptable || cppia) inline #end function exchange(value:Int):Int { + return untyped __cpp__("_hx_atomic_exchange({0}, {1})", this, value); + } + + public #if !(scriptable || cppia) inline #end function load():Int { + return untyped __cpp__("_hx_atomic_load({0})", this); + } + + public #if !(scriptable || cppia) inline #end function store(value:Int):Int { + return untyped __cpp__("_hx_atomic_store({0}, {1})", this, value); + } +} diff --git a/std/cpp/_std/haxe/ds/IntMap.hx b/std/cpp/_std/haxe/ds/IntMap.hx index 590f1e6a..0ee1b0cc 100644 --- a/std/cpp/_std/haxe/ds/IntMap.hx +++ b/std/cpp/_std/haxe/ds/IntMap.hx @@ -35,6 +35,7 @@ package haxe.ds; inline void set(int key, float value) { __int_hash_set_float(HX_MAP_THIS,key,value); } inline void set(int key, double value) { __int_hash_set_float(HX_MAP_THIS,key,value); } inline void set(int key, ::String value) { __int_hash_set_string(HX_MAP_THIS,key,value); } + inline void set(int key, cpp::Int64 value) { __int_hash_set_int64(HX_MAP_THIS,key,value); } template inline void set(int key, const ::cpp::Struct &value) {__int_hash_set(HX_MAP_THIS,key,value); } @@ -50,6 +51,7 @@ package haxe.ds; inline int get_int(int key) { return __int_hash_get_int(h,key); } inline Float get_float(int key) { return __int_hash_get_float(h,key); } inline String get_string(int key) { return __int_hash_get_string(h,key); } + inline cpp::Int64 get_int64(int key) { return __int_hash_get_int64(h,key); } ") @:coreApi class IntMap implements haxe.Constraints.IMap { @:ifFeature("haxe.ds.IntMap.*") @@ -123,6 +125,10 @@ package haxe.ds; untyped __int_hash_set_float(__cpp__("HX_MAP_THIS"), key, val); } + private function setInt64(key:Int, val:haxe.Int64):Void { + untyped __int_hash_set_int64(__cpp__("HX_MAP_THIS"), key, val); + } + private function getString(key:Int):String { return untyped __int_hash_get_string(h, key); } @@ -138,5 +144,9 @@ package haxe.ds; private function getFloat(key:Int):Float { return untyped __int_hash_get_float(h, key); } + + private function getInt64(key:Int):haxe.Int64 { + return untyped __int_hash_get_int64(h, key); + } #end } diff --git a/std/cpp/_std/haxe/ds/Map.hx b/std/cpp/_std/haxe/ds/Map.hx new file mode 100644 index 00000000..6e99f475 --- /dev/null +++ b/std/cpp/_std/haxe/ds/Map.hx @@ -0,0 +1,209 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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. + */ + +package haxe.ds; + +import haxe.Int64; +import haxe.ds.StringMap; +import haxe.ds.IntMap; +import haxe.ds.HashMap; +import haxe.ds.ObjectMap; +import haxe.ds.WeakMap; +import haxe.ds.EnumValueMap; +import haxe.Constraints.IMap; +import cpp.Int64Map; + +/** + Map allows key to value mapping for arbitrary value types, and many key + types. + + This is a multi-type abstract, it is instantiated as one of its + specialization types depending on its type parameters. + + A Map can be instantiated without explicit type parameters. Type inference + will then determine the type parameters from the usage. + + Maps can also be created with `[key1 => value1, key2 => value2]` syntax. + + Map is an abstract type, it is not available at runtime. + + @see https://haxe.org/manual/std-Map.html +**/ +@:transitive +@:multiType(@:followWithAbstracts K) +abstract Map(IMap) { + /** + Creates a new Map. + + This becomes a constructor call to one of the specialization types in + the output. The rules for that are as follows: + + 1. if `K` is a `String`, `haxe.ds.StringMap` is used + 2. if `K` is an `Int`, `haxe.ds.IntMap` is used + 3. if `K` is an `EnumValue`, `haxe.ds.EnumValueMap` is used + 4. if `K` is any other class or structure, `haxe.ds.ObjectMap` is used + 5. if `K` is any other type, it causes a compile-time error + + (Cpp) Map does not use weak keys on `ObjectMap` by default. + **/ + public function new(); + + /** + Maps `key` to `value`. + + If `key` already has a mapping, the previous value disappears. + + If `key` is `null`, the result is unspecified. + **/ + public inline function set(key:K, value:V) + this.set(key, value); + + /** + Returns the current mapping of `key`. + + If no such mapping exists, `null` is returned. + + Note that a check like `map.get(key) == null` can hold for two reasons: + + 1. the map has no mapping for `key` + 2. the map has a mapping with a value of `null` + + If it is important to distinguish these cases, `exists()` should be + used. + + If `key` is `null`, the result is unspecified. + **/ + @:arrayAccess public inline function get(key:K) + return this.get(key); + + /** + Returns true if `key` has a mapping, false otherwise. + + If `key` is `null`, the result is unspecified. + **/ + public inline function exists(key:K) + return this.exists(key); + + /** + Removes the mapping of `key` and returns true if such a mapping existed, + false otherwise. + + If `key` is `null`, the result is unspecified. + **/ + public inline function remove(key:K) + return this.remove(key); + + /** + Returns an Iterator over the keys of `this` Map. + + The order of keys is undefined. + **/ + public inline function keys():Iterator { + return this.keys(); + } + + /** + Returns an Iterator over the values of `this` Map. + + The order of values is undefined. + **/ + public inline function iterator():Iterator { + return this.iterator(); + } + + /** + Returns an Iterator over the keys and values of `this` Map. + + The order of values is undefined. + **/ + public inline function keyValueIterator():KeyValueIterator { + return this.keyValueIterator(); + } + + /** + Returns a shallow copy of `this` map. + + The order of values is undefined. + **/ + public inline function copy():Map { + return cast this.copy(); + } + + /** + Returns a String representation of `this` Map. + + The exact representation depends on the platform and key-type. + **/ + public inline function toString():String { + return this.toString(); + } + + /** + Removes all keys from `this` Map. + **/ + public inline function clear():Void { + this.clear(); + } + + @:arrayAccess @:noCompletion public inline function arrayWrite(k:K, v:V):V { + this.set(k, v); + return v; + } + + @:to static inline function toStringMap(t:IMap):StringMap { + return new StringMap(); + } + + @:to static inline function toIntMap(t:IMap):IntMap { + return new IntMap(); + } + + @:noDoc + @:to static inline function toInt64Map(t:IMap):Int64Map { + return new Int64Map(); + } + + @:to static inline function toEnumValueMapMap(t:IMap):EnumValueMap { + return new EnumValueMap(); + } + + @:to static inline function toObjectMap(t:IMap):ObjectMap { + return new ObjectMap(); + } + + @:from static inline function fromStringMap(map:StringMap):Map { + return cast map; + } + + @:from static inline function fromIntMap(map:IntMap):Map { + return cast map; + } + + @:noDoc + @:from static inline function fromInt64Map(map:Int64Map):Map { + return cast map; + } + + @:from static inline function fromObjectMap(map:ObjectMap):Map { + return cast map; + } +} diff --git a/std/cpp/_std/haxe/ds/ObjectMap.hx b/std/cpp/_std/haxe/ds/ObjectMap.hx index e078a5ae..21bc023d 100644 --- a/std/cpp/_std/haxe/ds/ObjectMap.hx +++ b/std/cpp/_std/haxe/ds/ObjectMap.hx @@ -35,6 +35,7 @@ package haxe.ds; inline void set(Dynamic key, float value) { __object_hash_set_float(HX_MAP_THIS,key,value); } inline void set(Dynamic key, double value) { __object_hash_set_float(HX_MAP_THIS,key,value); } inline void set(Dynamic key, ::String value) { __object_hash_set_string(HX_MAP_THIS,key,value); } + inline void set(Dynamic key, cpp::Int64 value) { __object_hash_set_int64(HX_MAP_THIS,key,value); } template @@ -48,7 +49,7 @@ package haxe.ds; inline int get_int(Dynamic key) { return __object_hash_get_int(h,key); } inline Float get_float(Dynamic key) { return __object_hash_get_float(h,key); } inline String get_string(Dynamic key) { return __object_hash_get_string(h,key); } - + inline cpp::Int64 get_int64(Dynamic key) { return __object_hash_get_int64(h,key); } ") @:coreApi class ObjectMap implements haxe.Constraints.IMap { @@ -123,6 +124,10 @@ class ObjectMap implements haxe.Constraints.IMap { untyped __object_hash_set_float(__cpp__("HX_MAP_THIS"), key, val); } + private function setInt64(key:Dynamic, val:haxe.Int64):Void { + untyped __object_hash_set_int64(__cpp__("HX_MAP_THIS"), key, val); + } + private function getString(key:Dynamic):String { return untyped __object_hash_get_string(h, key); } @@ -138,5 +143,9 @@ class ObjectMap implements haxe.Constraints.IMap { private function getFloat(key:Dynamic):Float { return untyped __object_hash_get_float(h, key); } + + private function getInt64(key:Dynamic):haxe.Int64 { + return untyped __object_hash_get_int64(h, key); + } #end } diff --git a/std/cpp/_std/haxe/ds/StringMap.hx b/std/cpp/_std/haxe/ds/StringMap.hx index 5147beac..0ae51282 100644 --- a/std/cpp/_std/haxe/ds/StringMap.hx +++ b/std/cpp/_std/haxe/ds/StringMap.hx @@ -35,6 +35,7 @@ package haxe.ds; inline void set(String key, float value) { __string_hash_set_float(HX_MAP_THIS,key,value); } inline void set(String key, double value) { __string_hash_set_float(HX_MAP_THIS,key,value); } inline void set(String key, ::String value) { __string_hash_set_string(HX_MAP_THIS,key,value); } + inline void set(String key, cpp::Int64 value) { __string_hash_set_int64(HX_MAP_THIS,key,value); } template inline void set(String key, const ::cpp::Struct &value) {__string_hash_set(HX_MAP_THIS,key,value); } @@ -50,6 +51,7 @@ package haxe.ds; inline int get_int(String key) { return __string_hash_get_int(h,key); } inline Float get_float(String key) { return __string_hash_get_float(h,key); } inline String get_string(String key) { return __string_hash_get_string(h,key); } + inline cpp::Int64 get_int64(String key) { return __string_hash_get_int64(h,key); } ") @:coreApi class StringMap implements haxe.Constraints.IMap { @:ifFeature("haxe.ds.StringMap.*") @@ -123,6 +125,10 @@ package haxe.ds; untyped __string_hash_set_float(__cpp__("HX_MAP_THIS"), key, val); } + private function setInt64(key:String, val:haxe.Int64):Void { + untyped __string_hash_set_int64(__cpp__("HX_MAP_THIS"), key, val); + } + private function getString(key:String):String { return untyped __string_hash_get_string(h, key); } @@ -138,5 +144,9 @@ package haxe.ds; private function getFloat(key:String):Float { return untyped __string_hash_get_float(h, key); } + + private function getInt64(key:String):haxe.Int64 { + return untyped __string_hash_get_int64(h, key); + } #end } diff --git a/std/cpp/_std/haxe/ds/WeakMap.hx b/std/cpp/_std/haxe/ds/WeakMap.hx index 728574f4..f4c2ef48 100644 --- a/std/cpp/_std/haxe/ds/WeakMap.hx +++ b/std/cpp/_std/haxe/ds/WeakMap.hx @@ -35,6 +35,7 @@ package haxe.ds; inline void set(Dynamic key, float value) { __object_hash_set_float(HX_MAP_THIS,key,value,true); } inline void set(Dynamic key, double value) { __object_hash_set_float(HX_MAP_THIS,key,value,true); } inline void set(Dynamic key, ::String value) { __object_hash_set_string(HX_MAP_THIS,key,value,true); } + inline void set(Dynamic key, cpp::Int64 value) { __object_hash_set_int64(HX_MAP_THIS,key,value,true); } template inline void set(Dynamic key, const ::cpp::Struct &value) {__object_hash_set(HX_MAP_THIS,key,value,true); } diff --git a/std/cpp/_std/sys/io/Process.hx b/std/cpp/_std/sys/io/Process.hx index 59726154..37bbf3b9 100644 --- a/std/cpp/_std/sys/io/Process.hx +++ b/std/cpp/_std/sys/io/Process.hx @@ -105,9 +105,7 @@ class Process { } public function exitCode(block:Bool = true):Null { - if (block == false) - throw "Non blocking exitCode() not supported on this platform"; - return NativeProcess.process_exit(p); + return NativeProcess.process_exit(p, block); } public function close():Void { diff --git a/std/cpp/_std/sys/thread/Condition.hx b/std/cpp/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..484db3c6 --- /dev/null +++ b/std/cpp/_std/sys/thread/Condition.hx @@ -0,0 +1,33 @@ +package sys.thread; + +@:coreApi +class Condition { + var c:Dynamic; + public function new():Void { + c = untyped __global__.__hxcpp_condition_create(); + } + + public function acquire():Void { + untyped __global__.__hxcpp_condition_acquire(c); + } + + public function tryAcquire():Bool { + return untyped __global__.__hxcpp_condition_try_acquire(c); + } + + public function release():Void { + untyped __global__.__hxcpp_condition_release(c); + } + + public function wait():Void { + untyped __global__.__hxcpp_condition_wait(c); + } + + public function signal():Void { + untyped __global__.__hxcpp_condition_signal(c); + } + + public function broadcast():Void { + untyped __global__.__hxcpp_condition_broadcast(c); + } +} diff --git a/std/cpp/_std/sys/thread/Semaphore.hx b/std/cpp/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..a09774f7 --- /dev/null +++ b/std/cpp/_std/sys/thread/Semaphore.hx @@ -0,0 +1,22 @@ +package sys.thread; + +@:coreApi +class Semaphore { + var m:Dynamic; + + public function new(value:Int) { + m = untyped __global__.__hxcpp_semaphore_create(value); + } + + public function acquire():Void { + untyped __global__.__hxcpp_semaphore_acquire(m); + } + + public function tryAcquire(?timeout:Float):Bool { + return untyped __global__.__hxcpp_semaphore_try_acquire(m, timeout == null ? 0 : (timeout:Float)); + } + + public function release():Void { + untyped __global__.__hxcpp_semaphore_release(m); + } +} diff --git a/std/cpp/cppia/HostClasses.hx b/std/cpp/cppia/HostClasses.hx index 392e403e..b5674f85 100644 --- a/std/cpp/cppia/HostClasses.hx +++ b/std/cpp/cppia/HostClasses.hx @@ -46,6 +46,8 @@ class HostClasses { "sys.thread.Mutex", "sys.thread.Thread", "sys.thread.Tls", + "sys.thread.Semaphore", + "sys.thread.Condition", "cpp.vm.ExecutionTrace", "cpp.vm.Gc", "cpp.vm.Profiler", @@ -53,6 +55,7 @@ class HostClasses { "cpp.vm.WeakRef", "cpp.Object", "cpp.Int64", + "cpp.Int64Map", "cpp.Finalizable", "Std", "StringBuf", @@ -147,6 +150,7 @@ class HostClasses { "List", "Map", "String", + "haxe.atomic.AtomicInt" ]; static function parseClassInfo(externs:Map, filename:String) { diff --git a/std/cs/_std/Std.hx b/std/cs/_std/Std.hx index c24b6aa5..d5fad622 100644 --- a/std/cs/_std/Std.hx +++ b/std/cs/_std/Std.hx @@ -23,6 +23,8 @@ import cs.Boot; import cs.Lib; +using StringTools; + @:coreApi @:nativeGen class Std { @:deprecated('Std.is is deprecated. Use Std.isOfType instead.') public static inline function is(v:Dynamic, t:Dynamic):Bool { @@ -79,54 +81,74 @@ import cs.Lib; return cast x; } + static inline function isSpaceChar(code:Int):Bool + return (code > 8 && code < 14) || code == 32; + + static inline function isHexPrefix(cur:Int, next:Int):Bool + return cur == '0'.code && (next == 'x'.code || next == 'X'.code); + + static inline function isDecimalDigit(code:Int):Bool + return '0'.code <= code && code <= '9'.code; + + static inline function isHexadecimalDigit(code:Int):Bool + return isDecimalDigit(code) || ('a'.code <= code && code <= 'f'.code) || ('A'.code <= code && code <= 'F'.code); + public static function parseInt(x:String):Null { if (x == null) return null; - var base = 10; - var len = x.length; - var foundCount = 0; - var sign = 0; - var firstDigitIndex = 0; - var lastDigitIndex = -1; - var previous = 0; - - for(i in 0...len) { - var c = StringTools.fastCodeAt(x, i); - switch c { - case _ if((c > 8 && c < 14) || c == 32): - if(foundCount > 0) { - return null; - } - continue; - case '-'.code if(foundCount == 0): - sign = -1; - case '+'.code if(foundCount == 0): - sign = 1; - case '0'.code if(foundCount == 0 || (foundCount == 1 && sign != 0)): - case 'x'.code | 'X'.code if(previous == '0'.code && ((foundCount == 1 && sign == 0) || (foundCount == 2 && sign != 0))): - base = 16; - case _ if('0'.code <= c && c <= '9'.code): - case _ if(base == 16 && (('a'.code <= c && c <= 'z'.code) || ('A'.code <= c && c <= 'Z'.code))): - case _: - break; - } - if((foundCount == 0 && sign == 0) || (foundCount == 1 && sign != 0)) { - firstDigitIndex = i; + final len = x.length; + var index = 0; + + inline function hasIndex(index:Int) + return index < len; + + // skip whitespace + while (hasIndex(index)) { + if (!isSpaceChar(x.unsafeCodeAt(index))) + break; + ++index; + } + + // handle sign + final isNegative = hasIndex(index) && { + final sign = x.unsafeCodeAt(index); + if (sign == '-'.code || sign == '+'.code) { + ++index; } - foundCount++; - lastDigitIndex = i; - previous = c; + sign == '-'.code; } - if(firstDigitIndex <= lastDigitIndex) { - var digits = x.substring(firstDigitIndex, lastDigitIndex + 1); - return try { - (sign == -1 ? -1 : 1) * cs.system.Convert.ToInt32(digits, base); - } catch(e:cs.system.FormatException) { - null; + + // handle base + final isHexadecimal = hasIndex(index + 1) && isHexPrefix(x.unsafeCodeAt(index), x.unsafeCodeAt(index + 1)); + if (isHexadecimal) + index += 2; // skip prefix + + // handle digits + final firstInvalidIndex = { + var cur = index; + if (isHexadecimal) { + while (hasIndex(cur)) { + if (!isHexadecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } + } else { + while (hasIndex(cur)) { + if (!isDecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } } + cur; } - return null; + + // no valid digits + if (index == firstInvalidIndex) + return null; + + final result = cs.system.Convert.ToInt32(x.substring(index, firstInvalidIndex), if (isHexadecimal) 16 else 10); + return if (isNegative) -result else result; } public static function parseFloat(x:String):Float { diff --git a/std/cs/_std/Sys.hx b/std/cs/_std/Sys.hx index 7cf8d55c..9adef45f 100644 --- a/std/cs/_std/Sys.hx +++ b/std/cs/_std/Sys.hx @@ -26,7 +26,6 @@ import cs.system.threading.Thread; @:coreApi class Sys { - private static var _env:haxe.ds.StringMap; private static var _args:Array; public static inline function print(v:Dynamic):Void { @@ -50,22 +49,17 @@ class Sys { return Environment.GetEnvironmentVariable(s); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { Environment.SetEnvironmentVariable(s, v); - if (_env != null) - _env.set(s, v); } public static function environment():Map { - if (_env == null) { - var e = _env = new haxe.ds.StringMap(); - var nenv = Environment.GetEnvironmentVariables().GetEnumerator(); - while (nenv.MoveNext()) { - e.set(nenv.Key, nenv.Value); - } + final env = new haxe.ds.StringMap(); + final nenv = Environment.GetEnvironmentVariables().GetEnumerator(); + while (nenv.MoveNext()) { + env.set(nenv.Key, nenv.Value); } - - return _env; + return env; } public static inline function sleep(seconds:Float):Void { @@ -78,7 +72,7 @@ class Sys { } public static inline function getCwd():String { - return cs.system.io.Directory.GetCurrentDirectory(); + return haxe.io.Path.addTrailingSlash(cs.system.io.Directory.GetCurrentDirectory()); } public static inline function setCwd(s:String):Void { diff --git a/std/cs/_std/haxe/Rest.hx b/std/cs/_std/haxe/Rest.hx index f4c28b20..ea830bb8 100644 --- a/std/cs/_std/haxe/Rest.hx +++ b/std/cs/_std/haxe/Rest.hx @@ -1,20 +1,26 @@ package haxe; -import haxe.iterators.RestIterator; -import haxe.iterators.RestKeyValueIterator; import cs.NativeArray; import cs.system.Array as CsArray; +import haxe.iterators.RestIterator; +import haxe.iterators.RestKeyValueIterator; private typedef NativeRest = #if erase_generics NativeArray #else NativeArray #end; @:coreApi abstract Rest(NativeRest) { - public var length(get,never):Int; + public var length(get, never):Int; + inline function get_length():Int return this.Length; @:from static public inline function of(array:Array):Rest + #if erase_generics + // This is wrong but so is everything else in my life return new Rest(@:privateAccess array.__a); + #else + return new Rest(cs.Lib.nativeArray(array, false)); + #end inline function new(a:NativeRest):Void this = a; @@ -51,4 +57,4 @@ abstract Rest(NativeRest) { public function toString():String { return toArray().toString(); } -} \ No newline at end of file +} diff --git a/std/cs/_std/haxe/atomic/AtomicInt.hx b/std/cs/_std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..d1b9e661 --- /dev/null +++ b/std/cs/_std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,61 @@ +package haxe.atomic; + +private class IntWrapper { + public var value:Int; + + public function new(value:Int) { + this.value = value; + } +} + +abstract AtomicInt(IntWrapper) { + public inline function new(value:Int) { + this = new IntWrapper(value); + } + + private inline function cas_loop(value:Int, op:(a:Int, b:Int) -> Int):Int { + var oldValue; + var newValue; + do { + oldValue = load(); + newValue = op(oldValue, value); + } while(compareExchange(oldValue, newValue) != oldValue); + return oldValue; + } + + public inline function add(b:Int):Int { + return cas_loop(b, (a, b) -> a + b); + } + + public inline function sub(b:Int):Int { + return cas_loop(b, (a, b) -> a - b); + } + + public inline function and(b:Int):Int { + return cas_loop(b, (a, b) -> cast a & b); + } + + public inline function or(b:Int):Int { + return cas_loop(b, (a, b) -> cast a | b); + } + + public inline function xor(b:Int):Int { + return cas_loop(b, (a, b) -> cast a ^ b); + } + + public inline function compareExchange(expected:Int, replacement:Int):Int { + return cs.Syntax.code("System.Threading.Interlocked.CompareExchange(ref ({0}).value, {1}, {2})", this, replacement, expected); + } + + public inline function exchange(value:Int):Int { + return cs.Syntax.code("System.Threading.Interlocked.Exchange(ref ({0}).value, {1})", this, value); + } + + public inline function load():Int { + return this.value; // according to the CLI spec reads and writes are atomic + } + + public inline function store(value:Int):Int { + return this.value = value; // according to the CLI spec reads and writes are atomic + } +} \ No newline at end of file diff --git a/std/cs/_std/haxe/atomic/AtomicObject.hx b/std/cs/_std/haxe/atomic/AtomicObject.hx new file mode 100644 index 00000000..c7a07bc4 --- /dev/null +++ b/std/cs/_std/haxe/atomic/AtomicObject.hx @@ -0,0 +1,33 @@ +package haxe.atomic; + +import cs.system.threading.Interlocked.*; + +private class ObjectWrapper { + public var value:T; + + public function new(value:T) { + this.value = value; + } +} + +extern abstract AtomicObject(ObjectWrapper) { + public inline function new(value:T) { + this = new ObjectWrapper(value); + } + + public inline function compareExchange(expected:T, replacement:T):T { + return cs.Syntax.code("System.Threading.Interlocked.CompareExchange(ref ({0}).value, {1}, {2})", this, replacement, expected); + } + + public inline function exchange(value:T):T { + return cs.Syntax.code("System.Threading.Interlocked.Exchange(ref ({0}).value, {1})", this, value); + } + + public inline function load():T { + return this.value; // according to the CLI spec reads and writes are atomic + } + + public inline function store(value:T):T { + return this.value = value; // according to the CLI spec reads and writes are atomic + } +} \ No newline at end of file diff --git a/std/cs/_std/haxe/ds/IntMap.hx b/std/cs/_std/haxe/ds/IntMap.hx index 064510bc..5f936de6 100644 --- a/std/cs/_std/haxe/ds/IntMap.hx +++ b/std/cs/_std/haxe/ds/IntMap.hx @@ -364,7 +364,7 @@ import cs.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -373,7 +373,7 @@ import cs.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/cs/_std/haxe/ds/ObjectMap.hx b/std/cs/_std/haxe/ds/ObjectMap.hx index 03481ddc..dd26bb1a 100644 --- a/std/cs/_std/haxe/ds/ObjectMap.hx +++ b/std/cs/_std/haxe/ds/ObjectMap.hx @@ -378,7 +378,7 @@ import cs.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -387,7 +387,7 @@ import cs.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/cs/_std/haxe/ds/StringMap.hx b/std/cs/_std/haxe/ds/StringMap.hx index 64fc72a2..39d652dc 100644 --- a/std/cs/_std/haxe/ds/StringMap.hx +++ b/std/cs/_std/haxe/ds/StringMap.hx @@ -377,7 +377,7 @@ import cs.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -386,7 +386,7 @@ import cs.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/cs/_std/sys/thread/Condition.hx b/std/cs/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..52b1f24a --- /dev/null +++ b/std/cs/_std/sys/thread/Condition.hx @@ -0,0 +1,37 @@ +package sys.thread; + +import cs.system.threading.Monitor; + +@:coreApi +@:access(sys.thread.Mutex) +class Condition { + final object:cs.system.Object; + + public function new():Void { + this.object = new cs.system.Object(); + } + + public function acquire():Void { + Monitor.Enter(object); + } + + public function tryAcquire():Bool { + return Monitor.TryEnter(object); + } + + public function release():Void { + Monitor.Exit(object); + } + + public function wait():Void { + Monitor.Wait(object); + } + + public function signal():Void { + Monitor.Pulse(object); + } + + public function broadcast():Void { + Monitor.PulseAll(object); + } +} diff --git a/std/cs/_std/sys/thread/Semaphore.hx b/std/cs/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..e08064b8 --- /dev/null +++ b/std/cs/_std/sys/thread/Semaphore.hx @@ -0,0 +1,22 @@ +package sys.thread; + +@:coreApi +class Semaphore { + final native:cs.system.threading.Semaphore; + + public function new(value:Int):Void { + this.native = new cs.system.threading.Semaphore(value, 0x7FFFFFFF); + } + + public function acquire():Void { + native.WaitOne(); + } + + public function tryAcquire(?timeout:Float):Bool { + return native.WaitOne(timeout == null ? 0 : Std.int(timeout * 1000)); + } + + public function release():Void { + native.Release(); + } +} diff --git a/std/cs/internal/HxObject.hx b/std/cs/internal/HxObject.hx index dc6453f7..5dbf5e3c 100644 --- a/std/cs/internal/HxObject.hx +++ b/std/cs/internal/HxObject.hx @@ -230,7 +230,7 @@ class DynamicObject extends HxObject { if (ts != null) return ts(); var ret = new StringBuf(); - ret.add("{"); + ret.add("["); var first = true; for (f in Reflect.fields(this)) { if (first) @@ -244,7 +244,7 @@ class DynamicObject extends HxObject { } if (!first) ret.add(" "); - ret.add("}"); + ret.add("]"); return ret.toString(); } } diff --git a/std/eval/_std/Sys.hx b/std/eval/_std/Sys.hx index 30561e3d..28e2075b 100644 --- a/std/eval/_std/Sys.hx +++ b/std/eval/_std/Sys.hx @@ -33,7 +33,7 @@ class Sys { extern static public function getEnv(s:String):String; - extern static public function putEnv(s:String, v:String):Void; + extern static public function putEnv(s:String, v:Null):Void; extern static public function environment():Map; diff --git a/std/eval/_std/sys/net/Host.hx b/std/eval/_std/sys/net/Host.hx index 3a73c052..cbc1adcd 100644 --- a/std/eval/_std/sys/net/Host.hx +++ b/std/eval/_std/sys/net/Host.hx @@ -43,11 +43,11 @@ class Host { this.ip = ip; } - extern static public function localhost(); + extern static public function localhost():String; - extern static function hostReverse(ip:Int); + extern static function hostReverse(ip:Int):String; - extern static function hostToString(ip:Int); + extern static function hostToString(ip:Int):String; - extern static function resolve(name:String); + extern static function resolve(name:String):Int; } diff --git a/std/eval/_std/sys/thread/Condition.hx b/std/eval/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..15b6b182 --- /dev/null +++ b/std/eval/_std/sys/thread/Condition.hx @@ -0,0 +1,41 @@ +package sys.thread; + +@:coreApi class Condition { + final cond:eval.luv.Condition; + final mutex:eval.luv.Mutex; + + public function new():Void { + cond = eval.luv.Condition.init().resolve(); + mutex = eval.luv.Mutex.init(true).resolve(); + eval.vm.Gc.finalise(destroy, this); + } + + static function destroy(cond:Condition):Void { + cond.cond.destroy(); + cond.mutex.destroy(); + } + + public function acquire():Void { + mutex.lock(); + } + + public function tryAcquire():Bool { + return mutex.tryLock().isOk(); + } + + public function release():Void { + mutex.unlock(); + } + + public function wait():Void { + cond.wait(mutex); + } + + public function signal():Void { + cond.signal(); + } + + public function broadcast():Void { + cond.broadcast(); + } +} diff --git a/std/eval/_std/sys/thread/EventLoop.hx b/std/eval/_std/sys/thread/EventLoop.hx index fad3699c..d5ef5e97 100644 --- a/std/eval/_std/sys/thread/EventLoop.hx +++ b/std/eval/_std/sys/thread/EventLoop.hx @@ -3,12 +3,23 @@ package sys.thread; import eval.luv.Loop; import eval.luv.Async; import eval.luv.Timer as LuvTimer; +import haxe.MainLoop; +/** + When an event loop has an available event to execute. +**/ @:coreApi enum NextEventTime { + /** There's already an event waiting to be executed */ Now; + /** No new events are expected. */ Never; + /** + An event is expected to arrive at any time. + If `time` is specified, then the event will be ready at that time for sure. + */ AnyTime(time:Null); + /** An event is expected to be ready for execution at `time`. */ At(time:Float); } @@ -27,37 +38,47 @@ private class RegularEvent { } } +/** + An event loop implementation used for `sys.thread.Thread` +**/ @:coreApi class EventLoop { @:allow(eval.luv.Loop) final handle:Loop; final mutex = new Mutex(); - final oneTimeEvents = new ArrayVoid>>(); - var oneTimeEventsIdx = 0; final wakeup:Async; var promisedEventsCount = 0; var pending:Array<()->Void> = []; - var looping = false; + var started:Bool = false; + + var isMainThread:Bool; + static var CREATED : Bool; public function new():Void { + isMainThread = !CREATED; + CREATED = true; handle = Loop.init().resolve(); wakeup = Async.init(handle, consumePending).resolve(); wakeup.unref(); } + /** + Schedule event for execution every `intervalMs` milliseconds in current loop. + **/ public function repeat(event:()->Void, intervalMs:Int):EventHandler { var e = new RegularEvent(event); mutex.acquire(); - pending.push(() -> { - e.timer = LuvTimer.init(handle).resolve(); - e.timer.start(e.run, intervalMs, intervalMs < 1 ? 1 : intervalMs).resolve(); - }); + e.timer = LuvTimer.init(handle).resolve(); + e.timer.start(e.run, intervalMs, intervalMs < 1 ? 1 : intervalMs).resolve(); mutex.release(); wakeup.send(); return e; } + /** + Prevent execution of a previously scheduled event in current loop. + **/ public function cancel(eventHandler:EventHandler):Void { mutex.acquire(); (eventHandler:RegularEvent).event = noop; @@ -71,6 +92,11 @@ class EventLoop { } static final noop = function() {} + /** + Notify this loop about an upcoming event. + This makes the thread stay alive and wait for as many events as the number of + times `.promise()` was called. These events should be added via `.runPromised()`. + **/ public function promise():Void { mutex.acquire(); ++promisedEventsCount; @@ -79,6 +105,9 @@ class EventLoop { wakeup.send(); } + /** + Execute `event` as soon as possible. + **/ public function run(event:()->Void):Void { mutex.acquire(); pending.push(event); @@ -86,6 +115,9 @@ class EventLoop { wakeup.send(); } + /** + Add previously promised `event` for execution. + **/ public function runPromised(event:()->Void):Void { mutex.acquire(); --promisedEventsCount; @@ -96,7 +128,7 @@ class EventLoop { } function refUnref():Void { - if(promisedEventsCount > 0) { + if (promisedEventsCount > 0 || (isMainThread && haxe.MainLoop.hasEvents())) { wakeup.ref(); } else { wakeup.unref(); @@ -104,37 +136,71 @@ class EventLoop { } public function progress():NextEventTime { - //TODO: throw if loop is already running - if((handle:Loop).run(NOWAIT)) { + if (started) throw "Event loop already started"; + + if (handle.run(NOWAIT)) { return AnyTime(null); } else { return Never; } } + /** + Blocks until a new event is added or `timeout` (in seconds) to expires. + + Depending on a target platform this method may also automatically execute arriving + events while waiting. However if any event is executed it will stop waiting. + + Returns `true` if more events are expected. + Returns `false` if no more events expected. + + Depending on a target platform this method may be non-reentrant. It must + not be called from event callbacks. + **/ public function wait(?timeout:Float):Bool { - //TODO: throw if loop is already running - if(timeout == null) { + if (started) throw "Event loop already started"; + + if(timeout != null) { var timer = LuvTimer.init(handle).resolve(); timer.start(() -> { timer.stop().resolve(); timer.close(() -> {}); }, Std.int(timeout * 1000)); - return (handle:Loop).run(ONCE); + return handle.run(ONCE); } else { - return (handle:Loop).run(ONCE); + return handle.run(ONCE); } } + /** + Execute all pending events. + Wait and execute as many events as the number of times `promise()` was called. + Runs until all repeating events are cancelled and no more events are expected. + + Depending on a target platform this method may be non-reentrant. It must + not be called from event callbacks. + **/ public function loop():Void { - //TODO: throw if loop is already running + if (started) throw "Event loop already started"; + started = true; consumePending(); - (handle:Loop).run(DEFAULT); + handle.run(DEFAULT); } function consumePending(?_:Async):Void { + mutex.acquire(); var p = pending; pending = []; + mutex.release(); for(fn in p) fn(); + + if (started && isMainThread) { + var next = @:privateAccess MainLoop.tick(); + if (haxe.MainLoop.hasEvents()) { + wakeup.send(); + } else { + refUnref(); + } + } } -} \ No newline at end of file +} diff --git a/std/eval/_std/sys/thread/Semaphore.hx b/std/eval/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..5c75b471 --- /dev/null +++ b/std/eval/_std/sys/thread/Semaphore.hx @@ -0,0 +1,36 @@ +package sys.thread; + +@:coreApi class Semaphore { + final native:eval.luv.Semaphore; + + public function new(value:Int):Void { + native = eval.luv.Semaphore.init(value).resolve(); + eval.vm.Gc.finalise(destroy, this); + } + + static function destroy(sem:Semaphore):Void { + sem.native.destroy(); + } + + public function acquire():Void { + native.wait(); + } + + public function tryAcquire(?timeout:Float):Bool { + if (timeout == null) { + return native.tryWait().isOk(); + } else { + var t = Sys.time() + timeout; + while (Sys.time() < t) { + if (native.tryWait().isOk()) { + return true; + } + } + return false; + } + } + + public function release():Void { + native.post(); + } +} diff --git a/std/eval/integers/UInt64.hx b/std/eval/integers/UInt64.hx index f776966a..082bff71 100644 --- a/std/eval/integers/UInt64.hx +++ b/std/eval/integers/UInt64.hx @@ -18,10 +18,6 @@ package eval.integers; /** Parse the given string value to an unsigned integer. -<<<<<<< HEAD - -======= ->>>>>>> development Throws if the given string is not a valid representation of an unsigned integer. **/ diff --git a/std/eval/luv/Dir.hx b/std/eval/luv/Dir.hx index 11a5abd5..4fd3f65c 100644 --- a/std/eval/luv/Dir.hx +++ b/std/eval/luv/Dir.hx @@ -60,14 +60,14 @@ typedef DirectoryScan = { **/ extern class DirSync { @:inheritDoc(eval.luv.Dir.open) - static public function open(loop:Loop, path:NativeString):Result; + static public function open(path:NativeString):Result; @:inheritDoc(eval.luv.Dir.close) - static public function close(dir:Dir, loop:Loop):Result; + static public function close(dir:Dir):Result; @:inheritDoc(eval.luv.Dir.read) - static public function read(dir:Dir, loop:Loop, ?numberOfEntries:Int):Result>; + static public function read(dir:Dir, ?numberOfEntries:Int):Result>; @:inheritDoc(eval.luv.Dir.scan) - static public function scan(loop:Loop, path:NativeString):Result; + static public function scan(path:NativeString):Result; } \ No newline at end of file diff --git a/std/eval/luv/Env.hx b/std/eval/luv/Env.hx index 5e86261a..fae97f72 100644 --- a/std/eval/luv/Env.hx +++ b/std/eval/luv/Env.hx @@ -16,6 +16,11 @@ extern class Env { **/ static function setEnv(name:String, value:NativeString):Result; + /** + Deletes an environment variable. + **/ + static function unsetEnv(name:String):Result; + /** Retrieves all environment variables. **/ diff --git a/std/eval/luv/Resource.hx b/std/eval/luv/Resource.hx index 40d575cb..5457e74f 100644 --- a/std/eval/luv/Resource.hx +++ b/std/eval/luv/Resource.hx @@ -68,7 +68,7 @@ extern class Resource { /** Evaluates to the resident set size for the current process. **/ - static function residentSetMemory(pid:Int):Result; + static function residentSetMemory():Result; /** Gets the resource usage measures for the current process. diff --git a/std/eval/luv/UVError.hx b/std/eval/luv/UVError.hx index 82389799..fd99941b 100644 --- a/std/eval/luv/UVError.hx +++ b/std/eval/luv/UVError.hx @@ -162,6 +162,10 @@ enum abstract UVError(Int) { var UV_EFTYPE = 76; /** illegal byte sequence */ var UV_EILSEQ = 77; + /** value too large for defined data type **/ + var UV_EOVERFLOW = 78; + /** socket type not supported**/ + var UV_ESOCKTNOSUPPORT = 79; /** Converts a system error code to a libuv error. diff --git a/std/flash/Lib.hx b/std/flash/Lib.hx index 06b631a5..d21f9432 100644 --- a/std/flash/Lib.hx +++ b/std/flash/Lib.hx @@ -100,4 +100,10 @@ class Lib { str = str.split("\\").join("\\\\"); flash.external.ExternalInterface.call("console." + type, str); } + + public static var parseInt(get, never):(string:String, ?radix:Int) -> Float; + + extern static inline function get_parseInt():(string:String, ?radix:Int) -> Float { + return untyped __global__["parseInt"]; + } } diff --git a/std/flash/_std/Std.hx b/std/flash/_std/Std.hx index 869c36bf..1e409ff0 100644 --- a/std/flash/_std/Std.hx +++ b/std/flash/_std/Std.hx @@ -49,13 +49,12 @@ import flash.Boot; return untyped __int__(x); } - public static function parseInt(x:String):Null - untyped { - var v = __global__["parseInt"](x); - if (__global__["isNaN"](v)) - return null; - return v; - } + public static function parseInt(x:String):Null { + final v = flash.Lib.parseInt(x); + if (Math.isNaN(v)) + return null; + return cast v; + } public static function parseFloat(x:String):Float { return untyped __global__["parseFloat"](x); diff --git a/std/flash/_std/haxe/ds/IntMap.hx b/std/flash/_std/haxe/ds/IntMap.hx index 54738c1f..ebec393e 100644 --- a/std/flash/_std/haxe/ds/IntMap.hx +++ b/std/flash/_std/haxe/ds/IntMap.hx @@ -69,7 +69,7 @@ package haxe.ds; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -78,7 +78,7 @@ package haxe.ds; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/flash/_std/haxe/ds/ObjectMap.hx b/std/flash/_std/haxe/ds/ObjectMap.hx index 288cb6f9..cb3f5526 100644 --- a/std/flash/_std/haxe/ds/ObjectMap.hx +++ b/std/flash/_std/haxe/ds/ObjectMap.hx @@ -66,14 +66,14 @@ class ObjectMap extends flash.utils.Dictionary implements haxe.Constrai } public function toString():String { - var s = ""; + var s = "["; var it = keys(); for (i in it) { s += (s == "" ? "" : ",") + Std.string(i); s += " => "; s += Std.string(get(i)); } - return s + "}"; + return s + "]"; } public function clear():Void { diff --git a/std/flash/_std/haxe/ds/StringMap.hx b/std/flash/_std/haxe/ds/StringMap.hx index a6fd0a06..960898b4 100644 --- a/std/flash/_std/haxe/ds/StringMap.hx +++ b/std/flash/_std/haxe/ds/StringMap.hx @@ -107,7 +107,7 @@ package haxe.ds; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -116,7 +116,7 @@ package haxe.ds; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/flash/_std/haxe/ds/UnsafeStringMap.hx b/std/flash/_std/haxe/ds/UnsafeStringMap.hx index 3d2969f2..daff4738 100644 --- a/std/flash/_std/haxe/ds/UnsafeStringMap.hx +++ b/std/flash/_std/haxe/ds/UnsafeStringMap.hx @@ -74,7 +74,7 @@ class UnsafeStringMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -83,7 +83,7 @@ class UnsafeStringMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/flash/_std/haxe/ds/WeakMap.hx b/std/flash/_std/haxe/ds/WeakMap.hx index fee582cd..c577a660 100644 --- a/std/flash/_std/haxe/ds/WeakMap.hx +++ b/std/flash/_std/haxe/ds/WeakMap.hx @@ -66,14 +66,14 @@ class WeakMap extends flash.utils.Dictionary implements haxe.Constraint } public function toString():String { - var s = ""; + var s = "["; var it = keys(); for (i in it) { s += (s == "" ? "" : ",") + Std.string(i); s += " => "; s += Std.string(get(i)); } - return s + "}"; + return s + "]"; } public function clear():Void { diff --git a/std/flash/globalization/DateTimeFormatter.hx b/std/flash/globalization/DateTimeFormatter.hx index e37bb8bf..da56e351 100644 --- a/std/flash/globalization/DateTimeFormatter.hx +++ b/std/flash/globalization/DateTimeFormatter.hx @@ -8,7 +8,7 @@ package flash.globalization; function format(dateTime : Date) : String; function formatUTC(dateTime : Date) : String; function getDateStyle() : DateTimeStyle; - function getDateTimePattern() : DateTimeStyle; + function getDateTimePattern() : String; function getFirstWeekday() : Int; function getMonthNames(?nameStyle : DateTimeNameStyle, ?context : DateTimeNameContext) : flash.Vector; function getTimeStyle() : DateTimeStyle; diff --git a/std/haxe/EntryPoint.hx b/std/haxe/EntryPoint.hx index 175b6b22..56d89597 100644 --- a/std/haxe/EntryPoint.hx +++ b/std/haxe/EntryPoint.hx @@ -150,6 +150,23 @@ class EntryPoint { flash.Lib.current.stage.addEventListener(flash.events.Event.ENTER_FRAME, function(_) processEvents()); #elseif (target.threaded && !cppia) //everything is delegated to sys.thread.EventLoop + #elseif lua + inline function luvRun(mode:String):Bool + return untyped __lua__('_hx_luv.run({0})', mode); + while (true) { + var nextTick = processEvents(); + if(untyped __lua__('_hx_luv.loop_alive()')) { + if(nextTick < 0) + luvRun("once") + else + luvRun("nowait"); + } else { + if (nextTick < 0) + break; + if (nextTick > 0) + sleepLock.wait(nextTick); + } + } #elseif sys while (true) { var nextTick = processEvents(); diff --git a/std/haxe/EnumFlags.hx b/std/haxe/EnumFlags.hx index 02d81bc7..b6458f0e 100644 --- a/std/haxe/EnumFlags.hx +++ b/std/haxe/EnumFlags.hx @@ -40,6 +40,14 @@ abstract EnumFlags(Int) { this = i; } + @:from static function from(e:T) : EnumFlags { + return new EnumFlags(1 << e.getIndex()); + } + + @:op(a|b) function or(f:haxe.EnumFlags) : haxe.EnumFlags; + @:op(a&b) function and(f:haxe.EnumFlags) : haxe.EnumFlags; + @:op(a^b) function xor(f:haxe.EnumFlags) : haxe.EnumFlags; + /** Checks if the index of enum instance `v` is set. @@ -76,6 +84,22 @@ abstract EnumFlags(Int) { this &= 0xFFFFFFFF - (1 << Type.enumIndex(v)); } + /** + Depending on the value of `condition` sets (`condition=true`) or unsets (`condition=false`) + the index of enum instance `v`. + + This method is optimized if `v` is an enum instance expression such as + `SomeEnum.SomeCtor`. + + If `v` is `null`, the result is unspecified. + **/ + public inline function setTo(v:T, condition:Bool):Void { + if(condition) + set(v) + else + unset(v); + } + /** Convert a integer bitflag into a typed one (this is a no-op, it does not have any impact on speed). diff --git a/std/haxe/EnumTools.hx b/std/haxe/EnumTools.hx index 6a69a2c0..35c787ee 100644 --- a/std/haxe/EnumTools.hx +++ b/std/haxe/EnumTools.hx @@ -37,7 +37,7 @@ extern class EnumTools { If `e` is inside a package, the package structure is returned dot- separated, with another dot separating the enum name: - + pack1.pack2.(...).packN.EnumName If `e` is a sub-type of a Haxe module, that module is not part of the @@ -110,6 +110,11 @@ extern class EnumTools { static inline function getConstructors(e:Enum):Array { return Type.getEnumConstructs(e); } + + #if (java && jvm) + @:noCompletion + extern static function values(en:Enum):java.NativeArray>; + #end } /** diff --git a/std/haxe/Json.hx b/std/haxe/Json.hx index 18a7dc4d..c0fd2e53 100644 --- a/std/haxe/Json.hx +++ b/std/haxe/Json.hx @@ -25,7 +25,7 @@ package haxe; /** Cross-platform JSON API: it will automatically use the optimized native API if available. Use `-D haxeJSON` to force usage of the Haxe implementation even if a native API is found: - This will provide extra encoding features such as enums (replaced by their index) and StringMaps. + This will provide extra encoding (but not decoding) features such as enums (replaced by their index) and StringMaps. @see https://haxe.org/manual/std-Json.html **/ diff --git a/std/haxe/MainLoop.hx b/std/haxe/MainLoop.hx index e854e39f..60f2f164 100644 --- a/std/haxe/MainLoop.hx +++ b/std/haxe/MainLoop.hx @@ -60,11 +60,6 @@ class MainEvent { @:access(haxe.MainEvent) class MainLoop { - #if (target.threaded && !cppia) - static var eventLoopHandler:Null; - static var mutex = new sys.thread.Mutex(); - static var mainThread = Thread.current(); - #end static var pending:MainEvent; @@ -94,7 +89,7 @@ class MainLoop { /** Add a pending event to be run into the main loop. **/ - public static function add(f:Void->Void, priority = 0):MainEvent@:privateAccess { + public static function add(f:Void->Void, priority = 0) : MainEvent { if (f == null) throw "Event function is null"; var e = new MainEvent(f, priority); @@ -103,29 +98,9 @@ class MainLoop { head.prev = e; e.next = head; pending = e; - injectIntoEventLoop(0); return e; } - static function injectIntoEventLoop(waitMs:Int) { - #if (target.threaded && !cppia) - mutex.acquire(); - if(eventLoopHandler != null) - mainThread.events.cancel(eventLoopHandler); - eventLoopHandler = mainThread.events.repeat( - () -> { - mainThread.events.cancel(eventLoopHandler); - var wait = tick(); - if(hasEvents()) { - injectIntoEventLoop(Std.int(wait * 1000)); - } - }, - waitMs - ); - mutex.release(); - #end - } - static function sortEvents() { // pending = haxe.ds.ListSort.sort(pending, function(e1, e2) return e1.nextRun > e2.nextRun ? -1 : 1); // we can't use directly ListSort because it requires prev/next to be public, which we don't want here diff --git a/std/haxe/Rest.hx b/std/haxe/Rest.hx index 656ec223..40a52a21 100644 --- a/std/haxe/Rest.hx +++ b/std/haxe/Rest.hx @@ -7,7 +7,7 @@ private typedef NativeRest = Array; /** A special type that represents a "rest" function argument. - + The special `...` syntax can be used for convenience and improved readability: ```haxe diff --git a/std/haxe/SysTools.hx b/std/haxe/SysTools.hx index 147f4e81..a99190fc 100644 --- a/std/haxe/SysTools.hx +++ b/std/haxe/SysTools.hx @@ -45,12 +45,12 @@ class SysTools { **/ public static function quoteWinArg(argument:String, escapeMetaCharacters:Bool):String { // If there is no space, tab, back-slash, or double-quotes, and it is not an empty string. - if (!~/^[^ \t\\"]+$/.match(argument)) { + if (!~/^(\/)?[^ \t\/\\"]+$/.match(argument)) { // Based on cpython's subprocess.list2cmdline(). // https://hg.python.org/cpython/file/50741316dd3a/Lib/subprocess.py#l620 var result = new StringBuf(); - var needquote = argument.indexOf(" ") != -1 || argument.indexOf("\t") != -1 || argument == ""; + var needquote = argument.indexOf(" ") != -1 || argument.indexOf("\t") != -1 || argument == "" || argument.indexOf("/") > 0; if (needquote) result.add('"'); diff --git a/std/haxe/Timer.hx b/std/haxe/Timer.hx index c641f46d..754294f0 100644 --- a/std/haxe/Timer.hx +++ b/std/haxe/Timer.hx @@ -40,6 +40,11 @@ import sys.thread.EventLoop; It is also possible to extend this class and override its `run()` method in the child class. + + Notice for threaded targets: + `Timer` instances require threads they were created in to run with Haxe's event loops. + Main thread of a Haxe program always contains an event loop. For other cases use + `sys.thread.Thread.createWithEventLoop` and `sys.thread.Thread.runWithEventLoop` methods. **/ class Timer { #if (flash || js) diff --git a/std/haxe/atomic/AtomicBool.hx b/std/haxe/atomic/AtomicBool.hx new file mode 100644 index 00000000..24549859 --- /dev/null +++ b/std/haxe/atomic/AtomicBool.hx @@ -0,0 +1,55 @@ +package haxe.atomic; + +#if !(target.atomics || core_api) +#error "Atomic operations are not supported on this target!" +#end + +/** + Atomic boolean. + (js) The Atomics and SharedArrayBuffer objects need to be available. Errors will be thrown if this is not the case. +**/ +@:coreApi +abstract AtomicBool(AtomicInt) { + private inline function toInt(v:Bool):Int { + return v ? 1 : 0; + } + + private inline function toBool(v:Int):Bool { + return v == 1; + } + + public inline function new(value:Bool):Void { + this = new AtomicInt(toInt(value)); + } + + /** + Atomically compares the value of `a` with `expected` and replaces `a` with `replacement` if they are equal.. + Returns the original value of `a`. + **/ + public inline function compareExchange(expected:Bool, replacement:Bool):Bool { + return toBool(this.compareExchange(toInt(expected), toInt(replacement))); + } + + /** + Atomically exchanges `a` with `value`. + Returns the original value of `a`. + **/ + public inline function exchange(value:Bool):Bool { + return toBool(this.exchange(toInt(value))); + } + + /** + Atomically fetches the value of `a`. + **/ + public inline function load():Bool { + return toBool(this.load()); + } + + /** + Atomically stores `value` into `a`. + Returns the value that has been stored. + **/ + public inline function store(value:Bool):Bool { + return toBool(this.store(toInt(value))); + } +} diff --git a/std/haxe/atomic/AtomicInt.hx b/std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..2c499322 --- /dev/null +++ b/std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,67 @@ +package haxe.atomic; + +#if !(target.atomics || core_api) +#error "This target does not support atomic operations." +#end + +/** + Atomic integer. + (js) The Atomics and SharedArrayBuffer objects need to be available. Errors will be thrown if this is not the case. +**/ +@:coreType +abstract AtomicInt { + public function new(value:Int):Void; + + /** + Atomically adds `b` to `a`. + Returns the original value of `a`. + **/ + public function add(b:Int):Int; + + /** + Atomically substracts `b` from `a`. + Returns the original value of `a`. + **/ + public function sub(b:Int):Int; + + /** + Atomically computes the bitwise and of `a` and `b` and stores it in `a`. + Returns the original value of `a`. + **/ + public function and(b:Int):Int; + + /** + Atomically computes the bitwise or of `a` and `b` and stores it in `a`. + Returns the original value of `a`. + **/ + public function or(b:Int):Int; + + /** + Atomically computes the bitwise xor of `a` and `b` and stores it in `a`. + Returns the original value of `a`. + **/ + public function xor(b:Int):Int; + + /** + Atomically compares the value of `a` with `expected` and replaces `a` with `replacement` if they are equal.. + Returns the original value of `a`. + **/ + public function compareExchange(expected:Int, replacement:Int):Int; + + /** + Atomically exchanges `a` with `value`. + Returns the original value of `a`. + **/ + public function exchange(value:Int):Int; + + /** + Atomically fetches the value of `a`. + **/ + public function load():Int; + + /** + Atomically stores `value` into `a`. + Returns the value that has been stored. + **/ + public function store(value:Int):Int; +} diff --git a/std/haxe/atomic/AtomicObject.hx b/std/haxe/atomic/AtomicObject.hx new file mode 100644 index 00000000..76cf2606 --- /dev/null +++ b/std/haxe/atomic/AtomicObject.hx @@ -0,0 +1,44 @@ +package haxe.atomic; + +#if !(target.atomics || core_api) +#error "This target does not support atomic operations." +#end + +#if (js || hxcpp) +#error "JavaScript and Hxcpp do not support AtomicObject" +#end + +/** + Atomic object. Use with care, this does not magically make it thread-safe to mutate objects. + Not supported on JavaScript or C++. +**/ +@:coreType +abstract AtomicObject { + public function new(value:T):Void; + + /** + Atomically compares the value of `a` with `expected` and replaces `a` with `replacement` if they are equal.. + Returns the original value of `a`. + + Note that comparison is done by reference, and not by value. + While this is expected for most objects, this might give unexpected behaviour for strings. + **/ + public function compareExchange(expected:T, replacement:T):T; + + /** + Atomically exchanges `a` with `value`. + Returns the original value of `a`. + **/ + public function exchange(value:T):T; + + /** + Atomically fetches the value of `a`. + **/ + public function load():T; + + /** + Atomically stores `value` into `a`. + Returns the value that has been stored. + **/ + public function store(value:T):T; +} diff --git a/std/haxe/display/Display.hx b/std/haxe/display/Display.hx index e5acd760..1fb1b191 100644 --- a/std/haxe/display/Display.hx +++ b/std/haxe/display/Display.hx @@ -295,6 +295,7 @@ typedef Metadata = { var platforms:Array; var targets:Array; var internal:Bool; + var ?origin:String; var ?links:Array; } diff --git a/std/haxe/display/JsonModuleTypes.hx b/std/haxe/display/JsonModuleTypes.hx index e546162a..503e4b40 100644 --- a/std/haxe/display/JsonModuleTypes.hx +++ b/std/haxe/display/JsonModuleTypes.hx @@ -163,6 +163,7 @@ enum abstract JsonBinopKind(String) { var OpInterval; var OpArrow; var OpIn; + var OpNullCoal; } typedef JsonBinop = { diff --git a/std/haxe/display/Server.hx b/std/haxe/display/Server.hx index e6e28b0b..63895758 100644 --- a/std/haxe/display/Server.hx +++ b/std/haxe/display/Server.hx @@ -41,6 +41,7 @@ class ServerMethods { static inline var ModuleMemory = new HaxeRequestMethod>("server/memory/module"); static inline var Modules = new HaxeRequestMethod>>("server/modules"); static inline var Module = new HaxeRequestMethod>("server/module"); + static inline var Type = new HaxeRequestMethod>>("server/type"); static inline var Files = new HaxeRequestMethod>>("server/files"); static inline var ModuleCreated = new HaxeRequestMethod>("server/moduleCreated"); } @@ -97,7 +98,9 @@ typedef JsonModule = { final types:Array; final file:String; final sign:String; + final dirty:Null; final dependencies:Array; + final dependents:Array; } typedef JsonServerFile = { @@ -108,7 +111,6 @@ typedef JsonServerFile = { } /* Memory */ - typedef HaxeMemoryResult = { final contexts:Array<{ final context:HaxeServerContext; @@ -165,3 +167,8 @@ typedef ContextParams = { typedef ModuleParams = ContextParams & { final path:String; } + +typedef TypeParams = ContextParams & { + final modulePath:String; + final typeName:String; +} diff --git a/std/haxe/ds/BalancedTree.hx b/std/haxe/ds/BalancedTree.hx index 315b176a..41d3abf6 100644 --- a/std/haxe/ds/BalancedTree.hx +++ b/std/haxe/ds/BalancedTree.hx @@ -227,7 +227,7 @@ class BalancedTree implements haxe.Constraints.IMap { } public function toString() { - return root == null ? '{}' : '{${root.toString()}}'; + return root == null ? "[]" : '[${root.toString()}]'; } /** @@ -264,6 +264,6 @@ class TreeNode { return this == null ? 0 : _height; public function toString() { - return (left == null ? "" : left.toString() + ", ") + '$key=$value' + (right == null ? "" : ", " + right.toString()); + return (left == null ? "" : left.toString() + ", ") + '$key => $value' + (right == null ? "" : ", " + right.toString()); } } diff --git a/std/haxe/ds/EnumValueMap.hx b/std/haxe/ds/EnumValueMap.hx index ef231864..0bbed6d0 100644 --- a/std/haxe/ds/EnumValueMap.hx +++ b/std/haxe/ds/EnumValueMap.hx @@ -33,11 +33,25 @@ class EnumValueMap extends haxe.ds.BalancedTree implements var d = k1.getIndex() - k2.getIndex(); if (d != 0) return d; + #if hl + var a1 = @:privateAccess Type._enumParameters(k1); + var a2 = @:privateAccess Type._enumParameters(k2); + var ld = a1.length - a2.length; + if (ld != 0) + return ld; + for (i in 0...a1.length) { + var d = compareArg(a1[i], a2[i]); + if (d != 0) + return d; + } + return 0; + #else var p1 = k1.getParameters(); var p2 = k2.getParameters(); if (p1.length == 0 && p2.length == 0) return 0; return compareArgs(p1, p2); + #end } function compareArgs(a1:Array, a2:Array):Int { diff --git a/std/haxe/ds/Map.hx b/std/haxe/ds/Map.hx index 344cccf4..59d9df38 100644 --- a/std/haxe/ds/Map.hx +++ b/std/haxe/ds/Map.hx @@ -24,9 +24,7 @@ package haxe.ds; import haxe.ds.StringMap; import haxe.ds.IntMap; -import haxe.ds.HashMap; import haxe.ds.ObjectMap; -import haxe.ds.WeakMap; import haxe.ds.EnumValueMap; import haxe.Constraints.IMap; diff --git a/std/haxe/ds/Vector.hx b/std/haxe/ds/Vector.hx index 61014e38..08c1a793 100644 --- a/std/haxe/ds/Vector.hx +++ b/std/haxe/ds/Vector.hx @@ -41,7 +41,7 @@ private typedef VectorData = eval.Vector #else Array - #end + #end; /** A Vector is a storage of fixed size. It can be faster than Array on some @@ -61,7 +61,7 @@ abstract Vector(VectorData) { If `length` is less than or equal to 0, the result is unspecified. **/ - public inline function new(length:Int) { + extern overload public inline function new(length:Int) { #if flash10 this = new flash.Vector(length, true); #elseif neko @@ -75,7 +75,7 @@ abstract Vector(VectorData) { #elseif cpp this = NativeArray.create(length); #elseif python - this = python.Syntax.code("[{0}]*{1}", null, length); + this = python.Syntax.code("([{0}]*{1})", null, length); #elseif lua this = untyped __lua_table__({length: length}); #elseif eval @@ -86,6 +86,43 @@ abstract Vector(VectorData) { #end } + /** + Creates a new Vector of length `length` filled with `defaultValue` elements. + + Can be faster than `new Vector(length)` for iteration on some targets for non-nullable elements. + + If `length` is less than or equal to 0, the result is unspecified. + **/ + extern overload public inline function new(length:Int, defaultValue:T):Vector { + #if js + this = [for (_ in 0...length) defaultValue]; + #elseif python + this = python.Syntax.code("([{0}]*{1})", defaultValue, length); + #else + + #if flash10 + this = new flash.Vector(length, true); + #elseif neko + this = untyped __dollar__amake(length); + #elseif cs + this = new cs.NativeArray(length); + #elseif java + this = new java.NativeArray(length); + #elseif cpp + this = NativeArray.create(length); + #elseif lua + this = untyped __lua_table__({length: length}); + #elseif eval + this = new eval.Vector(length); + #else + this = []; + untyped this.length = length; + #end + fill(defaultValue); + + #end + } + /** Returns the value at index `index`. @@ -141,6 +178,12 @@ abstract Vector(VectorData) { #end } + /** + Sets all `length` elements of `this` Vector to `value`. + **/ + public inline function fill(value:T):Void + for (i in 0...length) this[i] = value; + /** Copies `length` of elements from `src` Vector, beginning at `srcPos` to `dest` Vector, beginning at `destPos` diff --git a/std/haxe/format/JsonPrinter.hx b/std/haxe/format/JsonPrinter.hx index e7536328..c6ab3ee5 100644 --- a/std/haxe/format/JsonPrinter.hx +++ b/std/haxe/format/JsonPrinter.hx @@ -128,8 +128,8 @@ class JsonPrinter { } else classString(v); case TEnum(_): - var i:Dynamic = Type.enumIndex(v); - add(i); + var i = Type.enumIndex(v); + add(Std.string(i)); case TBool: add(#if (php || jvm || hl) (v ? 'true' : 'false') #else v #end); case TNull: diff --git a/std/haxe/http/HttpJs.hx b/std/haxe/http/HttpJs.hx index 316a5e25..5f5eb519 100644 --- a/std/haxe/http/HttpJs.hx +++ b/std/haxe/http/HttpJs.hx @@ -30,6 +30,7 @@ import haxe.io.Bytes; class HttpJs extends haxe.http.HttpBase { public var async:Bool; public var withCredentials:Bool; + public var responseHeaders:Map; var req:js.html.XMLHttpRequest; @@ -53,6 +54,7 @@ class HttpJs extends haxe.http.HttpBase { public override function request(?post:Bool) { this.responseAsString = null; this.responseBytes = null; + this.responseHeaders = null; var r = req = js.Browser.createXMLHttpRequest(); var onreadystatechange = function(_) { if (r.readyState != 4) @@ -73,6 +75,21 @@ class HttpJs extends haxe.http.HttpBase { onStatus(s); if (s != null && s >= 200 && s < 400) { req = null; + + // split headers and remove the last \r\n\r\n + var headers = r.getAllResponseHeaders().split('\r\n'); + headers = headers.filter(h -> h != ''); + + // store response headers + responseHeaders = new haxe.ds.StringMap(); + for (hline in headers) { + var a = hline.split(": "); + var hname = a.shift(); + var hval = if (a.length == 1) a[0] else a.join(": "); + hval = StringTools.ltrim(StringTools.rtrim(hval)); + responseHeaders.set(hname, hval); + } + success(Bytes.ofData(r.response)); } else if (s == null || (s == 0 && r.response == null)) { req = null; diff --git a/std/haxe/http/HttpNodeJs.hx b/std/haxe/http/HttpNodeJs.hx index cfb305ef..d845d315 100644 --- a/std/haxe/http/HttpNodeJs.hx +++ b/std/haxe/http/HttpNodeJs.hx @@ -27,6 +27,8 @@ import js.node.Buffer; import haxe.io.Bytes; class HttpNodeJs extends haxe.http.HttpBase { + public var responseHeaders:Map; + var req:js.node.http.ClientRequest; public function new(url:String) { @@ -47,6 +49,7 @@ class HttpNodeJs extends haxe.http.HttpBase { public override function request(?post:Bool) { responseAsString = null; responseBytes = null; + responseHeaders = null; var parsedUrl = new js.node.url.URL(url); var secure = (parsedUrl.protocol == "https:"); var host = parsedUrl.hostname; @@ -97,6 +100,14 @@ class HttpNodeJs extends haxe.http.HttpBase { var buf = (data.length == 1 ? data[0] : Buffer.concat(data)); responseBytes = Bytes.ofData(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength)); req = null; + + // store response headers + responseHeaders = new haxe.ds.StringMap(); + for (field in Reflect.fields(res.headers)) + { + responseHeaders.set(field, Reflect.field(res.headers, field)); + } + if (s != null && s >= 200 && s < 400) { success(responseBytes); } else { diff --git a/std/haxe/io/Mime.hx b/std/haxe/io/Mime.hx index 82a10b35..8aa7ec11 100644 --- a/std/haxe/io/Mime.hx +++ b/std/haxe/io/Mime.hx @@ -318,4 +318,5 @@ enum abstract Mime(String) from String to String { var ApplicationXCompress = 'application/x-compress'; var MultipartXZip = 'multipart/x-zip'; var TextXScriptZsh = 'text/x-script.zsh'; + var ImageAvif = 'image/avif'; } diff --git a/std/haxe/iterators/ArrayKeyValueIterator.hx b/std/haxe/iterators/ArrayKeyValueIterator.hx index 5261b7d3..ab382bb1 100644 --- a/std/haxe/iterators/ArrayKeyValueIterator.hx +++ b/std/haxe/iterators/ArrayKeyValueIterator.hx @@ -27,18 +27,15 @@ class ArrayKeyValueIterator { var current:Int = 0; var array:Array; - #if !hl inline #end - public function new(array:Array) { + public inline function new(array:Array) { this.array = array; } - #if !hl inline #end - public function hasNext():Bool { + public inline function hasNext():Bool { return current < array.length; } - #if !hl inline #end - public function next():{key:Int,value:T} { + public inline function next():{key:Int,value:T} { return {value:array[current], key:current++}; } } diff --git a/std/haxe/macro/CompilationServer.hx b/std/haxe/macro/CompilationServer.hx index fe58998c..6952d89f 100644 --- a/std/haxe/macro/CompilationServer.hx +++ b/std/haxe/macro/CompilationServer.hx @@ -52,6 +52,10 @@ enum abstract ModuleCheckPolicy(Int) { of the current module file. **/ var NoCheckShadowing = 3; + /** + Retype the module's contents if its file is invalidated. This is currently experimental. + **/ + var Retype = 4; } enum abstract ContextOptions(Int) { diff --git a/std/haxe/macro/Compiler.hx b/std/haxe/macro/Compiler.hx index 2b6d7003..24655a8b 100644 --- a/std/haxe/macro/Compiler.hx +++ b/std/haxe/macro/Compiler.hx @@ -22,6 +22,7 @@ package haxe.macro; +import haxe.display.Display; import haxe.macro.Expr; /** @@ -69,6 +70,7 @@ class Compiler { **/ public static function define(flag:String, ?value:String) { #if (neko || eval) + Context.assertInitMacro(); load("define", 2)(flag, value); #end } @@ -136,6 +138,7 @@ class Compiler { **/ public static function addClassPath(path:String) { #if (neko || eval) + Context.assertInitMacro(); load("add_class_path", 1)(path); #end } @@ -162,6 +165,19 @@ class Compiler { #end } + /** + Returns all the configuration settings applied to the compiler. + + Usage of this function outside a macro context returns `null`. + **/ + public static function getConfiguration():Null { + #if (neko || eval) + return load("get_configuration", 0)(); + #else + return null; + #end + } + /** Adds a native library depending on the platform (e.g. `-swf-lib` for Flash). @@ -169,6 +185,7 @@ class Compiler { **/ public static function addNativeLib(name:String) { #if (neko || eval) + Context.assertInitMacro(); load("add_native_lib", 1)(name); #end } @@ -201,69 +218,77 @@ class Compiler { @param strict If true and given package wasn't found in any of class paths, fail with an error. **/ public static function include(pack:String, ?rec = true, ?ignore:Array, ?classPaths:Array, strict = false) { - var ignoreWildcard:Array = []; - var ignoreString:Array = []; - if (ignore != null) { - for (ignoreRule in ignore) { - if (StringTools.endsWith(ignoreRule, "*")) { - ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1)); - } else { - ignoreString.push(ignoreRule); + function include(pack:String, ?rec = true, ?ignore:Array, ?classPaths:Array, strict = false) { + var ignoreWildcard:Array = []; + var ignoreString:Array = []; + if (ignore != null) { + for (ignoreRule in ignore) { + if (StringTools.endsWith(ignoreRule, "*")) { + ignoreWildcard.push(ignoreRule.substr(0, ignoreRule.length - 1)); + } else { + ignoreString.push(ignoreRule); + } } } - } - var skip = if (ignore == null) { - function(c) return false; - } else { - function(c:String) { - if (Lambda.has(ignoreString, c)) - return true; - for (ignoreRule in ignoreWildcard) - if (StringTools.startsWith(c, ignoreRule)) + var skip = if (ignore == null) { + function(c) return false; + } else { + function(c:String) { + if (Lambda.has(ignoreString, c)) return true; - return false; + for (ignoreRule in ignoreWildcard) + if (StringTools.startsWith(c, ignoreRule)) + return true; + return false; + } } - } - var displayValue = Context.definedValue("display"); - if (classPaths == null) { - classPaths = Context.getClassPath(); - // do not force inclusion when using completion - switch (displayValue) { - case null: - case "usage": - case _: - return; + var displayValue = Context.definedValue("display"); + if (classPaths == null) { + classPaths = Context.getClassPath(); + // do not force inclusion when using completion + switch (displayValue) { + case null: + case "usage": + case _: + return; + } + // normalize class path + for (i in 0...classPaths.length) { + var cp = StringTools.replace(classPaths[i], "\\", "/"); + if (StringTools.endsWith(cp, "/")) + cp = cp.substr(0, -1); + if (cp == "") + cp = "."; + classPaths[i] = cp; + } } - // normalize class path - for (i in 0...classPaths.length) { - var cp = StringTools.replace(classPaths[i], "\\", "/"); - if (StringTools.endsWith(cp, "/")) - cp = cp.substr(0, -1); - if (cp == "") - cp = "."; - classPaths[i] = cp; + var prefix = pack == '' ? '' : pack + '.'; + var found = false; + for (cp in classPaths) { + var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/"); + if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path)) + continue; + found = true; + for (file in sys.FileSystem.readDirectory(path)) { + if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) { + if( file == "import.hx" ) continue; + var cl = prefix + file.substr(0, file.length - 3); + if (skip(cl)) + continue; + Context.getModule(cl); + } else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file)) + include(prefix + file, true, ignore, classPaths); + } } + if (strict && !found) + Context.error('Package "$pack" was not found in any of class paths', Context.currentPos()); } - var prefix = pack == '' ? '' : pack + '.'; - var found = false; - for (cp in classPaths) { - var path = pack == '' ? cp : cp + "/" + pack.split(".").join("/"); - if (!sys.FileSystem.exists(path) || !sys.FileSystem.isDirectory(path)) - continue; - found = true; - for (file in sys.FileSystem.readDirectory(path)) { - if (StringTools.endsWith(file, ".hx") && file.substr(0, file.length - 3).indexOf(".") < 0) { - if( file == "import.hx" ) continue; - var cl = prefix + file.substr(0, file.length - 3); - if (skip(cl)) - continue; - Context.getModule(cl); - } else if (rec && sys.FileSystem.isDirectory(path + "/" + file) && !skip(prefix + file)) - include(prefix + file, true, ignore, classPaths); - } + + if (!Context.initMacrosDone()) { + Context.onAfterInitMacros(() -> include(pack, rec, ignore, classPaths, strict)); + } else { + include(pack, rec, ignore, classPaths, strict); } - if (strict && !found) - Context.error('Package "$pack" was not found in any of class paths', Context.currentPos()); } /** @@ -450,6 +475,44 @@ class Compiler { #end } + /** + Reference a json file describing user-defined metadata + See https://github.com/HaxeFoundation/haxe/blob/development/src-json/meta.json + **/ + public static function registerMetadataDescriptionFile(path:String, ?source:String):Void { + var f = sys.io.File.getContent(path); + var content:Array = haxe.Json.parse(f); + for (m in content) registerCustomMetadata(m, source); + } + + /** + Reference a json file describing user-defined defines + See https://github.com/HaxeFoundation/haxe/blob/development/src-json/define.json + **/ + public static function registerDefinesDescriptionFile(path:String, ?source:String):Void { + var f = sys.io.File.getContent(path); + var content:Array = haxe.Json.parse(f); + for (d in content) registerCustomDefine(d, source); + } + + /** + Register a custom medatada for documentation and completion purposes + **/ + public static function registerCustomMetadata(meta:MetadataDescription, ?source:String):Void { + #if (neko || eval) + load("register_metadata_impl", 2)(meta, source); + #end + } + + /** + Register a custom define for documentation purposes + **/ + public static function registerCustomDefine(define:DefineDescription, ?source:String):Void { + #if (neko || eval) + load("register_define_impl", 2)(define, source); + #end + } + /** Change the default JS output by using a custom generator callback **/ @@ -577,3 +640,108 @@ enum abstract NullSafetyMode(String) to String { **/ var StrictThreaded; } + +typedef MetadataDescription = { + final metadata:String; + final doc:String; + + /** + External resources for more information about this metadata. + **/ + @:optional final links:Array; + + /** + List (small description) of parameters that this metadata accepts. + **/ + @:optional final params:Array; + + /** + Haxe target(s) for which this metadata is used. + **/ + @:optional final platforms:Array; + + /** + Places where this metadata can be applied. + **/ + @:optional final targets:Array; +} + +typedef DefineDescription = { + final define:String; + final doc:String; + + /** + External resources for more information about this define. + **/ + @:optional final links:Array; + + /** + List (small description) of parameters that this define accepts. + **/ + @:optional final params:Array; + + /** + Haxe target(s) for which this define is used. + **/ + @:optional final platforms:Array; +} + +typedef CompilerConfiguration = { + /** + The version integer of the current Haxe compiler build. + **/ + final version:Int; + + /** + Returns an array of the arguments passed to the compiler from either the `.hxml` file or the command line. + **/ + final args:Array; + + /** + If `--debug` mode is enabled, this is `true`. + **/ + final debug:Bool; + + /** + If `--verbose` mode is enabled, this is `true`. + **/ + final verbose:Bool; + + /** + If `--no-opt` is enabled, this is `false`. + **/ + final foptimize:Bool; + + /** + The target platform. + **/ + final platform:haxe.display.Display.Platform; + + /** + The compilation configuration for the target platform. + **/ + final platformConfig:PlatformConfig; + + /** + A list of paths being used for the standard library. + **/ + final stdPath:Array; + + /** + The path of the class passed using the `-main` argument. + **/ + final mainClass:TypePath; + + /** + Special access rules for packages depending on the compiler configuration. + + For example, the "java" package is "Forbidden" when the target platform is Python. + **/ + final packageRules:Map; +} + +enum PackageRule { + Forbidden; + Directory(path:String); + Remap(path:String); +} diff --git a/std/haxe/macro/ComplexTypeTools.hx b/std/haxe/macro/ComplexTypeTools.hx index efe5144c..fd838f4f 100644 --- a/std/haxe/macro/ComplexTypeTools.hx +++ b/std/haxe/macro/ComplexTypeTools.hx @@ -39,7 +39,7 @@ class ComplexTypeTools { static public function toString(c:ComplexType):String return new Printer().printComplexType(c); - #if (macro || display) + #if macro /** Returns a type corresponding to `c`. diff --git a/std/haxe/macro/Context.hx b/std/haxe/macro/Context.hx index 7df7072b..73970fa3 100644 --- a/std/haxe/macro/Context.hx +++ b/std/haxe/macro/Context.hx @@ -47,45 +47,61 @@ class Context { Displays a compilation error `msg` at the given `Position` `pos` and aborts the current macro call. **/ - public static function error(msg:String, pos:Position):Dynamic { - return load("error", 2)(msg, pos); + public static function error(msg:String, pos:Position, ?depth:Int = 0):Dynamic { + return load("error", 2)(msg, pos, depth); } /** Displays a compilation error `msg` at the given `Position` `pos` and aborts the compilation. **/ - public static function fatalError(msg:String, pos:Position):Dynamic { - return load("fatal_error", 2)(msg, pos); + public static function fatalError(msg:String, pos:Position, ?depth:Int = 0):Dynamic { + return load("fatal_error", 2)(msg, pos, depth); + } + + /** + Displays a compilation error `msg` at the given `Position` `pos` + without aborting the current macro call. + **/ + public static function reportError(msg:String, pos:Position, ?depth:Int = 0):Void { + load("report_error", 2)(msg, pos, depth); } /** Displays a compilation warning `msg` at the given `Position` `pos`. **/ - public static function warning(msg:String, pos:Position) { - load("warning", 2)(msg, pos); + public static function warning(msg:String, pos:Position, ?depth:Int = 0) { + load("warning", 2)(msg, pos, depth); } /** Displays a compilation info `msg` at the given `Position` `pos`. **/ - public static function info(msg:String, pos:Position) { - load("info", 2)(msg, pos); + public static function info(msg:String, pos:Position, ?depth:Int = 0) { + load("info", 2)(msg, pos, depth); } /** Gets a list of all current compilation info/warning messages. **/ - public static function getMessages() : Array { - return load("get_messages",0)(); + public static function getMessages():Array { + return load("get_messages", 0)(); } /** Filters all current info/warning messages. Filtered out messages will not be displayed by the compiler. **/ - public static function filterMessages( predicate : Message -> Bool ) { - load("filter_messages",1)(predicate); + public static function filterMessages(predicate:Message->Bool) { + load("filter_messages", 1)(predicate); + } + + /** + Check if compiler is past initializations macros or not. + When it is, configuration phase is over and parsing/typing can start. + **/ + public static function initMacrosDone():Bool { + return load("init_macros_done", 0)(); } /** @@ -119,6 +135,10 @@ class Context { return load("contains_display_position", 1)(pos); } + public static function getDisplayMode():DisplayMode { + return load("get_display_mode", 0)(); + } + /** Returns the position at which the macro was called. **/ @@ -126,6 +146,14 @@ class Context { return load("current_pos", 0)(); } + /** + Get the call stack (excluding the call to `Context.getMacroStack()` + that led to current macro. + **/ + public static function getMacroStack():Array { + return load("get_macro_stack", 0)(); + } + /** Returns the type which is expected at the place the macro is called. @@ -136,6 +164,7 @@ class Context { macro is not an expression-macro. **/ public static function getExpectedType():Null { + assertInitMacrosDone(false); return load("get_expected_type", 0)(); } @@ -279,8 +308,13 @@ class Context { declared class path has priority. If no type can be found, an exception of type `String` is thrown. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function getType(name:String):Type { + assertInitMacrosDone(); return load("get_type", 1)(name); } @@ -292,11 +326,44 @@ class Context { declared class path has priority. If no module can be found, an exception of type `String` is thrown. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function getModule(name:String):Array { + assertInitMacrosDone(); return load("get_module", 1)(name); } + /** + Returns the typed expression of the call to the main function. + + This function will only work in the generation phase. Any calls + made outside a function passed to `haxe.macro.Context.onGenerate` + or `haxe.macro.Context.onAfterGenerate` will return `null`. + **/ + public static function getMainExpr():Null { + return load("get_main_expr", 0)(); + } + + /** + Returns an array of module types to be generated in the output. + + This list may change depending on the phase of compilation and + should not be treated as conclusive until the generation phase. + + Modifying the returned array has no effect on the compilation. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. + **/ + public static function getAllModuleTypes():Array { + assertInitMacrosDone(); + return load("get_module_types", 0)(); + } + /** Parses `expr` as Haxe code, returning the corresponding AST. @@ -334,6 +401,7 @@ class Context { Returns a hashed MD5 signature of value `v`. **/ public static function signature(v:Dynamic):String { + assertInitMacrosDone(false); return load("signature", 1)(v); } @@ -382,6 +450,19 @@ class Context { load("on_after_typing", 1)(callback); } + /** + Adds a callback function `callback` which is invoked after the compiler + is done running initialization macros, when typing begins. + + `onAfterInitMacros` should be used to delay typer-dependant code from + your initalization macros, to properly separate configuration phase and + actual typing. + **/ + public static function onAfterInitMacros(callback:Void->Void):Void { + assertInitMacro(); + load("on_after_init_macros", 1)(callback); + } + /** Adds a callback function `callback` which is invoked when a type name cannot be resolved. @@ -399,8 +480,13 @@ class Context { Typing the expression may result in a compiler error which can be caught using `try ... catch`. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function typeof(e:Expr):Type { + assertInitMacrosDone(); return load("typeof", 1)(e); } @@ -408,9 +494,17 @@ class Context { Types expression `e` and returns the corresponding `TypedExpr`. Typing the expression may result in a compiler error which can be - caught using `try ... catch`. + caught using `try ... catch`. Note that not all compiler errors can + be caught this way because the compiler might delay various checks + to a later stage, at which point the exception handler is no longer + active. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function typeExpr(e:Expr):TypedExpr { + assertInitMacrosDone(); return load("type_expr", 1)(e); } @@ -420,8 +514,13 @@ class Context { Resolving the type may result in a compiler error which can be caught using `try ... catch`. Resolution is performed based on the current context in which the macro is called. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function resolveType(t:ComplexType, p:Position):Type { + assertInitMacrosDone(); return load("resolve_type", 2)(t, p); } @@ -436,8 +535,13 @@ class Context { /** Tries to unify `t1` and `t2` and returns `true` if successful. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function unify(t1:Type, t2:Type):Bool { + assertInitMacrosDone(); return load("unify", 2)(t1, t2); } @@ -445,8 +549,13 @@ class Context { Follows a type. See `haxe.macro.TypeTools.follow` for details. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function follow(t:Type, ?once:Bool):Type { + assertInitMacrosDone(); return load("follow", 2)(t, once); } @@ -454,8 +563,13 @@ class Context { Follows a type, including abstracts' underlying implementation See `haxe.macro.TypeTools.followWithAbstracts` for details. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function followWithAbstracts(t:Type, once:Bool = false):Type { + assertInitMacrosDone(); return load("follow_with_abstracts", 2)(t, once); } @@ -514,11 +628,31 @@ class Context { If `moduleDependency` is given and is not `null`, it should contain a module path that will be used as a dependency for the newly defined module instead of the current module. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function defineType(t:TypeDefinition, ?moduleDependency:String):Void { + assertInitMacrosDone(); load("define_type", 2)(t, moduleDependency); } + /** + Creates and returns a new instance of monomorph (`TMono`) type. + + Returned monomorph can be used with e.g. `Context.unify` to make the compiler + bind the monomorph to an actual type and let macro further process the resulting type. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. + **/ + public static function makeMonomorph():Type { + assertInitMacrosDone(); + return load("make_monomorph", 0)(); + } + /** Defines a new module as `modulePath` with several `TypeDefinition` `types`. This is analogous to defining a .hx file. @@ -526,12 +660,17 @@ class Context { The individual `types` can reference each other and any identifier respects the `imports` and `usings` as usual, expect that imports are not allowed to have `.*` wildcards or `as s` shorthands. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function defineModule(modulePath:String, types:Array, ?imports:Array, ?usings:Array):Void { if (imports == null) imports = []; if (usings == null) usings = []; + assertInitMacrosDone(); load("define_module", 4)(modulePath, types, imports, usings); } @@ -539,8 +678,13 @@ class Context { Returns a syntax-level expression corresponding to typed expression `t`. This process may lose some information. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function getTypedExpr(t:Type.TypedExpr):Expr { + assertInitMacrosDone(); return load("get_typed_expr", 1)(t); } @@ -555,8 +699,13 @@ class Context { that is reset between compilations, so care should be taken when storing the expression returned by this method in a static variable and using the compilation server. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function storeTypedExpr(t:Type.TypedExpr):Expr { + assertInitMacrosDone(); return load("store_typed_expr", 1)(t); } @@ -574,11 +723,29 @@ class Context { that is reset between compilations, so care should be taken when storing the expression returned by this method in a static variable and using the compilation server. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function storeExpr(e:Expr):Expr { + assertInitMacrosDone(); return load("store_expr", 1)(e); } + /** + This function works like `storeExpr`, but also returns access to the expression's + type through the `type` field of the return value. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. + **/ + public static function typeAndStoreExpr(e:Expr):{final type:Type.Ref; final expr:Expr;} { + assertInitMacrosDone(); + return load("type_and_store_expr", 1)(e); + } + /** Manually adds a dependency between module `modulePath` and an external file `externFile`. @@ -587,8 +754,13 @@ class Context { `externFile` has changed. Has no effect if the compilation cache is not used. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. **/ public static function registerModuleDependency(modulePath:String, externFile:String) { + assertInitMacrosDone(); load("register_module_dependency", 2)(modulePath, externFile); } @@ -606,10 +778,47 @@ class Context { stopTimer(); ``` **/ - public static function timer(id:String):()->Void { + public static function timer(id:String):() -> Void { return load("timer", 1)(id); } + /** + Executes `code` in a context that has `imports` and `usings` added. + + This is equivalent to temporarily having `import` and `using` statements in a file. These + are only active during the execution of `code` and do not affect anything afterwards. This + is true even if `code` throws an exception. + + If any argument is `null`, the result is unspecified. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. + **/ + public static function withImports(imports:Array, usings:Array, code:() -> X):X { + assertInitMacrosDone(); + return load("with_imports", 3)(imports, usings, code); + } + + + /** + Executes `code` in a context that has some compiler options set, restore the compiler to its + default behavior afterwards. + + `allowInlining`: enable or disable inlining during typing with `typeExpr`. + + `allowTransform`: when disabled, the code typed with `typeExpr` will be almost exactly the same + as the input code. This will disable some abstract types transformations. + + Usage of this function from initialization macros is deprecated and may + cause compilation server issues. Use `Context.onAfterInitMacros` to + run your code once typer is ready to be used. + **/ + public static function withOptions(options:{?allowInlining:Bool,?allowTransform:Bool}, code : () -> X) : X { + assertInitMacrosDone(); + return load("with_options", 2)(options, code); + } + @:deprecated public static function registerModuleReuseCall(modulePath:String, macroCall:String) { throw "This method is no longer supported. See https://github.com/HaxeFoundation/haxe/issues/5746"; @@ -641,5 +850,33 @@ class Context { private static function sExpr(e:TypedExpr, pretty:Bool):String { return haxe.macro.Context.load("s_expr", 2)(e, pretty); } + + @:allow(haxe.macro.Compiler) + private static function assertInitMacro():Void { + if (initMacrosDone()) { + var stack = getMacroStack(); + + warning( + "This API should only be used from initialization macros.", + if (stack.length > 2) stack[2] else currentPos() + ); + } + } + + private static function assertInitMacrosDone(includeSuggestion = true):Void { + #if haxe_next + if (!initMacrosDone()) { + var stack = getMacroStack(); + var suggestion = includeSuggestion + ? "\nUse `Context.onAfterInitMacros` to register a callback to run when context is ready." + : ""; + + warning( + "Cannot use this API from initialization macros." + suggestion, + if (stack.length > 2) stack[2] else currentPos() + ); + } + #end + } #end } diff --git a/std/haxe/macro/DisplayMode.hx b/std/haxe/macro/DisplayMode.hx new file mode 100644 index 00000000..a177f960 --- /dev/null +++ b/std/haxe/macro/DisplayMode.hx @@ -0,0 +1,15 @@ +package haxe.macro; + +enum DisplayMode { + None; + Default; + Definition; + TypeDefinition; + Implementation; + Package; + Hover; + References(withDefinition:Bool, findDescendants:Bool, findBase:Bool); + ModuleSymbols; + WorkspaceSymbols(filter:String); + Signature; +} diff --git a/std/haxe/macro/ExampleJSGenerator.hx b/std/haxe/macro/ExampleJSGenerator.hx index 0468db75..05d7ec47 100644 --- a/std/haxe/macro/ExampleJSGenerator.hx +++ b/std/haxe/macro/ExampleJSGenerator.hx @@ -231,7 +231,13 @@ class ExampleJSGenerator { public function generate() { print("var $_, $hxClasses = $hxClasses || {}, $estr = function() { return js.Boot.__string_rec(this,''); }"); newline(); - print("function $bind(o,m) { var f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; return f; };"); + print( + #if (js_es < 5) + "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = function(){ return f.method.apply(f.scope, arguments); }; f.scope = o; f.method = m; o.hx__closures__[m.__id__] = f; } return f; }" + #else + "function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }" + #end + ); newline(); for (t in api.types) genType(t); diff --git a/std/haxe/macro/Expr.hx b/std/haxe/macro/Expr.hx index cd66f6f0..c2bce319 100644 --- a/std/haxe/macro/Expr.hx +++ b/std/haxe/macro/Expr.hx @@ -60,12 +60,12 @@ enum Constant { /** Represents an integer literal. **/ - CInt(v:String); + CInt(v:String, ?s:String); /** Represents a float literal. **/ - CFloat(f:String); + CFloat(f:String, ?s:String); /** Represents a string literal. @@ -214,6 +214,11 @@ enum Binop { `in` **/ OpIn; + + /** + `??` + **/ + OpNullCoal; } /** @@ -252,6 +257,11 @@ enum Unop { OpSpread; } +enum EFieldKind { + Normal; + Safe; +} + /** Represents a node in the AST. @see https://haxe.org/manual/macro-reification-expression.html @@ -321,6 +331,11 @@ typedef Var = { **/ var ?isFinal:Bool; + /** + Whether or not the variable is static. + **/ + var ?isStatic:Bool; + /** Metadata associatied with the variable, if available. **/ @@ -424,8 +439,10 @@ enum ExprDef { /** Field access on `e.field`. + + If `kind` is null, it is equal to Normal. **/ - EField(e:Expr, field:String); + EField(e:Expr, field:String, ?kind:EFieldKind); /** Parentheses `(e)`. @@ -545,11 +562,6 @@ enum ExprDef { **/ EDisplay(e:Expr, displayKind:DisplayKind); - /** - Used internally to provide completion. - **/ - EDisplayNew(t:TypePath); - /** A `(econd) ? eif : eelse` expression. **/ @@ -681,6 +693,11 @@ typedef TypeParamDecl = { **/ var ?constraints:Array; + /** + The optional default type of the type parameter. + **/ + var ?defaultType:Null; + /** The optional parameters of the type parameter. **/ @@ -994,6 +1011,11 @@ class Error extends Exception { **/ public var pos:Position; + /** + Child error messages, if any. + **/ + private var childErrors:Array; + /** Instantiates an error with given message and position. **/ diff --git a/std/haxe/macro/ExprTools.hx b/std/haxe/macro/ExprTools.hx index edce5d42..33208ae2 100644 --- a/std/haxe/macro/ExprTools.hx +++ b/std/haxe/macro/ExprTools.hx @@ -70,7 +70,7 @@ class ExprTools { **/ static public function iter(e:Expr, f:Expr->Void):Void { switch (e.expr) { - case EConst(_), EContinue, EBreak, EDisplayNew(_): + case EConst(_), EContinue, EBreak: case EField(e, _), EParenthesis(e), EUntyped(e), EThrow(e), EDisplay(e, _), ECheckType(e, _), EUnop(_, _, e), ECast(e, _), EIs(e, _) | EMeta(_, e): f(e); case EArray(e1, e2), EWhile(e1, e2, _), EBinop(_, e1, e2), EFor(e1, e2): @@ -176,7 +176,7 @@ class ExprTools { case EDisplay(e, dk): EDisplay(f(e), dk); case ETernary(econd, eif, eelse): ETernary(f(econd), f(eif), f(eelse)); case ECheckType(e, t): ECheckType(f(e), t); - case EDisplayNew(_), EContinue, EBreak: + case EContinue, EBreak: e.expr; case ETry(e, catches): var ret = []; diff --git a/std/haxe/macro/MacroStringTools.hx b/std/haxe/macro/MacroStringTools.hx index 83044a07..85e12b9b 100644 --- a/std/haxe/macro/MacroStringTools.hx +++ b/std/haxe/macro/MacroStringTools.hx @@ -32,7 +32,7 @@ import haxe.macro.Expr; @:hlNative("macro") #end class MacroStringTools { - #if (macro || display) + #if macro /** Formats `String` `s` using the usual interpolation rules. diff --git a/std/haxe/macro/PlatformConfig.hx b/std/haxe/macro/PlatformConfig.hx new file mode 100644 index 00000000..64d4d645 --- /dev/null +++ b/std/haxe/macro/PlatformConfig.hx @@ -0,0 +1,206 @@ +package haxe.macro; + +import haxe.macro.Expr; + +/** + Represents the internal structure generated with options assigned based on + the target platform. + + Warning: `PlatformConfig` and the typedefs unique to its fields correspond to + compiler-internal data structures and might change in minor Haxe releases in + order to adapt to internal changes. +**/ +typedef PlatformConfig = { + /** + Has a static type system, with not-nullable basic types (Int/Float/Bool) + **/ + final staticTypeSystem:Bool; + + /** + Has access to the "sys" package + **/ + final sys:Bool; + + /** + Captured variables handling (see before) + **/ + final capturePolicy:CapturePolicy; + + /** + When calling a method with optional args, do we replace the missing args with "null" constants + **/ + final padNulls:Bool; + + /** + Add a final return to methods not having one already - prevent some compiler warnings + **/ + final addFinalReturn:Bool; + + /** + Does the platform natively support overloaded functions + **/ + final overloadFunctions:Bool; + + /** + Can the platform use default values for non-nullable arguments + **/ + final canSkipNonNullableArgument:Bool; + + /** + Type paths that are reserved on the platform + **/ + final reservedTypePaths:Array; + + /** + Supports function == function + **/ + final supportsFunctionEquality:Bool; + + /** + Uses utf16 encoding with ucs2 api + **/ + final usesUtf16:Bool; + + /** + Target supports accessing `this` before calling `super(...)` + **/ + final thisBeforeSuper:Bool; + + /** + Target supports threads + **/ + final supportsThreads:Bool; + + /** + Target supports Unicode + **/ + final supportsUnicode:Bool; + + /** + Target supports rest arguments + **/ + final supportsRestArgs:Bool; + + /** + Exceptions handling config + **/ + final exceptions:ExceptionsConfig; + + /** + The scoping of local variables + **/ + final scoping:VarScopingConfig; + + /** + Target supports atomic operations via haxe.Atomic + **/ + final supportsAtomics:Bool; + +} + +enum CapturePolicy { + /** + Do nothing, let the platform handle it + **/ + None; + + /** + Wrap all captured variables into a single-element array to allow modifications + **/ + WrapRef; + + /** + Similar to wrap ref, but will only apply to the locals that are declared in loops + **/ + LoopVars; +} + +typedef VarScopingConfig = { + final scope:VarScope; + final flags:Array; +} + +enum VarScope { + FunctionScope; + BlockScope; +} + +enum VarScopingFlags { + /** + Variables are hoisted in their scope + **/ + VarHoisting; + + /** + It's not allowed to shadow existing variables in a scope. + **/ + NoShadowing; + + /** + It's not allowed to shadow a `catch` variable. + **/ + NoCatchVarShadowing; + + /** + Local vars cannot have the same name as the current top-level package or + (if in the root package) current class name + **/ + ReserveCurrentTopLevelSymbol; + + /** + Local vars cannot have a name used for any top-level symbol + (packages and classes in the root package) + **/ + ReserveAllTopLevelSymbols; + + /** + Reserve all type-paths converted to "flat path" with `Path.flat_path` + **/ + ReserveAllTypesFlat; + + /** + List of names cannot be taken by local vars + **/ + ReserveNames(names:Array); + + /** + Cases in a `switch` won't have blocks, but will share the same outer scope. + **/ + SwitchCasesNoBlocks; +} + +typedef ExceptionsConfig = { + /** + Base types which may be thrown from Haxe code without wrapping. + **/ + final nativeThrows:Array; + + /** + Base types which may be caught from Haxe code without wrapping. + **/ + final nativeCatches:Array; + + /** + Hint exceptions filter to avoid wrapping for targets, which can throw/catch any type + Ignored on targets with a specific native base type for exceptions. + **/ + final avoidWrapping:Bool; + + /** + Path of a native class or interface, which can be used for wildcard catches. + **/ + final wildcardCatch:TypePath; + + /** + Path of a native base class or interface, which can be thrown. + This type is used to cast `haxe.Exception.thrown(v)` calls to. + For example `throw 123` is compiled to `throw (cast Exception.thrown(123):ec_base_throw)` + **/ + final baseThrow:TypePath; + + /** + Checks if throwing this expression is a special case for current target + and should not be modified. + **/ + // final specialThrow:(TypedExpr)->Bool; +} diff --git a/std/haxe/macro/PositionTools.hx b/std/haxe/macro/PositionTools.hx index 8c4d6f62..f29300f6 100644 --- a/std/haxe/macro/PositionTools.hx +++ b/std/haxe/macro/PositionTools.hx @@ -60,7 +60,7 @@ class PositionTools { #end } - #if (macro || display) + #if macro /** Converts a `haxe.macro.Position` to a `haxe.display.Position.Location`. diff --git a/std/haxe/macro/Printer.hx b/std/haxe/macro/Printer.hx index 31282a97..306b8787 100644 --- a/std/haxe/macro/Printer.hx +++ b/std/haxe/macro/Printer.hx @@ -75,13 +75,15 @@ class Printer { case OpInterval: "..."; case OpArrow: "=>"; case OpIn: "in"; + case OpNullCoal: "??"; case OpAssignOp(op): printBinop(op) + "="; } function escapeString(s:String, delim:String) { return delim - + s.replace("\n", "\\n") + + s.replace('\\', '\\\\') + .replace("\n", "\\n") .replace("\t", "\\t") .replace("\r", "\\r") .replace("'", "\\'") @@ -100,8 +102,10 @@ class Printer { return switch (c) { case CString(s, SingleQuotes): printFormatString(s); case CString(s, _): printString(s); - case CIdent(s), CInt(s), CFloat(s): + case CIdent(s), CInt(s, null), CFloat(s, null): s; + case CInt(s, suffix), CFloat(s, suffix): + s + suffix; case CRegexp(s, opt): '~/$s/$opt'; } @@ -192,7 +196,8 @@ class Printer { return (tpd.meta != null && tpd.meta.length > 0 ? tpd.meta.map(printMetadata).join(" ") + " " : "") + tpd.name + (tpd.params != null && tpd.params.length > 0 ? "<" + tpd.params.map(printTypeParamDecl).join(", ") + ">" : "") - + (tpd.constraints != null && tpd.constraints.length > 0 ? ":(" + tpd.constraints.map(printComplexType).join(", ") + ")" : ""); + + (tpd.constraints != null && tpd.constraints.length > 0 ? ":(" + tpd.constraints.map(printComplexType).join(", ") + ")" : "") + + (tpd.defaultType != null ? "=" + printComplexType(tpd.defaultType) : ""); public function printFunctionArg(arg:FunctionArg) return (arg.opt ? "?" : "") + arg.name + opt(arg.type, printComplexType, ":") + opt(arg.value, printExpr, " = "); @@ -235,7 +240,7 @@ class Printer { case EConst(c): printConstant(c); case EArray(e1, e2): '${printExpr(e1)}[${printExpr(e2)}]'; case EBinop(op, e1, e2): '${printExpr(e1)} ${printBinop(op)} ${printExpr(e2)}'; - case EField(e1, n): '${printExpr(e1)}.$n'; + case EField(e1, n, kind): kind == Safe ? '${printExpr(e1)}?.$n' : '${printExpr(e1)}.$n'; case EParenthesis(e1): '(${printExpr(e1)})'; case EObjectDecl(fl): "{ " + fl.map(function(fld) return printObjectField(fld)).join(", ") + " }"; @@ -246,7 +251,8 @@ class Printer { case EUnop(op, false, e1): printUnop(op) + printExpr(e1); case EFunction(FNamed(no,inlined), func): (inlined ? 'inline ' : '') + 'function $no' + printFunction(func); case EFunction(kind, func): (kind != FArrow ? "function" : "") + printFunction(func, kind); - case EVars(vl): "var " + vl.map(printVar).join(", "); + case EVars([]): "var "; + case EVars(vl): ((vl[0].isStatic) ? "static " : "") + ((vl[0].isFinal) ? "final " : "var ") + vl.map(printVar).join(", "); case EBlock([]): '{ }'; case EBlock(el): var old = tabs; @@ -281,7 +287,6 @@ class Printer { case ECast(e1, _): "cast " + printExpr(e1); case EIs(e1, ct): '${printExpr(e1)} is ${printComplexType(ct)}'; case EDisplay(e1, _): '#DISPLAY(${printExpr(e1)})'; - case EDisplayNew(tp): '#DISPLAY(${printTypePath(tp)})'; case ETernary(econd, eif, eelse): '${printExpr(econd)} ? ${printExpr(eif)} : ${printExpr(eelse)}'; case ECheckType(e1, ct): '(${printExpr(e1)} : ${printComplexType(ct)})'; case EMeta({ name:":implicitReturn" }, { expr:EReturn(e1) }): printExpr(e1); @@ -543,8 +548,6 @@ class Printer { case EDisplay(e, displayKind): add("EDisplay"); loopI(e); - case EDisplayNew(t): - add("EDisplayNew"); case ETernary(econd, eif, eelse): add("ETernary"); loopI(econd); diff --git a/std/haxe/macro/Type.hx b/std/haxe/macro/Type.hx index f55cf81a..4e2a20de 100644 --- a/std/haxe/macro/Type.hx +++ b/std/haxe/macro/Type.hx @@ -180,6 +180,11 @@ typedef TypeParameter = { `KTypeParameter` kind. **/ var t:Type; + + /** + The default type for this type parameter. + **/ + var ?defaultType:Null; } /** diff --git a/std/haxe/macro/TypeTools.hx b/std/haxe/macro/TypeTools.hx index f75ac529..0aec8b47 100644 --- a/std/haxe/macro/TypeTools.hx +++ b/std/haxe/macro/TypeTools.hx @@ -85,7 +85,7 @@ class TypeTools { pos: cf.pos, meta: cf.meta.get(), } else { - throw "Invalid TAnonymous"; + throw "Invalid TAnonymous"; } } @@ -162,7 +162,7 @@ class TypeTools { } } - #if (macro || display) + #if macro /** Follows all typedefs of `t` to reach the actual type. @@ -174,12 +174,17 @@ class TypeTools { If `t` is null, an internal exception is thrown. - Usage example: + Usage example with monomorphs: var t = Context.typeof(macro null); // TMono() var ts = Context.typeof(macro "foo"); //TInst(String,[]) Context.unify(t, ts); trace(t); // TMono() trace(t.follow()); //TInst(String,[]) + + Usage example with typedefs: + var t = Context.typeof(macro ("foo" :MyString)); // typedef MyString = String + trace(t); // TType(MyString,[]) + trace(t.follow()); //TInst(String,[]) **/ static public inline function follow(t:Type, ?once:Bool):Type return Context.follow(t, once); @@ -363,6 +368,35 @@ class TypeTools { return null; #end } + + /** + Changes the name of the variable in the typed expression. + **/ + static public function setVarName(t:TVar, name:String) { + Context.load("set_var_name", 2)(t, name); + } + + /** + Converts type `t` to `haxe.macro.Type.ModuleType`. + **/ + static public function toModuleType(t:Type):ModuleType { + #if (neko || eval) + return Context.load("type_to_module_type", 1)(t); + #else + return null; + #end + } + + /** + Creates a type from the `haxe.macro.Type.ModuleType` argument. + **/ + static public function fromModuleType(mt:ModuleType):Type { + #if (neko || eval) + return Context.load("module_type_to_type", 1)(mt); + #else + return null; + #end + } #end /** diff --git a/std/haxe/macro/TypedExprTools.hx b/std/haxe/macro/TypedExprTools.hx index d0f81bf5..9334e286 100644 --- a/std/haxe/macro/TypedExprTools.hx +++ b/std/haxe/macro/TypedExprTools.hx @@ -167,7 +167,7 @@ class TypedExprTools { } } - #if (macro || display) + #if macro static public function toString(t:TypedExpr, ?pretty = false):String { return @:privateAccess haxe.macro.Context.sExpr(t, pretty); } diff --git a/std/hl/Api.hx b/std/hl/Api.hx index cab198b4..4c07c2e0 100644 --- a/std/hl/Api.hx +++ b/std/hl/Api.hx @@ -43,5 +43,10 @@ extern class Api { @:hlNative("std", "ptr_compare") static function comparePointer(a:Dynamic, b:Dynamic):Int; #if (hl_ver >= version("1.12.0")) @:hlNative("std", "is_prim_loaded") static function isPrimLoaded(f:haxe.Constraints.Function):Bool; + @:hlNative("?std", "mem_compact") static function compact( v : T, exclude : hl.NativeArray, flags : Int, outCount : hl.Ref ) : T; + @:hlNative("?std", "sys_check_reload") static function checkReload( ?debugFile : hl.Bytes ) : Bool; + #end + #if (hl_ver >= version("1.13.0")) + @:hlNative("?std", "sys_has_debugger") static function hasDebugger() : Bool; #end } diff --git a/std/hl/Atomics.hx b/std/hl/Atomics.hx new file mode 100644 index 00000000..f811da0d --- /dev/null +++ b/std/hl/Atomics.hx @@ -0,0 +1,19 @@ +package hl; + +@:hlNative("std", "atomic_") +extern class Atomics { + static function add32(r:hl.Ref, a:Int):Int; + static function sub32(r:hl.Ref, a:Int):Int; + static function and32(r:hl.Ref, a:Int):Int; + static function or32(r:hl.Ref, a:Int):Int; + static function xor32(r:hl.Ref, a:Int):Int; + static function compareExchange32(r:hl.Ref, a:Int, b:Int):Int; + static function exchange32(r:hl.Ref, val:Int):Int; + static function load32(r:hl.Ref):Int; + static function store32(r:hl.Ref, val:Int):Int; + + static function compareExchangePtr(r:hl.Ref, a:Dynamic, b:Dynamic):Dynamic; + static function exchangePtr(r:hl.Ref, val:Dynamic):Dynamic; + static function loadPtr(r:hl.Ref):Dynamic; + static function storePtr(r:hl.Ref, val:Dynamic):Dynamic; +} diff --git a/std/hl/CArray.hx b/std/hl/CArray.hx new file mode 100644 index 00000000..850d86f5 --- /dev/null +++ b/std/hl/CArray.hx @@ -0,0 +1,36 @@ +package hl; + +#if (hl_ver >= version("1.13.0")) +/** + CArray is a compact array where all objects are memory aligned and stored as a single GC block. + You must hold a reference to the CArray while any of the objects it contains is still referenced somewhere. + **/ +abstract CArray(Abstract<"hl_carray">) { + + public var length(get,never) : Int; + + inline function get_length() return getLen(cast this); + + @:arrayAccess inline function get( index : Int ) : T return getIndex(cast this, index); + + public static function alloc( cl : Class, size : Int ) : CArray { + return cast alloc_carray( (cast cl:BaseType).__type__ , size ); + } + + @:hlNative("?std","carray_get") + static function getIndex( arr : CArray, index : Int ) : Dynamic { + return null; + } + + @:hlNative("?std","carray_length") + static function getLen( arr : CArray ) : Int { + return 0; + } + + @:hlNative("?std","alloc_carray") + static function alloc_carray( t : hl.Type, size : Int ) : CArray { + return null; + } + +} +#end \ No newline at end of file diff --git a/std/hl/I64.hx b/std/hl/I64.hx index c447d04a..a4102139 100644 --- a/std/hl/I64.hx +++ b/std/hl/I64.hx @@ -22,4 +22,46 @@ package hl; -@:coreType @:notNull @:runtimeValue abstract I64 to Int from Int {} +@:coreType @:notNull @:runtimeValue abstract I64 from Int { + + /** + Destructively cast to Int + **/ + public inline function toInt():Int { + return cast this; + } + + @:to + @:deprecated("Implicit cast from I64 to Int (32 bits) is deprecated. Use .toInt() or explicitly cast instead.") + inline function implicitToInt(): Int { + return toInt(); + } + + #if (hl_ver >= version("1.12.0") && !hl_legacy32) + @:op(a+b) function add(v:I64) : I64; + @:op(a-b) function sub(v:I64) : I64; + @:op(a*b) function mul(v:I64) : I64; + @:op(a/b) function div(v:I64) : I64; + @:op(a%b) function mod(v:I64) : I64; + @:op(a<>b) function shr(v:Int) : I64; + @:op(a>>>b) function ushr(v:Int) : I64; + @:op(a|b) function or(v:I64) : I64; + @:op(a&b) function and(v:I64) : I64; + @:op(a^b) function xor(v:I64) : I64; + + @:op(-a) function neg() : I64; + @:op(~a) inline function compl() : I64 { return (-1:I64) - this; } + @:op(++a) function incr() : I64; + @:op(--a) function decr() : I64; + @:op(a++) function pincr() : I64; + @:op(a--) function pdecr() : I64; + + @:op(a==b) function eq(v:I64) : Bool; + @:op(a>=b) function gte(v:I64) : Bool; + @:op(a<=b) function lte(v:I64) : Bool; + @:op(a>b) function gt(v:I64) : Bool; + @:op(a) { - @:privateAccess _dialog(title.bytes, text.bytes, flags.toInt()); + return @:privateAccess _dialog(title.bytes, text.bytes, flags.toInt()) != 0; } @:hlNative("ui", "ui_loop") diff --git a/std/hl/_std/Sys.hx b/std/hl/_std/Sys.hx index feae9d03..29d5e095 100644 --- a/std/hl/_std/Sys.hx +++ b/std/hl/_std/Sys.hx @@ -86,7 +86,7 @@ class Sys { return makePath(v); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { if (!put_env(getPath(s), if (v == null) null else getPath(v))) throw "putEnv() failure"; } diff --git a/std/hl/_std/Type.hx b/std/hl/_std/Type.hx index 7326242c..8ac34543 100644 --- a/std/hl/_std/Type.hx +++ b/std/hl/_std/Type.hx @@ -38,12 +38,18 @@ class Type { static inline function get_allTypes():hl.types.BytesMap return untyped $allTypes(); - @:keep static function init():Void { + @:keep static function init():Bool { + if( allTypes != null ) + return false; untyped $allTypes(new hl.types.BytesMap()); + return true; } @:keep static function initClass(ct:hl.Type, t:hl.Type, name:hl.Bytes):hl.BaseType.Class@:privateAccess { - var c:hl.BaseType.Class = ct.allocObject(); + var c:hl.BaseType.Class = cast t.getGlobal(); + if( c != null ) + return c; + c = ct.allocObject(); t.setGlobal(c); c.__type__ = t; c.__name__ = String.fromUCS2(name); @@ -52,7 +58,10 @@ class Type { } @:keep static function initEnum(et:hl.Type, t:hl.Type):hl.BaseType.Enum@:privateAccess { - var e:hl.BaseType.Enum = et.allocObject(); + var e:hl.BaseType.Enum = cast t.getGlobal(); + if( e != null ) + return e; + e = et.allocObject(); e.__type__ = t; e.__evalues__ = t.getEnumValues(); e.__ename__ = t.getTypeName(); @@ -246,6 +255,7 @@ class Type { } public static function enumParameters(e:EnumValue):Array { + if( e == null ) (e:Dynamic)(); // trigger null access var arr = _enumParameters(e); return cast hl.types.ArrayObj.alloc(arr); } diff --git a/std/hl/_std/haxe/Exception.hx b/std/hl/_std/haxe/Exception.hx index ddad563e..8f3ae5fc 100644 --- a/std/hl/_std/haxe/Exception.hx +++ b/std/hl/_std/haxe/Exception.hx @@ -9,7 +9,7 @@ class Exception { @:noCompletion var __exceptionMessage:String; @:noCompletion var __exceptionStack:Null; - @:noCompletion var __nativeStack:hl.NativeArray; + @:noCompletion var __nativeStack:hl.NativeArray<#if (hl_ver >= "1.12.0") haxe.NativeStackTrace.Symbol #else hl.Bytes #end>; @:noCompletion @:ifFeature("haxe.Exception.get_stack") var __skipStack:Int = 0; @:noCompletion var __nativeException:Any; @:noCompletion var __previousException:Null; diff --git a/std/hl/_std/haxe/Int64.hx b/std/hl/_std/haxe/Int64.hx new file mode 100644 index 00000000..ee842592 --- /dev/null +++ b/std/hl/_std/haxe/Int64.hx @@ -0,0 +1,608 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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. + */ + +package haxe; + +using haxe.Int64; + +#if (hl_ver >= version("1.12.0") && !hl_legacy32) + +import haxe.Int64Helper; + +private typedef __Int64 = hl.I64; + +@:coreApi +@:transitive +abstract Int64(__Int64) from __Int64 to __Int64 { + + static var MASK : hl.I64 = { + var v : hl.I64 = 0xFFFF; + v | (v << 16); + } + + public static inline function make(high:Int32, low:Int32):Int64 { + var h : hl.I64 = high; + var l : hl.I64 = low; + return cast ((h << 32) | (l&MASK)); + } + + private inline function new(x:__Int64) + this = x; + + private var val(get, set):__Int64; + + inline function get_val():__Int64 + return this; + + inline function set_val(x:__Int64):__Int64 + return this = x; + + public var high(get, never):Int32; + + inline function get_high():Int32 + return cast(this >> 32); + + public var low(get, never):Int32; + + inline function get_low():Int32 + return cast this; + + public inline function copy():Int64 + return new Int64(this); + + @:from public static inline function ofInt(x:Int):Int64 + return cast x; + + @:deprecated('haxe.Int64.is() is deprecated. Use haxe.Int64.isInt64() instead') + inline public static function is(val:Dynamic):Bool + return isInt64(val); + + inline public static function isInt64(val:Dynamic):Bool + return hl.Type.getDynamic(val).kind == HI64; + + public static inline function toInt(x:Int64):Int { + if (x.val < 0x80000000 || x.val > 0x7FFFFFFF) + throw "Overflow"; + return cast x.val; + } + + public static inline function getHigh(x:Int64):Int32 + return cast(x.val >> 32); + + public static inline function getLow(x:Int64):Int32 + return cast(x.val); + + public static inline function isNeg(x:Int64):Bool + return x.val < 0; + + public static inline function isZero(x:Int64):Bool + return x.val == 0; + + public static inline function compare(a:Int64, b:Int64):Int { + if (a.val < b.val) + return -1; + if (a.val > b.val) + return 1; + return 0; + } + + public static inline function ucompare(a:Int64, b:Int64):Int { + if (a.val < 0) + return (b.val < 0) ? compare(a, b) : 1; + return (b.val < 0) ? -1 : compare(a, b); + } + + public static inline function toStr(x:Int64):String + return '${x.val}'; + + public static inline function divMod(dividend:Int64, divisor:Int64):{quotient:Int64, modulus:Int64} + return {quotient: dividend / divisor, modulus: dividend % divisor}; + + private inline function toString():String + return '$this'; + + public static function parseString(sParam:String):Int64 { + // can this be done?: return new Int64( java.lang.Long.LongClass.parseLong( sParam ) ); + return Int64Helper.parseString(sParam); + } + + public static function fromFloat(f:Float):Int64 { + return Int64Helper.fromFloat(f); + } + + @:op(-A) public static function neg(x:Int64):Int64 + return -x.val; + + @:op(++A) private inline function preIncrement():Int64 + return ++this; + + @:op(A++) private inline function postIncrement():Int64 + return this++; + + @:op(--A) private inline function preDecrement():Int64 + return --this; + + @:op(A--) private inline function postDecrement():Int64 + return this--; + + @:op(A + B) public static inline function add(a:Int64, b:Int64):Int64 + return a.val + b.val; + + @:op(A + B) @:commutative private static inline function addInt(a:Int64, b:Int):Int64 + return a.val + b; + + @:op(A - B) public static inline function sub(a:Int64, b:Int64):Int64 + return a.val - b.val; + + @:op(A - B) private static inline function subInt(a:Int64, b:Int):Int64 + return a.val - b; + + @:op(A - B) private static inline function intSub(a:Int, b:Int64):Int64 + return (a:hl.I64) - b.val; + + @:op(A * B) public static inline function mul(a:Int64, b:Int64):Int64 + return a.val * b.val; + + @:op(A * B) @:commutative private static inline function mulInt(a:Int64, b:Int):Int64 + return a.val * b; + + @:op(A / B) public static inline function div(a:Int64, b:Int64):Int64 + return a.val / b.val; + + @:op(A / B) private static inline function divInt(a:Int64, b:Int):Int64 + return a.val / b; + + @:op(A / B) private static inline function intDiv(a:Int, b:Int64):Int64 + return (a:hl.I64) / b.val; + + @:op(A % B) public static inline function mod(a:Int64, b:Int64):Int64 + return a.val % b.val; + + @:op(A % B) private static inline function modInt(a:Int64, b:Int):Int64 + return a.val % b; + + @:op(A % B) private static inline function intMod(a:Int, b:Int64):Int64 + return (a:hl.I64) % b.val; + + @:op(A == B) public static inline function eq(a:Int64, b:Int64):Bool + return a.val == b.val; + + @:op(A == B) @:commutative private static inline function eqInt(a:Int64, b:Int):Bool + return a.val == b; + + @:op(A != B) public static inline function neq(a:Int64, b:Int64):Bool + return a.val != b.val; + + @:op(A != B) @:commutative private static inline function neqInt(a:Int64, b:Int):Bool + return a.val != (b:hl.I64); + + @:op(A < B) private static inline function lt(a:Int64, b:Int64):Bool + return a.val < b.val; + + @:op(A < B) private static inline function ltInt(a:Int64, b:Int):Bool + return a.val < b; + + @:op(A < B) private static inline function intLt(a:Int, b:Int64):Bool + return (a:hl.I64) < b.val; + + @:op(A <= B) private static inline function lte(a:Int64, b:Int64):Bool + return a.val <= b.val; + + @:op(A <= B) private static inline function lteInt(a:Int64, b:Int):Bool + return a.val <= b; + + @:op(A <= B) private static inline function intLte(a:Int, b:Int64):Bool + return (a:hl.I64) <= b.val; + + @:op(A > B) private static inline function gt(a:Int64, b:Int64):Bool + return a.val > b.val; + + @:op(A > B) private static inline function gtInt(a:Int64, b:Int):Bool + return a.val > b; + + @:op(A > B) private static inline function intGt(a:Int, b:Int64):Bool + return (a:hl.I64) > b.val; + + @:op(A >= B) private static inline function gte(a:Int64, b:Int64):Bool + return a.val >= b.val; + + @:op(A >= B) private static inline function gteInt(a:Int64, b:Int):Bool + return a.val >= b; + + @:op(A >= B) private static inline function intGte(a:Int, b:Int64):Bool + return (a:hl.I64) >= b.val; + + @:op(~A) private static inline function complement(x:Int64):Int64 + return ~x.val; + + @:op(A & B) public static inline function and(a:Int64, b:Int64):Int64 + return a.val & b.val; + + @:op(A | B) public static inline function or(a:Int64, b:Int64):Int64 + return a.val | b.val; + + @:op(A ^ B) public static inline function xor(a:Int64, b:Int64):Int64 + return a.val ^ b.val; + + @:op(A << B) public static inline function shl(a:Int64, b:Int):Int64 + return a.val << b; + + @:op(A >> B) public static inline function shr(a:Int64, b:Int):Int64 + return a.val >> b; + + @:op(A >>> B) public static inline function ushr(a:Int64, b:Int):Int64 + return a.val >>> b; +} + +#else + +@:transitive +abstract Int64(__Int64) from __Int64 to __Int64 { + private inline function new(x:__Int64) + this = x; + + public inline function copy():Int64 + return make(high, low); + + public static inline function make(high:Int32, low:Int32):Int64 + return new Int64(new __Int64(high, low)); + + @:from public static inline function ofInt(x:Int):Int64 + return make(x >> 31, x); + + public static inline function toInt(x:Int64):Int { + if (x.high != x.low >> 31) + throw "Overflow"; + + return x.low; + } + + @:deprecated('haxe.Int64.is() is deprecated. Use haxe.Int64.isInt64() instead') + inline public static function is(val:Dynamic):Bool { + return isInt64(val); + } + + inline public static function isInt64(val:Dynamic):Bool + return Std.isOfType(val, __Int64); + + @:deprecated("Use high instead") + public static inline function getHigh(x:Int64):Int32 + return x.high; + + @:deprecated("Use low instead") + public static inline function getLow(x:Int64):Int32 + return x.low; + + public static inline function isNeg(x:Int64):Bool + return x.high < 0; + + public static inline function isZero(x:Int64):Bool + return x == 0; + + public static inline function compare(a:Int64, b:Int64):Int { + var v = a.high - b.high; + v = if (v != 0) v else Int32.ucompare(a.low, b.low); + return a.high < 0 ? (b.high < 0 ? v : -1) : (b.high >= 0 ? v : 1); + } + + public static inline function ucompare(a:Int64, b:Int64):Int { + var v = Int32.ucompare(a.high, b.high); + return if (v != 0) v else Int32.ucompare(a.low, b.low); + } + + public static inline function toStr(x:Int64):String + return x.toString(); + + function toString():String { + var i:Int64 = cast this; + if (i == 0) + return "0"; + var str = ""; + var neg = false; + if (i.isNeg()) { + neg = true; + // i = -i; cannot negate here as --9223372036854775808 = -9223372036854775808 + } + var ten:Int64 = 10; + while (i != 0) { + var r = i.divMod(ten); + if (r.modulus.isNeg()) { + str = Int64.neg(r.modulus).low + str; + i = Int64.neg(r.quotient); + } else { + str = r.modulus.low + str; + i = r.quotient; + } + } + if (neg) + str = "-" + str; + return str; + } + + public static inline function parseString(sParam:String):Int64 { + return Int64Helper.parseString(sParam); + } + + public static inline function fromFloat(f:Float):Int64 { + return Int64Helper.fromFloat(f); + } + + public static function divMod(dividend:Int64, divisor:Int64):{quotient:Int64, modulus:Int64} { + // Handle special cases of 0 and 1 + if (divisor.high == 0) { + switch (divisor.low) { + case 0: + throw "divide by zero"; + case 1: + return {quotient: dividend.copy(), modulus: 0}; + } + } + + var divSign = dividend.isNeg() != divisor.isNeg(); + + var modulus = dividend.isNeg() ? -dividend : dividend.copy(); + divisor = divisor.isNeg() ? -divisor : divisor; + + var quotient:Int64 = 0; + var mask:Int64 = 1; + + while (!divisor.isNeg()) { + var cmp = ucompare(divisor, modulus); + divisor <<= 1; + mask <<= 1; + if (cmp >= 0) + break; + } + + while (mask != 0) { + if (ucompare(modulus, divisor) >= 0) { + quotient |= mask; + modulus -= divisor; + } + mask >>>= 1; + divisor >>>= 1; + } + + if (divSign) + quotient = -quotient; + if (dividend.isNeg()) + modulus = -modulus; + + return { + quotient: quotient, + modulus: modulus + }; + } + + @:op(-A) public static inline function neg(x:Int64):Int64 { + var high = ~x.high; + var low = -x.low; + if (low == 0) + high++; + return make(high, low); + } + + @:op(++A) private inline function preIncrement():Int64 { + this = copy(); + this.low++; + if (this.low == 0) + this.high++; + return cast this; + } + + @:op(A++) private inline function postIncrement():Int64 { + var ret = this; + preIncrement(); + return ret; + } + + @:op(--A) private inline function preDecrement():Int64 { + this = copy(); + if (this.low == 0) + this.high--; + this.low--; + return cast this; + } + + @:op(A--) private inline function postDecrement():Int64 { + var ret = this; + preDecrement(); + return ret; + } + + @:op(A + B) public static inline function add(a:Int64, b:Int64):Int64 { + var high = a.high + b.high; + var low = a.low + b.low; + if (Int32.ucompare(low, a.low) < 0) + high++; + return make(high, low); + } + + @:op(A + B) @:commutative private static inline function addInt(a:Int64, b:Int):Int64 + return add(a, b); + + @:op(A - B) public static inline function sub(a:Int64, b:Int64):Int64 { + var high = a.high - b.high; + var low = a.low - b.low; + if (Int32.ucompare(a.low, b.low) < 0) + high--; + return make(high, low); + } + + @:op(A - B) private static inline function subInt(a:Int64, b:Int):Int64 + return sub(a, b); + + @:op(A - B) private static inline function intSub(a:Int, b:Int64):Int64 + return sub(a, b); + + @:op(A * B) + public static #if !lua inline #end function mul(a:Int64, b:Int64):Int64 { + var mask = 0xFFFF; + var al = a.low & mask, ah = a.low >>> 16; + var bl = b.low & mask, bh = b.low >>> 16; + var p00 = al * bl; + var p10 = ah * bl; + var p01 = al * bh; + var p11 = ah * bh; + var low = p00; + var high = p11 + (p01 >>> 16) + (p10 >>> 16); + p01 <<= 16; + low += p01; + if (Int32.ucompare(low, p01) < 0) + high++; + p10 <<= 16; + low += p10; + if (Int32.ucompare(low, p10) < 0) + high++; + high += a.low * b.high + a.high * b.low; + return make(high, low); + } + + @:op(A * B) @:commutative private static inline function mulInt(a:Int64, b:Int):Int64 + return mul(a, b); + + @:op(A / B) public static inline function div(a:Int64, b:Int64):Int64 + return divMod(a, b).quotient; + + @:op(A / B) private static inline function divInt(a:Int64, b:Int):Int64 + return div(a, b); + + @:op(A / B) private static inline function intDiv(a:Int, b:Int64):Int64 + return div(a, b).toInt(); + + @:op(A % B) public static inline function mod(a:Int64, b:Int64):Int64 + return divMod(a, b).modulus; + + @:op(A % B) private static inline function modInt(a:Int64, b:Int):Int64 + return mod(a, b).toInt(); + + @:op(A % B) private static inline function intMod(a:Int, b:Int64):Int64 + return mod(a, b).toInt(); + + @:op(A == B) public static inline function eq(a:Int64, b:Int64):Bool + return a.high == b.high && a.low == b.low; + + @:op(A == B) @:commutative private static inline function eqInt(a:Int64, b:Int):Bool + return eq(a, b); + + @:op(A != B) public static inline function neq(a:Int64, b:Int64):Bool + return a.high != b.high || a.low != b.low; + + @:op(A != B) @:commutative private static inline function neqInt(a:Int64, b:Int):Bool + return neq(a, b); + + @:op(A < B) private static inline function lt(a:Int64, b:Int64):Bool + return compare(a, b) < 0; + + @:op(A < B) private static inline function ltInt(a:Int64, b:Int):Bool + return lt(a, b); + + @:op(A < B) private static inline function intLt(a:Int, b:Int64):Bool + return lt(a, b); + + @:op(A <= B) private static inline function lte(a:Int64, b:Int64):Bool + return compare(a, b) <= 0; + + @:op(A <= B) private static inline function lteInt(a:Int64, b:Int):Bool + return lte(a, b); + + @:op(A <= B) private static inline function intLte(a:Int, b:Int64):Bool + return lte(a, b); + + @:op(A > B) private static inline function gt(a:Int64, b:Int64):Bool + return compare(a, b) > 0; + + @:op(A > B) private static inline function gtInt(a:Int64, b:Int):Bool + return gt(a, b); + + @:op(A > B) private static inline function intGt(a:Int, b:Int64):Bool + return gt(a, b); + + @:op(A >= B) private static inline function gte(a:Int64, b:Int64):Bool + return compare(a, b) >= 0; + + @:op(A >= B) private static inline function gteInt(a:Int64, b:Int):Bool + return gte(a, b); + + @:op(A >= B) private static inline function intGte(a:Int, b:Int64):Bool + return gte(a, b); + + @:op(~A) private static inline function complement(a:Int64):Int64 + return make(~a.high, ~a.low); + + @:op(A & B) public static inline function and(a:Int64, b:Int64):Int64 + return make(a.high & b.high, a.low & b.low); + + @:op(A | B) public static inline function or(a:Int64, b:Int64):Int64 + return make(a.high | b.high, a.low | b.low); + + @:op(A ^ B) public static inline function xor(a:Int64, b:Int64):Int64 + return make(a.high ^ b.high, a.low ^ b.low); + + @:op(A << B) public static inline function shl(a:Int64, b:Int):Int64 { + b &= 63; + return if (b == 0) a.copy() else if (b < 32) make((a.high << b) | (a.low >>> (32 - b)), a.low << b) else make(a.low << (b - 32), 0); + } + + @:op(A >> B) public static inline function shr(a:Int64, b:Int):Int64 { + b &= 63; + return if (b == 0) a.copy() else if (b < 32) make(a.high >> b, (a.high << (32 - b)) | (a.low >>> b)); else make(a.high >> 31, a.high >> (b - 32)); + } + + @:op(A >>> B) public static inline function ushr(a:Int64, b:Int):Int64 { + b &= 63; + return if (b == 0) a.copy() else if (b < 32) make(a.high >>> b, (a.high << (32 - b)) | (a.low >>> b)); else make(0, a.high >>> (b - 32)); + } + + public var high(get, never):Int32; + + private inline function get_high() + return this.high; + + private inline function set_high(x) + return this.high = x; + + public var low(get, never):Int32; + + private inline function get_low() + return this.low; + + private inline function set_low(x) + return this.low = x; +} + +private typedef __Int64 = ___Int64; + +private class ___Int64 { + public var high:Int32; + public var low:Int32; + + public inline function new(high, low) { + this.high = high; + this.low = low; + } + + public function toString():String + return Int64.toStr(cast this); +} + +#end diff --git a/std/hl/_std/haxe/NativeStackTrace.hx b/std/hl/_std/haxe/NativeStackTrace.hx index 31e55e71..e7cf077a 100644 --- a/std/hl/_std/haxe/NativeStackTrace.hx +++ b/std/hl/_std/haxe/NativeStackTrace.hx @@ -4,6 +4,8 @@ import hl.NativeArray; import hl.Bytes; import haxe.CallStack.StackItem; +typedef Symbol = #if (hl_ver >= version("1.12.0")) hl.Abstract<"hl_symbol"> #else hl.Bytes #end + /** Do not use manually. **/ @@ -14,6 +16,39 @@ class NativeStackTrace { static public inline function saveStack(exception:Any):Void { } + #if (hl_ver >= version("1.12.0") ) + + static public function exceptionStack():NativeArray { + var count = exceptionStackRaw(null); + var arr = new NativeArray(count); + exceptionStackRaw(arr); + return arr; + } + + static public inline function callStack():NativeArray { + var count = callStackRaw(null); + var arr = new NativeArray(count); + callStackRaw(arr); + return arr; + } + + @:hlNative("std", "exception_stack_raw") + static function exceptionStackRaw( arr : NativeArray ) : Int { + return 0; + } + + @:hlNative("std", "call_stack_raw") + static function callStackRaw( arr : NativeArray ) : Int { + return 0; + } + + @:hlNative("std","resolve_symbol") + static function resolveSymbol( sym : Symbol, buf : hl.Bytes, bufLen : hl.Ref ) : hl.Bytes { + return null; + } + + #else + @:hlNative("std", "exception_stack") static public function exceptionStack():NativeArray { return null; @@ -37,15 +72,26 @@ class NativeStackTrace { return skip < stack.length ? stack.sub(skip, stack.length - skip) : stack; } - static public function toHaxe(native:NativeArray, skip:Int = 0):Array { + #end + + static public function toHaxe(native:NativeArray, skip:Int=0 ):Array { var stack = []; var r = ~/^([A-Za-z0-9.$_]+)\.([~A-Za-z0-9_]+(\.[0-9]+)?)\((.+):([0-9]+)\)$/; var r_fun = ~/^fun\$([0-9]+)\((.+):([0-9]+)\)$/; - for (i in 0...native.length - 1) { - if(skip > i) { - continue; - } - var str = @:privateAccess String.fromUCS2(native[i]); + #if (hl_ver >= version("1.12.0")) + var maxLen = 1024; + var tmpBuf = @:privateAccess hl.Bytes.alloc(maxLen); + #end + for (i in 0...native.length-1) { + if( i < skip ) continue; + #if (hl_ver >= version("1.12.0")) + var len = maxLen; + var bytes = resolveSymbol(native[i],tmpBuf,len); + if( bytes == null ) continue; + #else + var bytes = native[i]; + #end + var str = @:privateAccess String.fromUCS2(bytes); if (r.match(str)) stack.push(FilePos(Method(r.matched(1), r.matched(2)), r.matched(4), Std.parseInt(r.matched(5)))); else if (r_fun.match(str)) diff --git a/std/hl/_std/haxe/atomic/AtomicInt.hx b/std/hl/_std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..f1efde11 --- /dev/null +++ b/std/hl/_std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,49 @@ +package haxe.atomic; + +#if (hl_ver < version("1.13.0") && !doc_gen) +#error "Atomic operations require HL 1.13+" +#end +import hl.Atomics; + +abstract AtomicInt(hl.NativeArray) { + public inline function new(value:Int):Void { + this = new hl.NativeArray(1); + this[0] = value; + } + + public inline function add(b:Int):Int { + return Atomics.add32(this.getRef(), b); + } + + public inline function sub(b:Int):Int { + return Atomics.sub32(this.getRef(), b); + } + + public inline function and(b:Int):Int { + return Atomics.and32(this.getRef(), b); + } + + public inline function or(b:Int):Int { + return Atomics.or32(this.getRef(), b); + } + + public inline function xor(b:Int):Int { + return Atomics.xor32(this.getRef(), b); + } + + public inline function compareExchange(expected:Int, replacement:Int):Int { + return Atomics.compareExchange32(this.getRef(), expected, replacement); + } + + public inline function exchange(value:Int):Int { + return Atomics.exchange32(this.getRef(), value); + } + + public inline function load():Int { + return Atomics.load32(this.getRef()); + } + + public inline function store(value:Int):Int { + return Atomics.store32(this.getRef(), value); + } +} diff --git a/std/hl/_std/haxe/atomic/AtomicObject.hx b/std/hl/_std/haxe/atomic/AtomicObject.hx new file mode 100644 index 00000000..e0a03afa --- /dev/null +++ b/std/hl/_std/haxe/atomic/AtomicObject.hx @@ -0,0 +1,31 @@ +package haxe.atomic; + +#if (hl_ver < version("1.13.0") && !doc_gen) +#error "Atomic operations require HL 1.13+" +#end +import hl.Atomics; + +// use hl.NativeArray instead of hl.NativeArray +// so that the compiler doesn't get confused and emit hl.Ref.make(this.getRef()) +abstract AtomicObject(hl.NativeArray) { + public inline function new(value:T):Void { + this = new hl.NativeArray(1); + this[0] = value; + } + + public inline function compareExchange(expected:T, replacement:T):T { + return Atomics.compareExchangePtr(this.getRef(), expected, replacement); + } + + public inline function exchange(value:T):T { + return Atomics.exchangePtr(this.getRef(), value); + } + + public inline function load():T { + return Atomics.loadPtr(this.getRef()); + } + + public inline function store(value:T):T { + return Atomics.storePtr(this.getRef(), value); + } +} diff --git a/std/hl/_std/haxe/ds/IntMap.hx b/std/hl/_std/haxe/ds/IntMap.hx index f98fb5e3..65884844 100644 --- a/std/hl/_std/haxe/ds/IntMap.hx +++ b/std/hl/_std/haxe/ds/IntMap.hx @@ -69,7 +69,7 @@ class IntMap implements haxe.Constraints.IMap { var s = new StringBuf(); var keys = h.keysArray(); var values = h.valuesArray(); - s.addChar('{'.code); + s.addChar("[".code); for (i in 0...keys.length) { if (i > 0) s.add(", "); @@ -77,7 +77,7 @@ class IntMap implements haxe.Constraints.IMap { s.add(" => "); s.add(values[i]); } - s.addChar('}'.code); + s.addChar("]".code); return s.toString(); } diff --git a/std/hl/_std/haxe/ds/ObjectMap.hx b/std/hl/_std/haxe/ds/ObjectMap.hx index 43247526..93392e97 100644 --- a/std/hl/_std/haxe/ds/ObjectMap.hx +++ b/std/hl/_std/haxe/ds/ObjectMap.hx @@ -69,7 +69,7 @@ class ObjectMap implements haxe.Constraints.IMap { var s = new StringBuf(); var keys = h.keysArray(); var values = h.valuesArray(); - s.addChar('{'.code); + s.addChar("[".code); for (i in 0...keys.length) { if (i > 0) s.add(", "); @@ -77,7 +77,7 @@ class ObjectMap implements haxe.Constraints.IMap { s.add(" => "); s.add(values[i]); } - s.addChar('}'.code); + s.addChar("]".code); return s.toString(); } diff --git a/std/hl/_std/haxe/ds/StringMap.hx b/std/hl/_std/haxe/ds/StringMap.hx index eda9c62b..0c5b3199 100644 --- a/std/hl/_std/haxe/ds/StringMap.hx +++ b/std/hl/_std/haxe/ds/StringMap.hx @@ -96,7 +96,7 @@ class StringMap implements haxe.Constraints.IMap { var s = new StringBuf(); var keys = h.keysArray(); var values = h.valuesArray(); - s.addChar('{'.code); + s.addChar("[".code); for (i in 0...keys.length) { if (i > 0) s.add(", "); @@ -105,7 +105,7 @@ class StringMap implements haxe.Constraints.IMap { s.add(" => "); s.add(values[i]); } - s.addChar('}'.code); + s.addChar("]".code); return s.toString(); } diff --git a/std/hl/_std/haxe/ds/Vector.hx b/std/hl/_std/haxe/ds/Vector.hx index d4890c73..49c43198 100644 --- a/std/hl/_std/haxe/ds/Vector.hx +++ b/std/hl/_std/haxe/ds/Vector.hx @@ -26,10 +26,16 @@ private typedef VectorData = Array @:coreApi abstract Vector(VectorData) { - public inline function new(length:Int) { + extern overload public inline function new(length:Int) { this = []; if (length > 0) - this[length - 1] = cast null; + this[length - 1] = @:nullSafety(Off) cast null; + } + + extern overload public inline function new(length:Int, defaultValue:T):Vector { + this = [ + for (i in 0...length) defaultValue + ]; } @:op([]) public inline function get(index:Int):T { @@ -46,6 +52,9 @@ abstract Vector(VectorData) { return this.length; } + public inline function fill(value:T):Void + for (i in 0...length) this[i] = value; + public static inline function blit(src:Vector, srcPos:Int, dest:Vector, destPos:Int, len:Int):Void { (cast dest : hl.types.ArrayBase.ArrayAccess).blit(destPos, (cast src : hl.types.ArrayBase.ArrayAccess), srcPos, len); } diff --git a/std/hl/_std/haxe/io/FPHelper.hx b/std/hl/_std/haxe/io/FPHelper.hx index 2d5389c2..b170f80e 100644 --- a/std/hl/_std/haxe/io/FPHelper.hx +++ b/std/hl/_std/haxe/io/FPHelper.hx @@ -45,11 +45,15 @@ class FPHelper { public static function doubleToI64(v:Float):Int64 { helper.setF64(0, v); + #if (hl_ver >= version("1.12.0") && !hl_legacy32) + return Int64.make(helper.getI32(4),helper.getI32(0)); + #else var i64 = i64tmp; @:privateAccess { i64.set_low(helper.getI32(0)); i64.set_high(helper.getI32(4)); } return i64; + #end } } diff --git a/std/hl/_std/sys/thread/Condition.hx b/std/hl/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..d8fdfb6e --- /dev/null +++ b/std/hl/_std/sys/thread/Condition.hx @@ -0,0 +1,50 @@ +package sys.thread; + +#if doc_gen +@:coreApi extern class Condition { + function new():Void; + + public function acquire():Void; + + public function tryAcquire():Bool; + + public function release():Void; + + public function wait():Void; + + public function signal():Void; + + public function broadcast():Void; +} +#else +abstract Condition(hl.Abstract<"hl_condition">) { + public function new():Void { + this = alloc(); + } + + @:hlNative("std", "condition_acquire") + public function acquire():Void {} + + @:hlNative("std", "condition_try_acquire") + public function tryAcquire():Bool { + return false; + } + + @:hlNative("std", "condition_release") + public function release():Void {} + + @:hlNative("std", "condition_wait") + public function wait():Void {} + + @:hlNative("std", "condition_signal") + public function signal():Void {} + + @:hlNative("std", "condition_broadcast") + public function broadcast():Void {} + + @:hlNative("std", "condition_alloc") + static function alloc():hl.Abstract<"hl_condition"> { + return null; + } +} +#end diff --git a/std/hl/_std/sys/thread/Semaphore.hx b/std/hl/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..e45391ce --- /dev/null +++ b/std/hl/_std/sys/thread/Semaphore.hx @@ -0,0 +1,32 @@ +package sys.thread; + +#if doc_gen +@:coreApi extern class Semaphore { + function new(value:Int):Void; + function acquire():Void; + function tryAcquire(?timeout:Float):Bool; + function release():Void; +} +#else +abstract Semaphore(hl.Abstract<"hl_semaphore">) { + public function new(value:Int):Void { + this = alloc(value); + } + + @:hlNative("std", "semaphore_acquire") + public function acquire():Void {} + + @:hlNative("std", "semaphore_release") + public function release():Void {} + + @:hlNative("std", "semaphore_try_acquire") + public function tryAcquire(?timeout:Float):Bool { + return false; + } + + @:hlNative("std", "semaphore_alloc") + static function alloc(value:Int):hl.Abstract<"hl_semaphore"> { + return null; + } +} +#end diff --git a/std/hl/_std/sys/thread/Thread.hx b/std/hl/_std/sys/thread/Thread.hx index 3db7ebb7..eae451b7 100644 --- a/std/hl/_std/sys/thread/Thread.hx +++ b/std/hl/_std/sys/thread/Thread.hx @@ -51,6 +51,27 @@ abstract Thread(ThreadImpl) from ThreadImpl { return HaxeThread.current(); } + + public function setName( name : String ) { + #if (hl_ver >= version("1.13.0")) + set_name(@:privateAccess this.handle, @:privateAccess name.toUtf8()); + #end + } + + public function getName() : Null { + #if (hl_ver >= version("1.13.0")) + var name = get_name(@:privateAccess this.handle); + return name == null ? null : @:privateAccess String.fromUTF8(name); + #else + return null; + #end + } + + #if (hl_ver >= version("1.13.0")) + @:hlNative("?std", "thread_set_name") static function set_name( t : ThreadHandle, name : hl.Bytes ) {} + @:hlNative("?std", "thread_get_name") static function get_name( t : ThreadHandle ) : hl.Bytes { return null; } + #end + function get_events():EventLoop { if(this.events == null) throw new NoEventLoopException(); @@ -66,25 +87,24 @@ abstract Thread(ThreadImpl) from ThreadImpl { private typedef ThreadHandle = hl.Abstract<"hl_thread">; private class HaxeThread { - static var mainThreadHandle:ThreadHandle; + static var mainThread:HaxeThread; - static var threads:Array<{thread:HaxeThread, handle:ThreadHandle}>; + static var threads:Array; static var threadsMutex:Mutex; + static var UID = 0; static function __init__() { - mainThreadHandle = currentHandle(); threadsMutex = new Mutex(); threads = []; - mainThread = new HaxeThread(); + mainThread = new HaxeThread(currentHandle()); mainThread.events = new EventLoop(); } + var id = UID++; public var events(default,null):Null; + var handle : ThreadHandle; final messages = new Deque(); - static var ids = 0; - var id = ids++; - @:hlNative("std", "thread_create") static function createHandle(callb:Void->Void):ThreadHandle { return null; @@ -97,47 +117,49 @@ private class HaxeThread { static public function current():HaxeThread { var handle = currentHandle(); - if(handle == mainThreadHandle) { + if(handle == mainThread.handle) { return mainThread; } threadsMutex.acquire(); var thread = null; for(item in threads) { if(item.handle == handle) { - thread = item.thread; + thread = item; break; } } if(thread == null) { - thread = new HaxeThread(); - threads.push({thread:thread, handle:handle}); + thread = new HaxeThread(handle); + threads.push(thread); } threadsMutex.release(); return thread; } public static function create(callb:()->Void, withEventLoop:Bool):Thread { - var item = {handle:null, thread:new HaxeThread()}; + var item = new HaxeThread(null); threadsMutex.acquire(); threads.push(item); threadsMutex.release(); if(withEventLoop) - item.thread.events = new EventLoop(); + item.events = new EventLoop(); item.handle = createHandle(() -> { if(item.handle == null) { item.handle = currentHandle(); } try { + hl.Api.setErrorHandler(function(_){}); callb(); if(withEventLoop) - item.thread.events.loop(); + item.events.loop(); } catch(e) { + hl.Api.setErrorHandler(null); dropThread(item); - throw e; + hl.Api.rethrow(e); } dropThread(item); }); - return item.thread; + return item; } public static function runWithEventLoop(job:()->Void):Void { @@ -172,7 +194,9 @@ private class HaxeThread { return messages.pop(block); } - public function new() {} + public function new(h) { + handle = h; + } public function sendMessage(msg:Dynamic) { messages.add(msg); diff --git a/std/hl/hl_version b/std/hl/hl_version index ed21137e..32bd932f 100644 --- a/std/hl/hl_version +++ b/std/hl/hl_version @@ -1 +1 @@ -1.10.0 \ No newline at end of file +1.12.0 \ No newline at end of file diff --git a/std/hl/types/ArrayBytes.hx b/std/hl/types/ArrayBytes.hx index 8edff5ad..7037f6c6 100644 --- a/std/hl/types/ArrayBytes.hx +++ b/std/hl/types/ArrayBytes.hx @@ -44,26 +44,6 @@ class BytesIterator extends ArrayIterator { } } -@:keep -@:generic -class BytesKeyValueIterator extends ArrayKeyValueIterator { - var a : ArrayBytes; - - public function new(a) { - super((null:Dynamic)); - this.a = a; - } - - override public function hasNext():Bool { - return current < a.length; - } - - override public function next():{key:Int, value:T} { - var v = @:privateAccess a.bytes.get(current); - return {key:current++, value:v}; - } -} - @:keep @:generic class ArrayBytes extends ArrayBase { var bytes:hl.BytesAccess; @@ -98,7 +78,9 @@ class BytesKeyValueIterator extends ArrayKeyValueIterator { if (length == 0) return null; length--; - return bytes[length]; + var v = bytes[length]; + bytes[length] = cast 0; + return v; } public function push(x:T):Int { @@ -126,6 +108,7 @@ class BytesKeyValueIterator extends ArrayKeyValueIterator { var v = bytes[0]; length--; (bytes : Bytes).blit(0, bytes, 1 << bytes.sizeBits, length << bytes.sizeBits); + bytes[length] = cast 0; return v; } @@ -191,6 +174,7 @@ class BytesKeyValueIterator extends ArrayKeyValueIterator { ret.size = ret.length = len; var end = pos + len; (bytes : Bytes).blit(pos << bytes.sizeBits, bytes, end << bytes.sizeBits, (length - end) << bytes.sizeBits); + (bytes : Bytes).fill((length - len) << bytes.sizeBits, (len) << bytes.sizeBits, 0); length -= len; return ret; } @@ -285,7 +269,7 @@ class BytesKeyValueIterator extends ArrayKeyValueIterator { } public function keyValueIterator() : ArrayKeyValueIterator { - return new BytesKeyValueIterator(this); + return new ArrayKeyValueIterator(cast this); } public function map(f:T->S):ArrayDyn@:privateAccess { diff --git a/std/hl/types/ArrayDyn.hx b/std/hl/types/ArrayDyn.hx index 603e4d9c..e044d842 100644 --- a/std/hl/types/ArrayDyn.hx +++ b/std/hl/types/ArrayDyn.hx @@ -43,24 +43,6 @@ class ArrayDynIterator extends ArrayIterator { } } -class ArrayDynKeyValueIterator extends ArrayKeyValueIterator { - var a : ArrayBase; - - public function new(a) { - super((null:Dynamic)); - this.a = a; - } - - override public function hasNext() { - return current < a.length; - } - - override public function next() { - var v = a.getDyn(current); - return {key:current++, value:v}; - } -} - @:keep class ArrayDyn extends ArrayAccess { public var length(get, never):Int; @@ -195,7 +177,7 @@ class ArrayDyn extends ArrayAccess { } public function keyValueIterator() : ArrayKeyValueIterator { - return new ArrayDynKeyValueIterator(array); + return new ArrayKeyValueIterator(cast array); } public function map(f:Dynamic->Dynamic):ArrayDyn { diff --git a/std/hl/types/ArrayObj.hx b/std/hl/types/ArrayObj.hx index 3431f898..decffe84 100644 --- a/std/hl/types/ArrayObj.hx +++ b/std/hl/types/ArrayObj.hx @@ -42,24 +42,6 @@ class ArrayObjIterator extends ArrayIterator { } } -class ArrayObjKeyValueIterator extends ArrayKeyValueIterator { - var arr:ArrayObj; - - public inline function new(arr:ArrayObj) { - super((null:Dynamic)); - this.arr = arr; - } - - override public function hasNext():Bool { - return current < arr.length; - } - - override public function next():{key:Int, value:T} { - var v = @:privateAccess arr.array[current]; - return {key:current++, value:v}; - } -} - @:keep class ArrayObj extends ArrayBase { var array:hl.NativeArray; @@ -291,7 +273,7 @@ class ArrayObj extends ArrayBase { } public function keyValueIterator():ArrayKeyValueIterator { - return new ArrayObjKeyValueIterator(this); + return new ArrayKeyValueIterator(cast this); } public function map(f:T->S):ArrayDyn { @@ -317,7 +299,7 @@ class ArrayObj extends ArrayBase { if (length < len) { __expand(len - 1); } else if (length > len) { - for (i in length...len) { + for (i in len...length) { array[i] = null; } this.length = len; diff --git a/std/hl/types/Int64Map.hx b/std/hl/types/Int64Map.hx new file mode 100644 index 00000000..fcf711bd --- /dev/null +++ b/std/hl/types/Int64Map.hx @@ -0,0 +1,74 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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. + */ + +package hl.types; + +#if (hl_ver >= version("1.13.0") && !hl_legacy32) + +typedef Int64MapData = Abstract<"hl_int64_map">; + +abstract Int64Map(Int64MapData) { + extern public inline function new() { + this = alloc(); + } + + @:hlNative("std", "hi64alloc") static function alloc():Int64MapData { + return null; + } + + @:hlNative("std", "hi64set") + public function set(key:haxe.Int64, value:Dynamic) {} + + @:hlNative("std", "hi64exists") + public function exists(key:haxe.Int64):Bool { + return false; + } + + @:hlNative("std", "hi64get") + public function get(key:haxe.Int64):Dynamic { + return null; + } + + @:hlNative("std", "hi64remove") + public function remove(key:haxe.Int64):Bool { + return false; + } + + @:hlNative("std", "hi64keys") + public function keysArray():NativeArray { + return null; + } + + @:hlNative("std", "hi64values") + public function valuesArray():NativeArray { + return null; + } + + @:hlNative("std", "hi64clear") + public function clear():Void {} + + extern public inline function iterator() { + return new NativeArray.NativeArrayIterator(valuesArray()); + } +} + +#end diff --git a/std/hl/uv/Fs.hx b/std/hl/uv/Fs.hx new file mode 100644 index 00000000..991da056 --- /dev/null +++ b/std/hl/uv/Fs.hx @@ -0,0 +1,51 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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. + */ + + package hl.uv; + + enum abstract Event(Int) { + var Rename = 1; + var Change = 2; +} + +@:hlNative("uv") +class Fs extends Handle { + public function new(?loop : Loop, path : String, onContentChanged : Event -> Void) { + if(loop == null) + loop = Loop.getDefault(); + super(fs_start_wrap(loop, (e) -> onContentChanged(cast(e, Event)), @:privateAccess path.toUtf8())); + } + + public function stop() { + if(handle == null) + return; + fs_stop_wrap(handle); + } + + static function fs_start_wrap(loop:Loop, cb : Int -> Void, path : hl.Bytes) : HandleData { + return null; + } + + static function fs_stop_wrap(handle:HandleData) : Bool { + return false; + } +} \ No newline at end of file diff --git a/std/java/_std/Std.hx b/std/java/_std/Std.hx index e9816c3c..1f939c88 100644 --- a/std/java/_std/Std.hx +++ b/std/java/_std/Std.hx @@ -23,6 +23,8 @@ import java.Boot; import java.Lib; +using StringTools; + @:coreApi @:nativeGen class Std { @:deprecated('Std.is is deprecated. Use Std.isOfType instead.') public static inline function is(v:Dynamic, t:Dynamic):Bool { @@ -63,54 +65,74 @@ import java.Lib; return cast x; } + static inline function isSpaceChar(code:Int):Bool + return (code > 8 && code < 14) || code == 32; + + static inline function isHexPrefix(cur:Int, next:Int):Bool + return cur == '0'.code && (next == 'x'.code || next == 'X'.code); + + static inline function isDecimalDigit(code:Int):Bool + return '0'.code <= code && code <= '9'.code; + + static inline function isHexadecimalDigit(code:Int):Bool + return isDecimalDigit(code) || ('a'.code <= code && code <= 'f'.code) || ('A'.code <= code && code <= 'F'.code); + public static function parseInt(x:String):Null { if (x == null) return null; - var base = 10; - var len = x.length; - var foundCount = 0; - var sign = 0; - var firstDigitIndex = 0; - var lastDigitIndex = -1; - var previous = 0; - - for(i in 0...len) { - var c = StringTools.fastCodeAt(x, i); - switch c { - case _ if((c > 8 && c < 14) || c == 32): - if(foundCount > 0) { - return null; - } - continue; - case '-'.code if(foundCount == 0): - sign = -1; - case '+'.code if(foundCount == 0): - sign = 1; - case '0'.code if(foundCount == 0 || (foundCount == 1 && sign != 0)): - case 'x'.code | 'X'.code if(previous == '0'.code && ((foundCount == 1 && sign == 0) || (foundCount == 2 && sign != 0))): - base = 16; - case _ if('0'.code <= c && c <= '9'.code): - case _ if(base == 16 && (('a'.code <= c && c <= 'z'.code) || ('A'.code <= c && c <= 'Z'.code))): - case _: - break; - } - if((foundCount == 0 && sign == 0) || (foundCount == 1 && sign != 0)) { - firstDigitIndex = i; + final len = x.length; + var index = 0; + + inline function hasIndex(index:Int) + return index < len; + + // skip whitespace + while (hasIndex(index)) { + if (!isSpaceChar(x.unsafeCodeAt(index))) + break; + ++index; + } + + // handle sign + final isNegative = hasIndex(index) && { + final sign = x.unsafeCodeAt(index); + if (sign == '-'.code || sign == '+'.code) { + ++index; } - foundCount++; - lastDigitIndex = i; - previous = c; + sign == '-'.code; } - if(firstDigitIndex <= lastDigitIndex) { - var digits = x.substring(firstDigitIndex + (base == 16 ? 2 : 0), lastDigitIndex + 1); - return try { - (sign == -1 ? -1 : 1) * java.lang.Integer.parseInt(digits, base); - } catch(e:java.lang.NumberFormatException) { - null; + + // handle base + final isHexadecimal = hasIndex(index + 1) && isHexPrefix(x.unsafeCodeAt(index), x.unsafeCodeAt(index + 1)); + if (isHexadecimal) + index += 2; // skip prefix + + // handle digits + final firstInvalidIndex = { + var cur = index; + if (isHexadecimal) { + while (hasIndex(cur)) { + if (!isHexadecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } + } else { + while (hasIndex(cur)) { + if (!isDecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } } + cur; } - return null; + + // no valid digits + if (index == firstInvalidIndex) + return null; + + final result = java.lang.Integer.parseInt(x.substring(index, firstInvalidIndex), if (isHexadecimal) 16 else 10); + return if (isNegative) -result else result; } public static function parseFloat(x:String):Float { diff --git a/std/java/_std/Sys.hx b/std/java/_std/Sys.hx index 75b2a87e..d4f0bb58 100644 --- a/std/java/_std/Sys.hx +++ b/std/java/_std/Sys.hx @@ -21,6 +21,8 @@ */ import java.lang.System; +import java.net.URI; +import java.nio.file.Paths; import sys.io.Process; using haxe.Int64; @@ -48,20 +50,19 @@ using haxe.Int64; return java.lang.System.getenv(s); } - public static function putEnv(s:String, v:String):Void { + public static function putEnv(s:String, v:Null):Void { // java offers no support for it (!) throw new haxe.exceptions.NotImplementedException("Not implemented in this platform"); } public static function environment():Map { - if (_env != null) - return _env; - var _env = _env = new haxe.ds.StringMap(); - for (mv in java.lang.System.getenv().entrySet()) { - _env.set(mv.getKey(), mv.getValue()); + if (_env == null) { + _env = new haxe.ds.StringMap(); + for (mv in java.lang.System.getenv().entrySet()) + _env.set(mv.getKey(), mv.getValue()); } - return _env; + return _env.copy(); } public static function sleep(seconds:Float):Void { @@ -141,7 +142,8 @@ using haxe.Int64; } public static function programPath():String { - return java.Lib.toNativeType(Sys).getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + final uri:URI = java.Lib.toNativeType(Sys).getProtectionDomain().getCodeSource().getLocation().toURI(); + return Std.string(Paths.get(uri)); } public static function getChar(echo:Bool):Int { diff --git a/std/java/_std/Type.hx b/std/java/_std/Type.hx index 63a06fbc..6966fdd5 100644 --- a/std/java/_std/Type.hx +++ b/std/java/_std/Type.hx @@ -179,8 +179,8 @@ enum ValueType { } // cache empty constructor arguments so we don't allocate it on each createEmptyInstance call - @:protected @:readOnly static var __createEmptyInstance_EMPTY_TYPES = java.NativeArray.make(java.Lib.toNativeEnum(java.internal.Runtime.EmptyObject)); - @:protected @:readOnly static var __createEmptyInstance_EMPTY_ARGS = java.NativeArray.make(java.internal.Runtime.EmptyObject.EMPTY); + @:protected @:readOnly static var __createEmptyInstance_EMPTY_TYPES = java.Lib.toNativeEnum(java.internal.Runtime.EmptyObject); + @:protected @:readOnly static var __createEmptyInstance_EMPTY_ARGS = java.internal.Runtime.EmptyObject.EMPTY; public static function createEmptyInstance(cl:Class):T { var t = java.Lib.toNativeType(cl); diff --git a/std/java/_std/haxe/Rest.hx b/std/java/_std/haxe/Rest.hx index 113965ce..1747861c 100644 --- a/std/java/_std/haxe/Rest.hx +++ b/std/java/_std/haxe/Rest.hx @@ -3,39 +3,87 @@ package haxe; import haxe.iterators.RestIterator; import haxe.iterators.RestKeyValueIterator; import java.NativeArray; -import java.lang.System; +import java.StdTypes; import java.lang.Object; +import java.lang.System; import java.util.Arrays; -private typedef NativeRest = NativeArray; +private typedef NativeRest = NativeArray; @:coreApi abstract Rest(NativeRest) { - public var length(get,never):Int; + public var length(get, never):Int; + inline function get_length():Int return this.length; - @:from static public function of(array:Array):Rest { - var native = @:privateAccess array.__a; - var result:NativeRest; - #if jvm - result = (cast native:Object).clone(); - #else - result = new NativeRest(native.length); - for(i in 0...native.length) - result[i] = cast native[i]; - #end + @:from extern inline static public function of(array:Array):Rest { + var result = createNative(array.length); + var src:NativeArray = cast @:privateAccess array.__a; + for (i in 0...result.length) + result[i] = cast src[i]; return new Rest(result); } + @:noDoc + @:generic + static function ofNativePrimitive(result:NativeRest, collection:NativeArray):Rest { + for (i in 0...collection.length) + result[i] = cast collection[i]; + return new Rest(cast result); + } + + @:noDoc + @:from static function ofNativeInt(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeFloat(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeBool(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeInt8(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeInt16(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeChar16(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeHaxeInt64(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNativeInt64(collection:NativeArray):Rest + return ofNativePrimitive(new NativeRest(collection.length), collection); + + @:noDoc + @:from static function ofNative(collection:NativeArray):Rest { + return new Rest(collection); + } + inline function new(a:NativeRest):Void this = a; + /** + * Implemented in genjvm (to auto-box primitive types) and genjava + */ + static function createNative(length:Int):NativeRest + return new NativeRest(length); + @:arrayAccess inline function get(index:Int):T - return cast this[index]; + return this[index]; @:to public function toArray():Array { - return [for(i in 0...this.length) cast this[i]]; + return [for (i in 0...this.length) this[i]]; } public inline function iterator():RestIterator @@ -44,15 +92,21 @@ abstract Rest(NativeRest) { public inline function keyValueIterator():RestKeyValueIterator return new RestKeyValueIterator(this); - public function append(item:T):Rest { - var result = new NativeRest(this.length + 1); + extern inline public function append(item:T):Rest { + return _append(createNative(this.length + 1), item); + } + + function _append(result:NativeRest, item:T):Rest { System.arraycopy(this, 0, result, 0, this.length); result[this.length] = cast item; return new Rest(result); } - public function prepend(item:T):Rest { - var result = new NativeRest(this.length + 1); + extern inline public function prepend(item:T):Rest { + return _prepend(createNative(this.length + 1), item); + } + + function _prepend(result:NativeRest, item:T):Rest { System.arraycopy(this, 0, result, 1, this.length); result[0] = cast item; return new Rest(result); @@ -61,4 +115,4 @@ abstract Rest(NativeRest) { public function toString():String { return toArray().toString(); } -} \ No newline at end of file +} diff --git a/std/java/_std/haxe/atomic/AtomicBool.hx b/std/java/_std/haxe/atomic/AtomicBool.hx new file mode 100644 index 00000000..2b98caab --- /dev/null +++ b/std/java/_std/haxe/atomic/AtomicBool.hx @@ -0,0 +1,34 @@ +package haxe.atomic; + +import java.util.concurrent.atomic.AtomicBoolean; + +abstract AtomicBool(AtomicBoolean) { + public inline function new(value:Bool) { + this = new AtomicBoolean(value); + } + + public inline function compareExchange(expected:Bool, replacement:Bool):Bool { + // Java's compareAndSet returns a boolean, so do a CAS loop to be able to return the original value without a potential race condition + + var original; + var real_replacement; + do { + original = this.get(); + real_replacement = original == expected ? replacement : original; + } while (!this.compareAndSet(original, real_replacement)); + return original; + } + + public inline function exchange(value:Bool):Bool { + return this.getAndSet(value); + } + + public inline function load():Bool { + return this.get(); + } + + public inline function store(value:Bool):Bool { + this.set(value); + return value; + } +} diff --git a/std/java/_std/haxe/atomic/AtomicInt.hx b/std/java/_std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..64dd6c17 --- /dev/null +++ b/std/java/_std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,64 @@ +package haxe.atomic; + +import java.util.concurrent.atomic.AtomicInteger; + +abstract AtomicInt(AtomicInteger) { + public inline function new(value:Int) { + this = new AtomicInteger(value); + } + + private inline function cas_loop(value:Int, op:(a:Int, b:Int) -> Int):Int { + var val; + + do { + val = this.get(); + } while (!this.compareAndSet(val, op(val, value))); + + return val; + } + + public inline function add(b:Int):Int { + return this.getAndAdd(b); + } + + public inline function sub(b:Int):Int { + return this.getAndAdd(-b); + } + + public inline function and(b:Int):Int { + return cas_loop(b, (a:Int, b:Int) -> a & b); + } + + public inline function or(b:Int):Int { + return cas_loop(b, (a:Int, b:Int) -> a | b); + } + + public inline function xor(b:Int):Int { + return cas_loop(b, (a:Int, b:Int) -> a ^ b); + } + + public inline function compareExchange(expected:Int, replacement:Int):Int { + // Java's compareAndSet returns a boolean, so do a CAS loop to be able to return the original value without a potential race condition + + var original; + var real_replacement; + do { + original = this.get(); + real_replacement = original == expected ? replacement : original; + } while (!this.compareAndSet(original, real_replacement)); + return original; + } + + public inline function exchange(value:Int):Int { + return this.getAndSet(value); + } + + public inline function load():Int { + return this.get(); + } + + public inline function store(value:Int):Int { + this.set(value); + return value; + } +} diff --git a/std/java/_std/haxe/atomic/AtomicObject.hx b/std/java/_std/haxe/atomic/AtomicObject.hx new file mode 100644 index 00000000..2bccf7a1 --- /dev/null +++ b/std/java/_std/haxe/atomic/AtomicObject.hx @@ -0,0 +1,34 @@ +package haxe.atomic; + +import java.util.concurrent.atomic.AtomicReference; + +abstract AtomicObject(AtomicReference) { + public inline function new(value:T) { + this = new AtomicReference(value); + } + + public inline function compareExchange(expected:T, replacement:T):T { + // Java's compareAndSet returns a boolean, so do a CAS loop to be able to return the original value without a potential race condition + + var original; + var real_replacement; + do { + original = this.get(); + real_replacement = original == expected ? replacement : original; + } while (!this.compareAndSet(original, real_replacement)); + return original; + } + + public inline function exchange(value:T):T { + return this.getAndSet(value); + } + + public inline function load():T { + return this.get(); + } + + public inline function store(value:T):T { + this.set(value); + return value; + } +} diff --git a/std/java/_std/haxe/ds/IntMap.hx b/std/java/_std/haxe/ds/IntMap.hx index 75053a2c..a5427aa7 100644 --- a/std/java/_std/haxe/ds/IntMap.hx +++ b/std/java/_std/haxe/ds/IntMap.hx @@ -364,7 +364,7 @@ import java.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -373,7 +373,7 @@ import java.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/java/_std/haxe/ds/ObjectMap.hx b/std/java/_std/haxe/ds/ObjectMap.hx index d01b4f63..c289ec08 100644 --- a/std/java/_std/haxe/ds/ObjectMap.hx +++ b/std/java/_std/haxe/ds/ObjectMap.hx @@ -378,7 +378,7 @@ import java.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -387,7 +387,7 @@ import java.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/java/_std/haxe/ds/StringMap.hx b/std/java/_std/haxe/ds/StringMap.hx index f0d44e2c..307b1517 100644 --- a/std/java/_std/haxe/ds/StringMap.hx +++ b/std/java/_std/haxe/ds/StringMap.hx @@ -377,7 +377,7 @@ import java.NativeArray; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -386,7 +386,7 @@ import java.NativeArray; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/java/_std/haxe/ds/WeakMap.hx b/std/java/_std/haxe/ds/WeakMap.hx index 7fe55c74..47ba4933 100644 --- a/std/java/_std/haxe/ds/WeakMap.hx +++ b/std/java/_std/haxe/ds/WeakMap.hx @@ -404,7 +404,7 @@ import java.lang.ref.ReferenceQueue; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -413,7 +413,7 @@ import java.lang.ref.ReferenceQueue; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/java/_std/sys/FileSystem.hx b/std/java/_std/sys/FileSystem.hx index 1c45f687..2635b441 100644 --- a/std/java/_std/sys/FileSystem.hx +++ b/std/java/_std/sys/FileSystem.hx @@ -41,19 +41,41 @@ class FileSystem { var f = new File(path); if (!f.exists()) throw "Path " + path + " doesn't exist"; - return { - gid: 0, // java doesn't let you get this info - uid: 0, // same - atime: Date.now(), // same - mtime: Date.fromTime(cast(f.lastModified(), Float)), - ctime: Date.fromTime(cast(f.lastModified(), Float)), // same - size: cast(f.length(), Int), // TODO: maybe change to Int64 for Haxe 3? - dev: 0, // FIXME: not sure what that is - ino: 0, // FIXME: not sure what that is - nlink: 0, // FIXME: not sure what that is - rdev: 0, // FIXME: not sure what that is - mode: 0 // FIXME: not sure what that is - }; + + try { + final pathObject = java.nio.file.Paths.get(path); + final attributes = java.nio.file.Files.readAttributes(pathObject, "unix:*"); + + return { + atime: Date.fromTime(cast(attributes.get("lastAccessTime").toMillis(), Float)), + ctime: Date.fromTime(cast(attributes.get("creationTime").toMillis(), Float)), + dev: cast(attributes.get("dev"), Int), + gid: attributes.get("gid"), + ino: cast(attributes.get("ino"), Int), + mode: attributes.get("mode"), + mtime: Date.fromTime(cast(attributes.get("lastModifiedTime").toMillis(), Float)), + nlink: attributes.get("nlink"), + rdev: cast(attributes.get("rdev"), Int), + size: cast(attributes.get("size"), Int), + uid: attributes.get("uid"), + }; + } + + catch (e) { + return { + gid: 0, // java doesn't let you get this info + uid: 0, // same + atime: Date.now(), // same + mtime: Date.fromTime(cast(f.lastModified(), Float)), + ctime: Date.fromTime(cast(f.lastModified(), Float)), // same + size: cast(f.length(), Int), // TODO: maybe change to Int64 for Haxe 3? + dev: 0, // FIXME: not sure what that is + ino: 0, // FIXME: not sure what that is + nlink: 0, // FIXME: not sure what that is + rdev: 0, // FIXME: not sure what that is + mode: 0 // FIXME: not sure what that is + }; + } } public static function fullPath(relPath:String):String { diff --git a/std/java/_std/sys/io/Process.hx b/std/java/_std/sys/io/Process.hx index 73a8f740..aa258425 100644 --- a/std/java/_std/sys/io/Process.hx +++ b/std/java/_std/sys/io/Process.hx @@ -75,7 +75,7 @@ class Process { } } - return new java.lang.ProcessBuilder(pargs); + return new java.lang.ProcessBuilder(...pargs); } public function new(cmd:String, ?args:Array, ?detached:Bool):Void { diff --git a/std/java/_std/sys/thread/Condition.hx b/std/java/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..fb469d0a --- /dev/null +++ b/std/java/_std/sys/thread/Condition.hx @@ -0,0 +1,45 @@ +package sys.thread; + +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.Condition as NativeCondition; + +@:access(sys.thread.Mutex) +@:coreApi +@:native('haxe.java.vm.Condition') +class Condition { + final lock:ReentrantLock; + final native:NativeCondition; + + public function new():Void { + this.lock = new ReentrantLock(); + this.native = lock.newCondition(); + } + + public function acquire():Void { + lock.lock(); + } + + public function tryAcquire():Bool { + return this.lock.tryLock(); + } + + public function release():Void { + lock.unlock(); + } + + // without the @:native, you get "java.lang.VerifyError: class sys.thread.Condition overrides final method java.lang.Object.wait()V" on jvm + // and "wait() in Condition cannot override wait() in Object" from javac + + @:native("waitOn") + public function wait():Void { + native.await(); + } + + public function signal():Void { + native.signal(); + } + + public function broadcast():Void { + native.signalAll(); + } +} diff --git a/std/java/_std/sys/thread/Semaphore.hx b/std/java/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..329434af --- /dev/null +++ b/std/java/_std/sys/thread/Semaphore.hx @@ -0,0 +1,25 @@ +package sys.thread; + +import java.util.concurrent.TimeUnit; + +@:coreApi +@:native('haxe.java.vm.Semaphore') +class Semaphore { + final native:java.util.concurrent.Semaphore; + + public function new(value:Int):Void { + this.native = new java.util.concurrent.Semaphore(value); + } + + public function acquire():Void { + native.acquire(); + } + + public function tryAcquire(?timeout:Float):Bool { + return timeout == null ? native.tryAcquire() : native.tryAcquire(haxe.Int64.fromFloat(timeout * 1000000000),TimeUnit.NANOSECONDS); + } + + public function release():Void { + native.release(); + } +} diff --git a/std/java/internal/HxObject.hx b/std/java/internal/HxObject.hx index 041cb8a7..fdc7fd18 100644 --- a/std/java/internal/HxObject.hx +++ b/std/java/internal/HxObject.hx @@ -193,7 +193,7 @@ class DynamicObject extends HxObject { if (ts != null) return ts(); var ret = new StringBuf(); - ret.add("{"); + ret.add("["); var first = true; for (f in Reflect.fields(this)) { if (first) @@ -207,7 +207,7 @@ class DynamicObject extends HxObject { } if (!first) ret.add(" "); - ret.add("}"); + ret.add("]"); return ret.toString(); } } diff --git a/std/js/Cookie.hx b/std/js/Cookie.hx index 3520c2d8..8994cc68 100644 --- a/std/js/Cookie.hx +++ b/std/js/Cookie.hx @@ -29,7 +29,7 @@ class Cookie { Create or update a cookie. @param expireDelay In seconds. If null, the cookie expires at end of session. **/ - public static function set(name:String, value:String, ?expireDelay:Int, ?path:String, ?domain:String) { + public static function set(name:String, value:String, ?expireDelay:Int, ?path:String, ?domain:String, ?secure:Bool, ?sameSite:String) { var s = name + "=" + StringTools.urlEncode(value); if (expireDelay != null) { var d = DateTools.delta(Date.now(), expireDelay * 1000); @@ -41,6 +41,12 @@ class Cookie { if (domain != null) { s += ";domain=" + domain; } + if (secure == true) { + s += ";secure"; + } + if (sameSite != null) { + s += ";SameSite=" + sameSite; + } Browser.document.cookie = s; } diff --git a/std/js/Lib.hx b/std/js/Lib.hx index 204d7eea..43722b4e 100644 --- a/std/js/Lib.hx +++ b/std/js/Lib.hx @@ -34,6 +34,14 @@ class Lib { js.Syntax.code("debugger"); } + /** + Inserts an `import` expression that loads JavaScript object from + a module or file specified in the `module` argument. + **/ + public static inline function dynamicImport(module:String):js.lib.Promise { + return js.Syntax.code("import({0})", module); + } + /** Display an alert message box containing the given message. @deprecated Use Browser.alert() instead. @@ -124,7 +132,7 @@ class Lib { } /** - Re-throw last cathed exception, preserving original stack information. + Re-throw last caught exception, preserving original stack information. Calling this is only possible inside a catch statement. **/ diff --git a/std/js/_std/Reflect.hx b/std/js/_std/Reflect.hx index 36343cd2..9a6d12ac 100644 --- a/std/js/_std/Reflect.hx +++ b/std/js/_std/Reflect.hx @@ -79,12 +79,8 @@ return (a == b) ? 0 : (((cast a) > (cast b)) ? 1 : -1); } - public static function compareMethods(f1:Dynamic, f2:Dynamic):Bool { - if (f1 == f2) - return true; - if (!isFunction(f1) || !isFunction(f2)) - return false; - return f1.scope == f2.scope && f1.method == f2.method && f1.method != null; + public static inline function compareMethods(f1:Dynamic, f2:Dynamic):Bool { + return f1 == f2; } @:access(js.Boot) diff --git a/std/js/_std/Std.hx b/std/js/_std/Std.hx index f7bb0f87..e11fe9c3 100644 --- a/std/js/_std/Std.hx +++ b/std/js/_std/Std.hx @@ -54,17 +54,24 @@ import js.Syntax; @:pure public static function parseInt(x:String):Null { - if(x != null) { - for(i in 0...x.length) { - var c = StringTools.fastCodeAt(x, i); - if(c <= 8 || (c >= 14 && c != ' '.code && c != '-'.code)) { - var nc = StringTools.fastCodeAt(x, i + 1); - var v = js.Lib.parseInt(x, (nc == "x".code || nc == "X".code) ? 16 : 10); - return Math.isNaN(v) ? null : cast v; - } + #if (js_es >= 5) + final v = js.Lib.parseInt(x); + #else + // before ES5, octal was supported in some implementations, so we need to explicitly use base 10 or 16 + if (x == null) + return null; + var v:Float = Math.NaN; + for (i => c in StringTools.keyValueIterator(x)) { + if ((c <= 8 || c >= 14) && !(c == ' '.code || c == '-'.code || c == '+'.code)) { + final nc = js.Syntax.code("{0}[{1}]", x, i + 1); + v = js.Lib.parseInt(x, c == '0'.code && (nc == "x" || nc == "X") ? 16 : 10); + break; } } - return null; + #end + if (Math.isNaN(v)) + return null; + return cast v; } public static inline function parseFloat(x:String):Float { @@ -77,7 +84,7 @@ import js.Syntax; static function __init__():Void untyped { - __feature__("js.Boot.getClass", String.prototype.__class__ = __feature__("Type.resolveClass", $hxClasses["String"] = String, String)); + __feature__("js.Boot.getClass", Object.defineProperty(String.prototype, "__class__", {value: __feature__("Type.resolveClass", $hxClasses["String"] = String, String), enumerable: false, writable: true})); __feature__("js.Boot.isClass", String.__name__ = __feature__("Type.getClassName", "String", true)); __feature__("Type.resolveClass", $hxClasses["Array"] = Array); __feature__("js.Boot.isClass", Array.__name__ = __feature__("Type.getClassName", "Array", true)); diff --git a/std/js/_std/haxe/NativeStackTrace.hx b/std/js/_std/haxe/NativeStackTrace.hx index f8cab5d0..05221f9b 100644 --- a/std/js/_std/haxe/NativeStackTrace.hx +++ b/std/js/_std/haxe/NativeStackTrace.hx @@ -62,7 +62,7 @@ class NativeStackTrace { for (i in 0...stack.length) { if(skip > i) continue; var line = stack[i]; - var matched:Null> = Syntax.code('{0}.match(/^ at ([A-Za-z0-9_. ]+) \\(([^)]+):([0-9]+):([0-9]+)\\)$/)', line); + var matched:Null> = Syntax.code("{0}.match(/^ at ([$A-Za-z0-9_. ]+) \\(([^)]+):([0-9]+):([0-9]+)\\)$/)", line); if (matched != null) { var path = matched[1].split("."); if(path[0] == "$hxClasses") { diff --git a/std/js/_std/haxe/atomic/AtomicInt.hx b/std/js/_std/haxe/atomic/AtomicInt.hx new file mode 100644 index 00000000..a86b2842 --- /dev/null +++ b/std/js/_std/haxe/atomic/AtomicInt.hx @@ -0,0 +1,50 @@ +package haxe.atomic; + +import js.lib.Atomics; + +abstract AtomicInt(js.lib.Int32Array) { + public inline function new(value:Int) { + this = new js.lib.Int32Array(new js.lib.SharedArrayBuffer(js.lib.Int32Array.BYTES_PER_ELEMENT)); + this[0] = value; + } + + private function asArray():js.lib.Int32Array { + return this; + } + + public inline function add(b:Int):Int { + return Atomics.add(asArray(), 0, b); + } + + public inline function sub(b:Int):Int { + return Atomics.sub(asArray(), 0, b); + } + + public inline function and(b:Int):Int { + return Atomics.and(asArray(), 0, b); + } + + public inline function or(b:Int):Int { + return Atomics.or(asArray(), 0, b); + } + + public inline function xor(b:Int):Int { + return Atomics.xor(asArray(), 0, b); + } + + public inline function compareExchange(expected:Int, replacement:Int):Int { + return Atomics.compareExchange(asArray(), 0, expected, replacement); + } + + public inline function exchange(value:Int):Int { + return Atomics.exchange(asArray(), 0, value); + } + + public inline function load():Int { + return Atomics.load(asArray(), 0); + } + + public inline function store(value:Int):Int { + return Atomics.store(asArray(), 0, value); + } +} diff --git a/std/js/_std/haxe/ds/IntMap.hx b/std/js/_std/haxe/ds/IntMap.hx index 62cfb0f5..64dddedf 100644 --- a/std/js/_std/haxe/ds/IntMap.hx +++ b/std/js/_std/haxe/ds/IntMap.hx @@ -81,7 +81,7 @@ package haxe.ds; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -90,7 +90,7 @@ package haxe.ds; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/js/_std/haxe/ds/ObjectMap.hx b/std/js/_std/haxe/ds/ObjectMap.hx index fb7e2780..c46b2cee 100644 --- a/std/js/_std/haxe/ds/ObjectMap.hx +++ b/std/js/_std/haxe/ds/ObjectMap.hx @@ -27,13 +27,6 @@ import js.Lib; @:coreApi class ObjectMap implements haxe.Constraints.IMap { - static var count:Int; - - // initialize count through __init__ magic, because these are generated - // before normal static initializations for which ObjectMap should be ready to use - // see https://github.com/HaxeFoundation/haxe/issues/6792 - static inline function __init__():Void - count = 0; static inline function assignId(obj:{}):Int { return Syntax.code('({0}.__id__ = {1})', obj, Lib.getNextHaxeUID()); @@ -113,7 +106,7 @@ class ObjectMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -122,7 +115,7 @@ class ObjectMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/js/_std/haxe/ds/StringMap.hx b/std/js/_std/haxe/ds/StringMap.hx index f93f8ad9..3bb72dfb 100644 --- a/std/js/_std/haxe/ds/StringMap.hx +++ b/std/js/_std/haxe/ds/StringMap.hx @@ -87,12 +87,12 @@ import haxe.DynamicAccess; @:analyzer(no_optimize) static function stringify(h:Dynamic):String { - var s = "{", first = true; + var s = "[", first = true; js.Syntax.code("for (var key in {0}) {", h); js.Syntax.code("\tif ({0}) {0} = false; else {1} += ',';", first, s); js.Syntax.code("\t{0} += key + ' => ' + {1}({2}[key]);", s, Std.string, h); js.Syntax.code("}"); - return s + "}"; + return s + "]"; } } @@ -286,7 +286,7 @@ private class StringMapIterator { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var keys = arrayKeys(); for (i in 0...keys.length) { var k = keys[i]; @@ -296,7 +296,7 @@ private class StringMapIterator { if (i < keys.length - 1) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/js/html/BeforeInstallPromptEvent.hx b/std/js/html/BeforeInstallPromptEvent.hx new file mode 100644 index 00000000..5e1382aa --- /dev/null +++ b/std/js/html/BeforeInstallPromptEvent.hx @@ -0,0 +1,34 @@ +package js.html; + +import js.lib.Promise; + +/** + The `BeforeInstallPromptEvent` is fired at the `Window.onbeforeinstallprompt` handler, + before a user is prompted to install a web site to a home screen. + + @see https://developer.mozilla.org/en-US/docs/Web/API/BeforeInstallPromptEvent +**/ +@:native("BeforeInstallPromptEvent") +extern class BeforeInstallPromptEvent extends Event { + + /** The platforms on which this event was dispatched. **/ + final platforms: Array; + + /** The user's choice to the install prompt. **/ + final userChoice: Promise; + + /** Creates a new `BeforeInstallPromptEvent`. **/ + function new(); + + /** Shows the install prompt. **/ + function prompt(): Promise; +} + +typedef BeforeInstallPromptUserChoice = { + final outcome: BeforeInstallPromptUserChoiceOutcome; +} + +enum abstract BeforeInstallPromptUserChoiceOutcome(String) { + var Accepted = "accepted"; + var Dismissed = "dismissed"; +} diff --git a/std/js/html/DOMElement.hx b/std/js/html/DOMElement.hx index 8b3c31e9..fee60e98 100644 --- a/std/js/html/DOMElement.hx +++ b/std/js/html/DOMElement.hx @@ -504,8 +504,8 @@ extern class DOMElement extends Node { Asynchronously asks the browser to make the element full-screen. @throws DOMError **/ - function requestFullscreen() : Void; - + function requestFullscreen(?options: FullscreenOptions) : js.lib.Promise; + /** Allows to asynchronously ask for the pointer to be locked on the given element. **/ diff --git a/std/js/html/DialogElement.hx b/std/js/html/DialogElement.hx new file mode 100644 index 00000000..21e81421 --- /dev/null +++ b/std/js/html/DialogElement.hx @@ -0,0 +1,60 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package js.html; + +/** + The `HTMLDialogElement` interface provides methods to manipulate `` elements. + It inherits properties and methods from the `HTMLElement` interface. + + @see +**/ +@:native("HTMLDialogElement") +extern class DialogElement extends Element { + + /** + A `Boolean` reflecting the open HTML attribute, indicating whether the dialog is available for interaction. + **/ + var open: Bool; + + /** + A `DOMString` that sets or returns the return value for the dialog. + **/ + var returnValue: String; + + /** + Closes the dialog. + An optional `DOMString` may be passed as an argument, updating the `returnValue` of the the dialog. + **/ + function close(?returnValue: String): Void; + + /** + Displays the dialog modelessly, i.e. still allowing interaction with content outside of the dialog. + **/ + function show(): Void; + + /** + Displays the dialog as a modal, over the top of any other dialogs that might be present. + Interaction outside the dialog is blocked. + **/ + function showModal(): Void; +} diff --git a/std/js/html/Document.hx b/std/js/html/Document.hx index 8085da99..b739b3fe 100644 --- a/std/js/html/Document.hx +++ b/std/js/html/Document.hx @@ -510,8 +510,13 @@ extern class Document extends Node { Releases the current mouse capture if it's on an element in this document. **/ function releaseCapture() : Void; - function exitFullscreen() : Void; - + + /** + Requests that the element on this document which is currently being presented in fullscreen mode + be taken out of fullscreen mode, restoring the previous state of the screen. + **/ + function exitFullscreen() : js.lib.Promise; + /** Release the pointer lock. **/ diff --git a/std/js/html/FullscreenOptions.hx b/std/js/html/FullscreenOptions.hx new file mode 100644 index 00000000..262dc73e --- /dev/null +++ b/std/js/html/FullscreenOptions.hx @@ -0,0 +1,40 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package js.html; + +/** + An object that controls the behavior of the transition to fullscreen mode. +**/ +typedef FullscreenOptions = { + + /** + Controls whether or not to show navigation UI while the element is in fullscreen mode. + **/ + var ?navigationUI: FullscreenOptionsNavigationUI; +} + +enum abstract FullscreenOptionsNavigationUI(String) { + var Auto = "auto"; + var Hide = "hide"; + var Show = "show"; +} diff --git a/std/js/html/HTMLDocument.hx b/std/js/html/HTMLDocument.hx index 8d9d0486..ccf56a6d 100644 --- a/std/js/html/HTMLDocument.hx +++ b/std/js/html/HTMLDocument.hx @@ -192,5 +192,6 @@ extern class HTMLDocument extends Document { inline function createTableRowElement() : TableRowElement { return cast createElement("tr"); } /** Shorthand for creating an HTML `` element. */ inline function createHtmlElement() : HtmlElement { return cast createElement("html"); } - -} \ No newline at end of file + /** Shorthand for creating an HTML `` element. */ + inline function createDialoglElement() : DialogElement { return cast createElement("dialog"); } +} diff --git a/std/js/html/Window.hx b/std/js/html/Window.hx index 84837b92..59b6f216 100644 --- a/std/js/html/Window.hx +++ b/std/js/html/Window.hx @@ -20,8 +20,6 @@ * DEALINGS IN THE SOFTWARE. */ -// This file is generated from mozilla\Window.webidl. Do not edit! - package js.html; import js.lib.Promise; @@ -559,6 +557,11 @@ extern class Window extends EventTarget { **/ function dump( str : String ) : Void; + /** + Creates a deep clone of a given value using the structured clone algorithm. + **/ + function structuredClone(value: T, ?options: {transfer: Array}): T; + /** Toggles a user's ability to resize a window. **/ diff --git a/std/js/html/WorkerGlobalScope.hx b/std/js/html/WorkerGlobalScope.hx index 6750412f..ecccea94 100644 --- a/std/js/html/WorkerGlobalScope.hx +++ b/std/js/html/WorkerGlobalScope.hx @@ -20,8 +20,6 @@ * DEALINGS IN THE SOFTWARE. */ -// This file is generated from mozilla\WorkerGlobalScope.webidl. Do not edit! - package js.html; import js.lib.Promise; @@ -83,6 +81,12 @@ extern class WorkerGlobalScope extends EventTarget { Allows you to write a message to stdout — i.e. in your terminal. This is the same as Firefox's `window.dump`, but for workers. **/ function dump( ?str : String ) : Void; + + /** + Creates a deep clone of a given value using the structured clone algorithm. + **/ + function structuredClone(value: T, ?options: {transfer: Array}): T; + /** @throws DOMError */ function btoa( btoa : String ) : String; /** @throws DOMError */ diff --git a/std/js/lib/Atomics.hx b/std/js/lib/Atomics.hx new file mode 100644 index 00000000..0fb148d1 --- /dev/null +++ b/std/js/lib/Atomics.hx @@ -0,0 +1,99 @@ +package js.lib; + +private typedef E = haxe.extern.EitherType; +private typedef IntTypedArray = E>>>>; + +/** + The Atomics object provides atomic operations as static methods. They are used with SharedArrayBuffer and ArrayBuffer objects. + + Documentation [Atomics](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/contributors.txt), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). +**/ +@:native("Atomics") +extern class Atomics { + /** + Adds the provided value to the existing value at the specified index of the array. + Returns the old value at that index. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function add(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + Computes a bitwise AND on the value at the specified index of the array with the provided value. + Returns the old value at that index. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function and(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + Stores a value at the specified index of the array, if it equals a value. + Returns the old value. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function compareExchange(typedArray:IntTypedArray, index:Int, expectedValue:Int, replacementValue:Int):Int; + + /** + Stores a value at the specified index of the array. + Returns the old value. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function exchange(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + An optimization primitive that can be used to determine whether to use locks or atomic operations. + Returns `true` if an atomic operation on arrays of the given element size will be implemented using a hardware atomic operation (as opposed to a lock). Experts only. + **/ + static function isLockFree(size:Int):Bool; + + /** + Returns the value at the specified index of the array. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function load(typedArray:IntTypedArray, index:Int):Int; + + /** + Notifies agents that are waiting on the specified index of the array. + Returns the number of agents that were notified. + **/ + static function notify(typedArray:IntTypedArray, index:Int, ?count:Int):Int; + + /** + Computes a bitwise OR on the value at the specified index of the array with the provided value. + Returns the old value at that index. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function or(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + Stores a value at the specified index of the array. + Returns the value. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function store(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + Subtracts a value at the specified index of the array. + Returns the old value at that index. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function sub(typedArray:IntTypedArray, index:Int, value:Int):Int; + + /** + Verifies that the specified index of the array still contains a value and sleeps awaiting or times out. + Returns either "ok", "not-equal", or "timed-out". If waiting is not allowed in the calling agent then it throws an Error exception. + Most browsers will not allow wait() on the browser's main thread.) + **/ + static function wait(typedArray:Int32Array, index:Int, value:Int, ?timeout:Int):WaitValue; + + /** + Computes a bitwise XOR on the value at the specified index of the array with the provided value. + Returns the old value at that index. + This atomic operation guarantees that no other write happens until the modified value is written back. + **/ + static function xor(typedArray:IntTypedArray, index:Int, value:Int):Int; +} + +enum abstract WaitValue(String) { + var OK = "ok"; + var NotEqual = "not-equal"; + var TimedOut = "timed-out"; +} diff --git a/std/js/lib/BufferSource.hx b/std/js/lib/BufferSource.hx index f36a0b6f..6141de9e 100644 --- a/std/js/lib/BufferSource.hx +++ b/std/js/lib/BufferSource.hx @@ -34,9 +34,13 @@ import haxe.extern.EitherType; @see */ -@:forward -abstract BufferSource(ArrayBuffer) to ArrayBuffer from ArrayBuffer { - @:from public static inline function fromBufferView(view:ArrayBufferView) { - return cast view.buffer; +abstract BufferSource(Dynamic) from ArrayBuffer from ArrayBufferView { + + public var byteLength(get, never): Int; + + @:pure + inline function get_byteLength(): Int { + return this.byteLength; } + } diff --git a/std/js/lib/Promise.hx b/std/js/lib/Promise.hx index b73c8b56..ef26ada9 100644 --- a/std/js/lib/Promise.hx +++ b/std/js/lib/Promise.hx @@ -59,7 +59,8 @@ extern class Promise { the first promise in the iterable that rejected. This method can be useful for aggregating results of multiple promises. **/ - static function all(iterable:Array):Promise>; + @:overload(function(iterable:Array):Promise> {}) + static function all(iterable:Array>):Promise>; /** Returns a promise that resolves after all of the given promises have either fulfilled or rejected, @@ -71,14 +72,16 @@ extern class Promise { In comparison, the Promise returned by `Promise.all` may be more appropriate if the tasks are dependent on each other / if you'd like to immediately reject upon any of them rejecting. **/ - static function allSettled(iterable:Array):Promise>; + @:overload(function(iterable:Array):Promise>> {}) + static function allSettled(iterable:Array>):Promise>>; /** Returns a promise that fulfills or rejects as soon as one of the promises in the iterable fulfills or rejects, with the value or reason from that promise. **/ - static function race(iterable:Array):Promise; + @:overload(function(iterable:Array):Promise {}) + static function race(iterable:Array>):Promise; /** @throws DOMError */ function new(init:(resolve:(value:T) -> Void, reject:(reason:Dynamic) -> Void) -> Void):Void; @@ -129,9 +132,9 @@ typedef ThenableStruct = { function then(onFulfilled:Null>, ?onRejected:PromiseHandler):Thenable; } -typedef PromiseSettleOutcome = { +typedef PromiseSettleOutcome = { var status:PromiseSettleStatus; - var ?value:Dynamic; + var ?value:T; var ?reason:Dynamic; } diff --git a/std/js/lib/SharedArrayBuffer.hx b/std/js/lib/SharedArrayBuffer.hx new file mode 100644 index 00000000..efd8859d --- /dev/null +++ b/std/js/lib/SharedArrayBuffer.hx @@ -0,0 +1,16 @@ +package js.lib; + +/** + The SharedArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer, similar to the ArrayBuffer object, but in a way that they can be used to create views on shared memory. + A SharedArrayBuffer is not a Transferable Object, unlike an ArrayBuffer which is transferable. + + Documentation [SharedArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer/contributors.txt), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). +**/ +@:native("SharedArrayBuffer") +extern class SharedArrayBuffer { + final byteLength:Int; + + function new(?length:Int):Void; + + function slice(?begin:Int, ?end:Int):ArrayBuffer; +} diff --git a/std/js/lib/WeakRef.hx b/std/js/lib/WeakRef.hx new file mode 100644 index 00000000..2f6faa86 --- /dev/null +++ b/std/js/lib/WeakRef.hx @@ -0,0 +1,38 @@ +package js.lib; +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * 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 `WeakRef` object lets you hold a weak reference to another object, without preventing that object from getting garbage-collected. + Documentation [WeakRef](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef) by [Mozilla Contributors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef$history), licensed under [CC-BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/). +**/ +@:native("WeakRef") +extern class WeakRef { + /** + Creates a new WeakRef object. + **/ + @:pure function new(target:T); + /** + Returns the WeakRef object's target object, or null if the target object has been reclaimed. + **/ + @:pure function deref():Null; +} diff --git a/std/js/lib/intl/DisplayNames.hx b/std/js/lib/intl/DisplayNames.hx new file mode 100644 index 00000000..8098bfc7 --- /dev/null +++ b/std/js/lib/intl/DisplayNames.hx @@ -0,0 +1,133 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package js.lib.intl; + +/** + The `Intl.DisplayNames` object enables the consistent translation of language, + region and script display names. +**/ +@:native("Intl.DisplayNames") +extern class DisplayNames { + + /** + Creates a new `Intl.DisplayNames` object. + **/ + @:overload(function(?locales: Array, ?options: DisplayNamesOptions): Void {}) + function new(?locales: String, ?options: DisplayNamesOptions); + + /** + Receives a code and returns a string based on the locale and options + provided when instantiating `Intl.DisplayNames`. + **/ + function of(code: String): String; + + /** + Returns a new object with properties reflecting the locale and formatting options + computed during initialization of the object. + **/ + function resolvedOptions(): DisplayNamesResolvedOptions; + + /** + Returns an array containing those of the provided locales that are supported + in display names without having to fall back to the runtime's default locale. + **/ + @:overload(function(locales: Array, ?options: DisplayNamesSupportedLocalesOfOptions): Array {}) + static function supportedLocalesOf(locales: String, ?options: DisplayNamesSupportedLocalesOfOptions): Array; +} + +enum abstract DisplayNamesFallback(String) { + var Code = "code"; + var None = "none"; +} + +typedef DisplayNamesOptions = { + /** + The fallback to use. + The default is `Code`. + **/ + var fallback: DisplayNamesFallback; + + /** + The locale matching algorithm to use. + The default is `BestFit`. + **/ + var ?localeMatcher: LocaleMatcher; + + /** + The formatting style to use. + The default is `Long`. + **/ + var ?style: DisplayNamesStyle; + + /** + The type to use. + The default is `Language`. + **/ + var ?type: DisplayNamesType; +} + +typedef DisplayNamesResolvedOptions = { + /** + The value provided for this property in the `options` argument of the constructor + or the default value (`Code`). + **/ + final fallback: DisplayNamesFallback; + + /** + The BCP 47 language tag for the locale actually used. + **/ + final locale: String; + + /** + The value provided for this property in the `options` argument of the constructor + or the default value (`Long`). + **/ + final style: DisplayNamesStyle; + + /** + The value provided for this property in the `options` argument of the constructor + or the default value (`Language`). + **/ + final type: DisplayNamesType; +} + +enum abstract DisplayNamesStyle(String) { + var Long = "long"; + var Narrow = "narrow"; + var Short = "short"; +} + +typedef DisplayNamesSupportedLocalesOfOptions = { + /** + The locale matching algorithm to use. + The default is `BestFit`. + */ + var ?localeMatcher: LocaleMatcher; +} + +enum abstract DisplayNamesType(String) { + var Currency = "currency"; + var Language = "language"; + var Region = "region"; + var Script = "script"; +} diff --git a/std/js/lib/intl/ListFormat.hx b/std/js/lib/intl/ListFormat.hx new file mode 100644 index 00000000..9504cf3b --- /dev/null +++ b/std/js/lib/intl/ListFormat.hx @@ -0,0 +1,121 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package js.lib.intl; + +/** + The `Intl.ListFormat` object enables language-sensitive list formatting. +**/ +@:native("Intl.ListFormat") +extern class ListFormat { + + /** + Creates a new `Intl.ListFormat` object. + **/ + @:overload(function(?locales: Array, ?options: ListFormatOptions): Void {}) + function new(?locales: String, ?options: ListFormatOptions); + + /** + Returns a language-specific formatted string representing the elements of the list. + **/ + function format(?list: Array): String; + + /** + Returns an array of objects representing the different components + that can be used to format a list of values in a locale-aware fashion. + **/ + function formatToParts(?list: Array): Array; + + /** + Returns an array containing those of the provided locales that are supported + without having to fall back to the runtime's default locale. + **/ + @:overload(function(locales: Array, ?options: ListFormatSupportedLocalesOfOptions): Array {}) + static function supportedLocalesOf(locales: String, ?options: ListFormatSupportedLocalesOfOptions): Array; +} + +typedef ListFormatOptions = { + /** + The locale matching algorithm to use. + The default is `BestFit`. + **/ + var ?localeMatcher: LocaleMatcher; + + /** + The length of the formatted message. + The default is `Long`. + **/ + var ?style: ListFormatStyle; + + /** + The format of output message. + **/ + var ?type: ListFormatType; +} + +typedef ListFormatPart = { + final type: ListFormatPartType; + final value: String; +} + +enum abstract ListFormatPartType(String) { + /** + A value from the list. + **/ + var Element = "element"; + + /** + A linguistic construct. + **/ + var Literal = "literal"; +} + +enum abstract ListFormatStyle(String) { + var Long = "long"; + var Narrow = "narrow"; + var Short = "short"; +} + +typedef ListFormatSupportedLocalesOfOptions = { + /** + The locale matching algorithm to use. + The default is `BestFit`. + */ + var ?localeMatcher: LocaleMatcher; +} + +enum abstract ListFormatType(String) { + /** + Stands for "and"-based lists. + **/ + var Conjunction = "conjunction"; + + /** + Stands for "or"-based lists. + **/ + var Disjunction = "disjunction"; + + /** + Stands for lists of values with units. + **/ + var Unit = "unit"; +} diff --git a/std/jvm/Closure.hx b/std/jvm/Closure.hx index 3e07a4b4..0501b3ae 100644 --- a/std/jvm/Closure.hx +++ b/std/jvm/Closure.hx @@ -47,7 +47,7 @@ class Closure extends ClosureDispatch { args; }; try { - return method.invoke(context, args); + return method.invoke(context, ...args); } catch (e:java.lang.reflect.InvocationTargetException) { throw e.getCause(); } diff --git a/std/jvm/Jvm.hx b/std/jvm/Jvm.hx index 8db7b18b..c643587d 100644 --- a/std/jvm/Jvm.hx +++ b/std/jvm/Jvm.hx @@ -291,7 +291,7 @@ class Jvm { static public function readFieldClosure(obj:Dynamic, name:String, parameterTypes:NativeArray>):Dynamic { var cl = (obj : java.lang.Object).getClass(); - var method = cl.getMethod(name, parameterTypes); + var method = cl.getMethod(name, ...parameterTypes); if (method.isBridge()) { /* This is probably not what we want... go through all methods and see if we find one that isn't a bridge. This is pretty awkward, but I can't figure out how to use the Java reflection diff --git a/std/jvm/_std/Std.hx b/std/jvm/_std/Std.hx index a2b54471..abf78cdb 100644 --- a/std/jvm/_std/Std.hx +++ b/std/jvm/_std/Std.hx @@ -22,6 +22,8 @@ import jvm.Jvm; +using StringTools; + @:coreApi class Std { @:deprecated('Std.is is deprecated. Use Std.isOfType instead.') @@ -63,55 +65,74 @@ class Std { return cast x; } + static inline function isSpaceChar(code:Int):Bool + return (code > 8 && code < 14) || code == 32; + + static inline function isHexPrefix(cur:Int, next:Int):Bool + return cur == '0'.code && (next == 'x'.code || next == 'X'.code); + + static inline function isDecimalDigit(code:Int):Bool + return '0'.code <= code && code <= '9'.code; + + static inline function isHexadecimalDigit(code:Int):Bool + return isDecimalDigit(code) || ('a'.code <= code && code <= 'f'.code) || ('A'.code <= code && code <= 'F'.code); + public static function parseInt(x:String):Null { - if (x == null) { + if (x == null) return null; + + final len = x.length; + var index = 0; + + inline function hasIndex(index:Int) + return index < len; + + // skip whitespace + while (hasIndex(index)) { + if (!isSpaceChar(x.unsafeCodeAt(index))) + break; + ++index; } - var base = 10; - var len = x.length; - var foundCount = 0; - var sign = 0; - var firstDigitIndex = 0; - var lastDigitIndex = -1; - var previous = 0; - - for (i in 0...len) { - var c = StringTools.fastCodeAt(x, i); - switch c { - case _ if ((c > 8 && c < 14) || c == 32): - if (foundCount > 0) { - return null; - } - continue; - case '-'.code if (foundCount == 0): - sign = -1; - case '+'.code if (foundCount == 0): - sign = 1; - case '0'.code if (foundCount == 0 || (foundCount == 1 && sign != 0)): - case 'x'.code | 'X'.code if (previous == '0'.code && ((foundCount == 1 && sign == 0) || (foundCount == 2 && sign != 0))): - base = 16; - case _ if ('0'.code <= c && c <= '9'.code): - case _ if (base == 16 && (('a'.code <= c && c <= 'z'.code) || ('A'.code <= c && c <= 'Z'.code))): - case _: - break; - } - if ((foundCount == 0 && sign == 0) || (foundCount == 1 && sign != 0)) { - firstDigitIndex = i; + // handle sign + final isNegative = hasIndex(index) && { + final sign = x.unsafeCodeAt(index); + if (sign == '-'.code || sign == '+'.code) { + ++index; } - foundCount++; - lastDigitIndex = i; - previous = c; + sign == '-'.code; } - if (firstDigitIndex <= lastDigitIndex) { - var digits = x.substring(firstDigitIndex + (base == 16 ? 2 : 0), lastDigitIndex + 1); - return try { - (sign == -1 ? -1 : 1) * java.lang.Integer.parseInt(digits, base); - } catch (e:java.lang.NumberFormatException) { - null; + + // handle base + final isHexadecimal = hasIndex(index + 1) && isHexPrefix(x.unsafeCodeAt(index), x.unsafeCodeAt(index + 1)); + if (isHexadecimal) + index += 2; // skip prefix + + // handle digits + final firstInvalidIndex = { + var cur = index; + if (isHexadecimal) { + while (hasIndex(cur)) { + if (!isHexadecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } + } else { + while (hasIndex(cur)) { + if (!isDecimalDigit(x.unsafeCodeAt(cur))) + break; + ++cur; + } } + cur; } - return null; + + // no valid digits + if (index == firstInvalidIndex) + return null; + + final result = java.lang.Integer.parseInt(x.substring(index, firstInvalidIndex), if (isHexadecimal) 16 else 10); + return if (isNegative) -result else result; } public static function parseFloat(x:String):Float { diff --git a/std/jvm/_std/Type.hx b/std/jvm/_std/Type.hx index 0e5cf289..d86d8aac 100644 --- a/std/jvm/_std/Type.hx +++ b/std/jvm/_std/Type.hx @@ -123,17 +123,9 @@ class Type { } } - static final emptyArg = { - var a = new java.NativeArray(1); - a[0] = (null : jvm.EmptyConstructor); - a; - } + static final emptyArg = (null : jvm.EmptyConstructor); - static final emptyClass = { - var a = new java.NativeArray(1); - a[0] = jvm.EmptyConstructor.native(); - a; - } + static final emptyClass = jvm.EmptyConstructor.native(); public static function createInstance(cl:Class, args:Array):T { var args = @:privateAccess args.getNative(); @@ -150,7 +142,7 @@ class Type { switch (Jvm.unifyCallArguments(args, params, true)) { case Some(args): ctor.setAccessible(true); - return ctor.newInstance(args); + return ctor.newInstance(...args); case None: } } @@ -166,7 +158,7 @@ class Type { case Some(args): var obj = emptyCtor.newInstance(emptyArg); method.setAccessible(true); - method.invoke(obj, args); + method.invoke(obj, ...args); return obj; case None: } @@ -178,7 +170,8 @@ class Type { public static function createEmptyInstance(cl:Class):T { var annotation = (cl.native().getAnnotation((cast ClassReflectionInformation : java.lang.Class))); if (annotation != null) { - return cl.native().getConstructor(emptyClass).newInstance(emptyArg); + return cl.native().getConstructor(emptyClass) + .newInstance(emptyArg); } else { return cl.native().newInstance(); } @@ -241,7 +234,15 @@ class Type { public static function getEnumConstructs(e:Enum):Array { var clInfo:java.lang.Class = cast EnumReflectionInformation; var annotation = e.native().getAnnotation(clInfo); - return @:privateAccess Array.ofNative(annotation.constructorNames()); + if (annotation != null) { + return @:privateAccess Array.ofNative(annotation.constructorNames()); + } + var vals = e.values(); + var ret = []; + for (i in 0...vals.length) { + ret[i] = vals[i].name(); + } + return ret; } public static function typeof(v:Dynamic):ValueType { @@ -303,7 +304,7 @@ class Type { var ret = []; for (name in all) { var v = Jvm.readField(e, name); - if (Jvm.instanceof(v, jvm.Enum)) { + if (Jvm.instanceof(v, java.lang.Enum)) { ret.push(v); } } diff --git a/std/jvm/_std/haxe/Rest.hx b/std/jvm/_std/haxe/Rest.hx new file mode 100644 index 00000000..1d31c5e9 --- /dev/null +++ b/std/jvm/_std/haxe/Rest.hx @@ -0,0 +1,61 @@ +package haxe; + +import haxe.ds.Vector; +import haxe.iterators.RestIterator; +import haxe.iterators.RestKeyValueIterator; + +private typedef NativeRest = Vector; + +@:coreApi +abstract Rest(NativeRest) { + public var length(get, never):Int; + + inline function get_length():Int + return this.length; + + inline function new(a:NativeRest) { + this = a; + } + + @:arrayAccess inline function get(index:Int):T + return this[index]; + + @:from extern inline static public function of(array:Array):Rest { + var result = new NativeRest(array.length); + for (i in 0...array.length) + result[i] = array[i]; + return new Rest(result); + } + + @:from extern inline static function fromNative(a:java.NativeArray):Rest { + return new Rest(Vector.fromData(a)); + } + + extern inline public function append(item:T):Rest { + var r = new NativeRest(length + 1); + Vector.blit(this, 0, r, 0, length); + r[length] = item; + return new Rest(r); + } + + extern inline public function prepend(item:T):Rest { + var r = new NativeRest(length + 1); + Vector.blit(this, 0, r, 1, length); + r[0] = item; + return new Rest(r); + } + + public inline function iterator():RestIterator + return new RestIterator(this); + + public inline function keyValueIterator():RestKeyValueIterator + return new RestKeyValueIterator(this); + + @:to public inline function toArray():Array { + return [for (i in 0...this.length) this[i]]; + } + + public inline function toString():String { + return toArray().toString(); + } +} diff --git a/std/jvm/_std/haxe/ds/StringMap.hx b/std/jvm/_std/haxe/ds/StringMap.hx index 4d77e119..85099650 100644 --- a/std/jvm/_std/haxe/ds/StringMap.hx +++ b/std/jvm/_std/haxe/ds/StringMap.hx @@ -72,7 +72,7 @@ class StringMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -81,7 +81,7 @@ class StringMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/lua/FileHandle.hx b/std/lua/FileHandle.hx index fff4617d..1167a1e0 100644 --- a/std/lua/FileHandle.hx +++ b/std/lua/FileHandle.hx @@ -29,6 +29,7 @@ import sys.io.FileInput; extern class FileHandle extends UserData { function flush():Void; function read(arg:Rest>):String; + function lines():NativeIterator; function close():Void; function write(str:String):Void; diff --git a/std/lua/_lua/_hx_anon.lua b/std/lua/_lua/_hx_anon.lua index 15826b2d..6ffc55b1 100644 --- a/std/lua/_lua/_hx_anon.lua +++ b/std/lua/_lua/_hx_anon.lua @@ -33,8 +33,8 @@ local function _hx_new(prototype) end function _hx_field_arr(obj) - res = {} - idx = 0 + local res = {} + local idx = 0 if obj.__fields__ ~= nil then obj = obj.__fields__ end diff --git a/std/lua/_lua/_hx_bit.lua b/std/lua/_lua/_hx_bit.lua index f0ca1be6..0eef3f32 100644 --- a/std/lua/_lua/_hx_bit.lua +++ b/std/lua/_lua/_hx_bit.lua @@ -1,16 +1,21 @@ --- require this for lua 5.1 -pcall(require, 'bit') -if bit then - _hx_bit_raw = bit - _hx_bit = setmetatable({}, { __index = _hx_bit_raw }); -else - _hx_bit_raw = _G.require('bit32') - _hx_bit = setmetatable({}, { __index = _hx_bit_raw }); +local hasBit32, bit32 = pcall(require, 'bit32') +if hasBit32 then --if we are on Lua 5.1, bit32 will be the default. + _hx_bit_raw = bit32 + _hx_bit = setmetatable({}, { __index = _hx_bit_raw }) -- lua 5.2 weirdness - _hx_bit.bnot = function(...) return _hx_bit_clamp(_hx_bit_raw.bnot(...)) end; - _hx_bit.bxor = function(...) return _hx_bit_clamp(_hx_bit_raw.bxor(...)) end; + _hx_bit.bnot = function(...) return _hx_bit_clamp(_hx_bit_raw.bnot(...)) end + _hx_bit.bxor = function(...) return _hx_bit_clamp(_hx_bit_raw.bxor(...)) end +else + --If we do not have bit32, fallback to 'bit' + local hasBit, bit = pcall(require, 'bit') + if not hasBit then + error("Failed to load bit or bit32") + end + _hx_bit_raw = bit + _hx_bit = setmetatable({}, { __index = _hx_bit_raw }) end + -- see https://github.com/HaxeFoundation/haxe/issues/8849 -_hx_bit.bor = function(...) return _hx_bit_clamp(_hx_bit_raw.bor(...)) end; -_hx_bit.band = function(...) return _hx_bit_clamp(_hx_bit_raw.band(...)) end; -_hx_bit.arshift = function(...) return _hx_bit_clamp(_hx_bit_raw.arshift(...)) end; +_hx_bit.bor = function(...) return _hx_bit_clamp(_hx_bit_raw.bor(...)) end +_hx_bit.band = function(...) return _hx_bit_clamp(_hx_bit_raw.band(...)) end +_hx_bit.arshift = function(...) return _hx_bit_clamp(_hx_bit_raw.arshift(...)) end diff --git a/std/lua/_lua/_hx_handle_error.lua b/std/lua/_lua/_hx_handle_error.lua new file mode 100644 index 00000000..52f97408 --- /dev/null +++ b/std/lua/_lua/_hx_handle_error.lua @@ -0,0 +1,8 @@ +function _hx_handle_error(obj) + local message = tostring(obj) + if _G.debug and _G.debug.traceback then + -- level 2 to skip _hx_handle_error + message = _G.debug.traceback(message, 2) + end + return setmetatable({}, { __tostring = function() return message end }) +end diff --git a/std/lua/_lua/_hx_luv.lua b/std/lua/_lua/_hx_luv.lua new file mode 100644 index 00000000..d8cef473 --- /dev/null +++ b/std/lua/_lua/_hx_luv.lua @@ -0,0 +1,8 @@ +if package.loaded.luv then + _hx_luv = _G.require("luv"); +else + _hx_luv = { + run=function(mode) return false end, + loop_alive=function() return false end + } +end \ No newline at end of file diff --git a/std/lua/_lua/_hx_tostring.lua b/std/lua/_lua/_hx_tostring.lua index fc72fb96..a3ebff2e 100644 --- a/std/lua/_lua/_hx_tostring.lua +++ b/std/lua/_lua/_hx_tostring.lua @@ -99,16 +99,3 @@ function _hx_tostring(obj, depth) return "" end end - -function _hx_error(obj) - if obj.value then - _G.print("runtime error:\n " .. _hx_tostring(obj.value)); - else - _G.print("runtime error:\n " .. tostring(obj)); - end - - if _G.debug and _G.debug.traceback then - _G.print(debug.traceback()); - end -end - diff --git a/std/lua/_std/EReg.hx b/std/lua/_std/EReg.hx index 077a7fbd..78f9d8c3 100644 --- a/std/lua/_std/EReg.hx +++ b/std/lua/_std/EReg.hx @@ -52,7 +52,7 @@ class EReg { } } - ropt |= FLAGS.UTF8; // always check validity of utf8 string + ropt |= FLAGS.UTF; // always check validity of utf8 string ropt |= FLAGS.UCP; // always enable utf8 character properties if (global == null) @@ -208,7 +208,7 @@ class EReg { static function __init__():Void { if (Rex == null) { - throw "Rex is missing. Please install lrexlib-pcre."; + throw "Rex is missing. Please install lrexlib-pcre2."; } } } diff --git a/std/lua/_std/Reflect.hx b/std/lua/_std/Reflect.hx index 600dc01c..ef4d5950 100644 --- a/std/lua/_std/Reflect.hx +++ b/std/lua/_std/Reflect.hx @@ -26,10 +26,12 @@ import lua.Boot; @:coreApi class Reflect { public inline static function hasField(o:Dynamic, field:String):Bool { - if (Lua.type(o) == "string" && (untyped String.prototype[field] != null || field == "length")) { - return true; + return if (inline isFunction(o)) { + false; + } else if (Lua.type(o) == "string" && (untyped String.prototype[field] != null || field == "length")) { + true; } else - return untyped o.__fields__ != null ? o.__fields__[field] != null : o[field] != null; + untyped o.__fields__ != null ? o.__fields__[field] != null : o[field] != null; } public static function field(o:Dynamic, field:String):Dynamic @@ -116,7 +118,7 @@ import lua.Boot; untyped { if (v == null) return false; - var t = __lua__("type(v)"); + var t = lua.Lua.type(v); return (t == "string" || (t == "table" && v.__enum__ == null)) || (t == "function" && (lua.Boot.isClass(v) || lua.Boot.isEnum(v)) != null); } diff --git a/std/lua/_std/Std.hx b/std/lua/_std/Std.hx index b0607904..c4b94d6e 100644 --- a/std/lua/_std/Std.hx +++ b/std/lua/_std/Std.hx @@ -58,28 +58,24 @@ import lua.NativeStringTools; public static function parseInt(x:String):Null { if (x == null) return null; - var hexMatch = NativeStringTools.match(x, "^[ \t\r\n]*([%-+]*0[xX][%da-fA-F]*)"); - if (hexMatch != null) { - var sign = switch StringTools.fastCodeAt(hexMatch, 0) { - case '-'.code: -1; - case '+'.code: 1; - case _: 0; - } - return (sign == -1 ? -1 : 1) * lua.Lua.tonumber(hexMatch.substr(sign == 0 ? 2 : 3), 16); - } else { - var intMatch = NativeStringTools.match(x, "^ *[%-+]?%d*"); - if (intMatch != null) { - return lua.Lua.tonumber(intMatch); - } else { - return null; - } + untyped { + __lua__("local sign, numString = {0}", NativeStringTools.match(x, "^%s*([%-+]?)0[xX]([%da-fA-F]*)")); + if (numString != null) + return switch sign { + case '-': -lua.Lua.tonumber(numString, 16); + case _: lua.Lua.tonumber(numString, 16); + } } + final intMatch = NativeStringTools.match(x, "^%s*[%-+]?%d*"); + if (intMatch == null) + return null; + return lua.Lua.tonumber(intMatch); } public static function parseFloat(x:String):Float { if (x == null || x == "") return Math.NaN; - var digitMatch = NativeStringTools.match(x, "^ *[%.%-+]?[0-9]%d*"); + var digitMatch = NativeStringTools.match(x, "^%s*[%.%-+]?[0-9]%d*"); if (digitMatch == null) { return Math.NaN; } diff --git a/std/lua/_std/String.hx b/std/lua/_std/String.hx index e4210c97..0ff7a4b5 100644 --- a/std/lua/_std/String.hx +++ b/std/lua/_std/String.hx @@ -62,7 +62,7 @@ class String { public inline function toLowerCase():String return BaseString.lower(this); - public inline function indexOf(str:String, ?startIndex:Int):Int { + public function indexOf(str:String, ?startIndex:Int):Int { if (startIndex == null) startIndex = 1; else @@ -86,7 +86,7 @@ class String { return startIndex > length ? length : startIndex; } - public inline function lastIndexOf(str:String, ?startIndex:Int):Int { + public function lastIndexOf(str:String, ?startIndex:Int):Int { var ret = -1; if (startIndex == null) startIndex = length; @@ -99,11 +99,11 @@ class String { return ret; } - public inline function split(delimiter:String):Array { - var idx = 1; + public function split(delimiter:String):Array { + var idx:Null = 1; var ret = []; while (idx != null) { - var newidx = 0; + var newidx:Null = 0; if (delimiter.length > 0) { newidx = BaseString.find(this, delimiter, idx, true).begin; } else if (idx >= this.length) { @@ -128,7 +128,7 @@ class String { return this; } - public inline function substring(startIndex:Int, ?endIndex:Int):String { + public function substring(startIndex:Int, ?endIndex:Int):String { if (endIndex == null) endIndex = this.length; if (endIndex < 0) @@ -151,7 +151,7 @@ class String { return BaseString.byte(this, index + 1); } - public inline function substr(pos:Int, ?len:Int):String { + public function substr(pos:Int, ?len:Int):String { if (len == null || len > pos + this.length) len = this.length; else if (len < 0) diff --git a/std/lua/_std/Sys.hx b/std/lua/_std/Sys.hx index d788dda8..e94e14ff 100644 --- a/std/lua/_std/Sys.hx +++ b/std/lua/_std/Sys.hx @@ -89,7 +89,7 @@ class Sys { } public inline static function getCwd():String - return Misc.cwd(); + return haxe.io.Path.addTrailingSlash(Misc.cwd()); public inline static function setCwd(s:String):Void Misc.chdir(s); @@ -98,7 +98,9 @@ class Sys { return Os.getenv(s); } - public inline static function putEnv(s:String, v:String):Void { + public inline static function putEnv(s:String, v:Null):Void { + if (v == null) + return Os.unsetenv(s); Os.setenv(s, v); } diff --git a/std/lua/_std/haxe/Exception.hx b/std/lua/_std/haxe/Exception.hx index 0a99bd3e..c267a7e6 100644 --- a/std/lua/_std/haxe/Exception.hx +++ b/std/lua/_std/haxe/Exception.hx @@ -49,6 +49,7 @@ class Exception { return __nativeException; } + @:keep // required for uncaught error handling public function toString():String { return message; } diff --git a/std/lua/_std/haxe/ds/IntMap.hx b/std/lua/_std/haxe/ds/IntMap.hx index 68c23db9..8d4683ea 100644 --- a/std/lua/_std/haxe/ds/IntMap.hx +++ b/std/lua/_std/haxe/ds/IntMap.hx @@ -96,7 +96,7 @@ class IntMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -105,7 +105,7 @@ class IntMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/lua/_std/haxe/ds/ObjectMap.hx b/std/lua/_std/haxe/ds/ObjectMap.hx index 3525cce4..638ff999 100644 --- a/std/lua/_std/haxe/ds/ObjectMap.hx +++ b/std/lua/_std/haxe/ds/ObjectMap.hx @@ -100,7 +100,7 @@ class ObjectMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -109,7 +109,7 @@ class ObjectMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/lua/_std/haxe/ds/StringMap.hx b/std/lua/_std/haxe/ds/StringMap.hx index f4b19fa0..956fa7cf 100644 --- a/std/lua/_std/haxe/ds/StringMap.hx +++ b/std/lua/_std/haxe/ds/StringMap.hx @@ -46,7 +46,7 @@ class StringMap implements haxe.Constraints.IMap { untyped { var ret = h[key]; if (ret == tnull) { - ret = null; + return null; } return ret; } @@ -100,7 +100,7 @@ class StringMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -109,7 +109,7 @@ class StringMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/lua/_std/haxe/format/JsonParser.hx b/std/lua/_std/haxe/format/JsonParser.hx index 2de964ec..d6dbba34 100644 --- a/std/lua/_std/haxe/format/JsonParser.hx +++ b/std/lua/_std/haxe/format/JsonParser.hx @@ -42,7 +42,11 @@ class JsonParser { If `str` is null, the result is unspecified. **/ static public inline function parse(str:String):Dynamic { + #if lua_vanilla + return new JsonParser(str).doParse(); + #else return lua.lib.hxluasimdjson.Json.parse(str); + #end } var str:String; diff --git a/std/lua/_std/sys/net/Socket.hx b/std/lua/_std/sys/net/Socket.hx index 23ce051c..9a982d0d 100644 --- a/std/lua/_std/sys/net/Socket.hx +++ b/std/lua/_std/sys/net/Socket.hx @@ -159,76 +159,4 @@ class Socket { var write_arr = res.write == null ? [] : Table.toArray(res.write).map(convert_socket); return {read: read_arr, write: write_arr, others: []}; } -} - -private class SocketInput extends haxe.io.Input { - var tcp:TcpClient; - - public function new(tcp:TcpClient) { - this.tcp = tcp; - } - - override public function readByte():Int { - var res = tcp.receive(1); - if (res.message == "closed"){ - throw new haxe.io.Eof(); - } - else if (res.message != null) - throw 'Error : ${res.message}'; - return res.result.charCodeAt(0); - } - - override public function readBytes(s:Bytes, pos:Int, len:Int):Int { - var leftToRead = len; - var b = s.getData(); - if (pos < 0 || len < 0 || pos + len > s.length) - throw haxe.io.Error.OutsideBounds; - var readCount = 0; - try { - while (leftToRead > 0) { - b[pos] = cast readByte(); - pos++; - readCount++; - leftToRead--; - } - } catch (e:haxe.io.Eof) { - if (readCount == 0) { - throw e; - } - } - return readCount; - } - -} - -private class SocketOutput extends haxe.io.Output { - var tcp:TcpClient; - - public function new(tcp:TcpClient) { - this.tcp = tcp; - } - - override public function writeByte(c:Int):Void { - var char = NativeStringTools.char(c); - var res = tcp.send(char); - if (res.message != null){ - throw 'Error : Socket writeByte : ${res.message}'; - } - } - - override public function writeBytes(s:Bytes, pos:Int, len:Int):Int { - if (pos < 0 || len < 0 || pos + len > s.length) - throw Error.OutsideBounds; - var b = s.getData().slice(pos, pos +len).map(function(byte){ - return lua.NativeStringTools.char(byte); - }); - var encoded = Table.concat(cast b, 0); - var res = tcp.send(encoded); - if (res.message != null){ - throw 'Error : Socket writeByte : ${res.message}'; - } - - return len; - } - -} +} \ No newline at end of file diff --git a/std/lua/_std/sys/net/SocketInput.hx b/std/lua/_std/sys/net/SocketInput.hx new file mode 100644 index 00000000..7b772df2 --- /dev/null +++ b/std/lua/_std/sys/net/SocketInput.hx @@ -0,0 +1,69 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package sys.net; + +import lua.lib.luasocket.socket.TcpClient; +import lua.*; + +import haxe.io.Bytes; +import haxe.io.Error; + +class SocketInput extends haxe.io.Input { + var tcp:TcpClient; + + public function new(tcp:TcpClient) { + this.tcp = tcp; + } + + override public function readByte():Int { + var res = tcp.receive(1); + if (res.message == "closed"){ + throw new haxe.io.Eof(); + } + else if (res.message != null) + throw 'Error : ${res.message}'; + return res.result.charCodeAt(0); + } + + override public function readBytes(s:Bytes, pos:Int, len:Int):Int { + var leftToRead = len; + var b = s.getData(); + if (pos < 0 || len < 0 || pos + len > s.length) + throw haxe.io.Error.OutsideBounds; + var readCount = 0; + try { + while (leftToRead > 0) { + b[pos] = cast readByte(); + pos++; + readCount++; + leftToRead--; + } + } catch (e:haxe.io.Eof) { + if (readCount == 0) { + throw e; + } + } + return readCount; + } + +} \ No newline at end of file diff --git a/std/lua/_std/sys/net/SocketOutput.hx b/std/lua/_std/sys/net/SocketOutput.hx new file mode 100644 index 00000000..d36edf05 --- /dev/null +++ b/std/lua/_std/sys/net/SocketOutput.hx @@ -0,0 +1,61 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package sys.net; + +import lua.lib.luasocket.socket.TcpClient; +import lua.*; + +import haxe.io.Bytes; +import haxe.io.Error; + +class SocketOutput extends haxe.io.Output { + var tcp:TcpClient; + + public function new(tcp:TcpClient) { + this.tcp = tcp; + } + + override public function writeByte(c:Int):Void { + var char = NativeStringTools.char(c); + var res = tcp.send(char); + if (res.message != null){ + throw 'Error : Socket writeByte : ${res.message}'; + } + } + + override public function writeBytes(s:Bytes, pos:Int, len:Int):Int { + if (pos < 0 || len < 0 || pos + len > s.length) + throw Error.OutsideBounds; + var b = s.getData().slice(pos, pos +len).map(function(byte){ + return lua.NativeStringTools.char(byte); + }); + var encoded = Table.concat(cast b, 0); + var res = tcp.send(encoded); + if (res.message != null){ + throw 'Error : Socket writeByte : ${res.message}'; + } + + return len; + } + +} \ No newline at end of file diff --git a/std/lua/_std/sys/ssl/Socket.hx b/std/lua/_std/sys/ssl/Socket.hx new file mode 100644 index 00000000..ab00ee40 --- /dev/null +++ b/std/lua/_std/sys/ssl/Socket.hx @@ -0,0 +1,69 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + + package sys.ssl; + + import sys.net.Host; + import sys.net.SocketInput; + import sys.net.SocketOutput; + + import lua.lib.luasocket.Socket as LuaSocket; + import lua.lib.luasec.Ssl as LuaSecSsl; + import lua.lib.luasec.SslTcpClient; + + import haxe.io.Bytes; + import haxe.io.Error; + +class Socket extends sys.net.Socket { + var _sslSocket:SslTcpClient; + + private function wrap(sock:LuaSocket):SslTcpClient { + var res = LuaSecSsl.wrap(sock, {mode: Client, protocol: Any}); + if (res.message != null) { + throw 'Socket Error : ${res.message}'; + } + return res.result; + } + + public function handshake():Void { + var res = this._sslSocket.dohandshake(); + if (res.message != null) { + throw 'Handshake Error : ${res.message}'; + } + } + + public override function connect(host:Host, port:Int):Void { + var res = LuaSocket.connect(host.host, port); + if (res.message != null) + throw 'Socket Error : ${res.message}'; + var sslSock = this.wrap(res.result); + input = new SocketInput(sslSock); + output = new SocketOutput(sslSock); + _sslSocket = sslSock; + _sslSocket.settimeout(timeout); + this.handshake(); + } + + public override function close():Void { + _sslSocket.close(); + } +} diff --git a/std/lua/lib/lrexlib/Rex.hx b/std/lua/lib/lrexlib/Rex.hx index 3bfe0f6f..a4a62b5c 100644 --- a/std/lua/lib/lrexlib/Rex.hx +++ b/std/lua/lib/lrexlib/Rex.hx @@ -24,7 +24,7 @@ package lua.lib.lrexlib; import haxe.extern.EitherType; -@:luaRequire("rex_pcre") +@:luaRequire("rex_pcre2") extern class Rex { inline static function create(expr:String, flag:EitherType):Rex { return untyped Rex['new'](expr, flag); diff --git a/std/lua/lib/luasec/Ssl.hx b/std/lua/lib/luasec/Ssl.hx new file mode 100644 index 00000000..d5ba53fe --- /dev/null +++ b/std/lua/lib/luasec/Ssl.hx @@ -0,0 +1,44 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package lua.lib.luasec; + +import lua.lib.luasocket.Socket as LuaSocket; + +@:luaRequire("ssl") +extern class Ssl { + static function wrap(sock:LuaSocket, params: SslParams):Result; +} + +typedef SslParams = { + mode: Mode, + protocol: Protocol, +} + +enum abstract Mode(String) { + var Client = "client"; + var Server = "server"; +} + +enum abstract Protocol(String) { + var Any = "any"; +} \ No newline at end of file diff --git a/std/lua/lib/luasec/SslTcpClient.hx b/std/lua/lib/luasec/SslTcpClient.hx new file mode 100644 index 00000000..92027fea --- /dev/null +++ b/std/lua/lib/luasec/SslTcpClient.hx @@ -0,0 +1,29 @@ +/* + * Copyright (C)2005-2022 Haxe Foundation + * + * 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. + */ + +package lua.lib.luasec; + +import lua.lib.luasocket.socket.TcpClient; + +extern class SslTcpClient extends TcpClient { + function dohandshake():Result; +} \ No newline at end of file diff --git a/std/lua/lib/luv/Check.hx b/std/lua/lib/luv/Check.hx index 2c74c9e0..296bb4e8 100644 --- a/std/lua/lib/luv/Check.hx +++ b/std/lua/lib/luv/Check.hx @@ -27,6 +27,6 @@ extern class Check extends Handle { static function new_check():Check; @:native("new_check") function new():Void; - function start(handle:Handle):Int; + function start(cb:()->Void):Int; function stop():Int; } diff --git a/std/lua/lib/luv/Loop.hx b/std/lua/lib/luv/Loop.hx index ed71fe7f..15feaa8f 100644 --- a/std/lua/lib/luv/Loop.hx +++ b/std/lua/lib/luv/Loop.hx @@ -3,6 +3,11 @@ package lua.lib.luv; @:luaRequire("luv") extern class Loop { static function loop_close():Bool; + /** + Runs the event loop of libuv. + + Haxe compiler automatically inserts a call to this function at the end of user's code if needed. + **/ static function run(?mode:String):Bool; static function loop_alive():Bool; static function stop():Void; diff --git a/std/lua/lib/luv/fs/FileSystem.hx b/std/lua/lib/luv/fs/FileSystem.hx index 75970fa0..d75ecf97 100644 --- a/std/lua/lib/luv/fs/FileSystem.hx +++ b/std/lua/lib/luv/fs/FileSystem.hx @@ -27,11 +27,15 @@ import lua.Result; @:luaRequire("luv") extern class FileSystem { + static final constants:Table; + @:native("fs_close") @:overload(function(file:FileDescriptor, cb:String->Bool->Void):Request {}) static function close(file:FileDescriptor):Result; @:native("fs_open") + @:overload(function(path:String, flags:Int, mode:Int):Result {}) + @:overload(function(path:String, flags:Int, mode:Int, ?cb:String->FileDescriptor->Void):Request {}) @:overload(function(path:String, flags:Open, mode:Int, ?cb:String->FileDescriptor->Void):Request {}) static function open(path:String, flags:Open, mode:Int):Result; @@ -40,20 +44,24 @@ extern class FileSystem { static function read(file:FileDescriptor, len:Int, offset:Int):Result; @:native("fs_unlink") - @:overload(function(file:FileDescriptor, ?cb:String->String->Void):Request {}) - static function unlink(file:FileDescriptor, content:String):Result; + @:overload(function(file:String, ?cb:String->Bool->Void):Request {}) + static function unlink(file:String, content:String):Result; @:native("fs_write") - @:overload(function(file:FileDescriptor, content:String, offset:Int, ?cb:String->Bool->Void):Int {}) - static function write(file:FileDescriptor, content:String, offset:Int):Result; + @:overload(function(file:FileDescriptor, content:String, offset:Int, ?cb:String->Int->Void):Int {}) + static function write(file:FileDescriptor, content:String, offset:Int):Result; @:native("fs_mkdir") @:overload(function(path:String, mode:Int, cb:String->Bool->Void):Request {}) static function mkdir(path:String, mode:Int):Result; @:native("fs_mkdtemp") - @:overload(function(data:String, cb:String->Bool->Void):Request {}) - static function mkdtemp(data:String):Result; + @:overload(function(template:String, cb:String->String->Void):Request {}) + static function mkdtemp(template:String):Result; + + @:native("fs_mkstemp") + @:overload(function(template:String, cb:String->FileDescriptor->String->Void):Request {}) + static function mkstemp(template:String):Result; @:native("fs_rmdir") @:overload(function(path:String, cb:String->Bool->Void):Request {}) @@ -146,23 +154,27 @@ extern class FileSystem { Not available on windows **/ @:native("fs_lchown") - @:overload(function(descriptor:FileDescriptor, uid:Int, gid:Int, cb:String->Bool->Void):Request {}) - static function lchown(descriptor:FileDescriptor, uid:Int, gid:Int):Bool; + @:overload(function(descriptor:String, uid:Int, gid:Int, cb:String->Bool->Void):Request {}) + static function lchown(descriptor:String, uid:Int, gid:Int):Bool; + + @:native("fs_copyfile") + @:overload(function(path:String, newPath:String, flags:Null, cb:String->Bool->Void):Request {}) + static function copyfile(path:String, newPath:String, ?flags:CopyFlags):Bool; @:native("fs_statfs") @:overload(function(path:String, cb:StatFs->Bool->Void):Request {}) static function statfs(path:String):StatFs; @:native("fs_opendir") - @:overload(function(path:String, cb:Handle->Bool->Void):Request {}) + @:overload(function(path:String, cb:String->Handle->Void, ?entries:Int):Request {}) static function opendir(path:String):Handle; @:native("fs_readdir") - @:overload(function(dir:Handle, cb:Table->Bool->Void):Request {}) - static function readdir(path:String):Table; + @:overload(function(dir:Handle, cb:String->Table->Void):Request {}) + static function readdir(path:Handle):Table; @:native("fs_closedir") - @:overload(function(dir:Handle, cb:Bool->Void):Request {}) + @:overload(function(dir:Handle, cb:String->Bool->Void):Request {}) static function closedir(dir:Handle):Bool; } @@ -213,3 +225,24 @@ typedef StatFs = { files:Int, ffree:Int } + +typedef CopyFlags = { + ?excl:Bool, + ?ficlone:Bool, + ?ficlone_force:Bool +} + +enum abstract AccessMode(Int) to Int { + // from libuv sources + + /* Test for read permission. */ + var R_OK = 4; + /* Test for write permission. */ + var W_OK = 2; + /* Test for execute permission. */ + var X_OK = 1; + /* Test for existence. */ + var F_OK = 0; + + @:op(A | B) function or(b:AccessMode):Int; +} \ No newline at end of file diff --git a/std/neko/Boot.hx b/std/neko/Boot.hx index bd1335a3..19ff40aa 100644 --- a/std/neko/Boot.hx +++ b/std/neko/Boot.hx @@ -108,15 +108,14 @@ class Boot { } } - private static function __tagserialize(o:Dynamic) - untyped { - var n = o.__enum__.__ename__; - var x = __dollar__amake(n.length + 1); - for (i in 0...n.length) - x[i] = n[i].__s; - x[n.length] = o.tag; - return x; - } + private static function __tagserialize(o:Dynamic) untyped { + var n = o.__enum__.__ename__; + var x = __dollar__amake(n.length + 1); + for (i in 0...n.length) + x[i] = n[i].__s; + x[n.length] = o.tag; + return x; + } private static function __unserialize(v:Dynamic) { untyped { @@ -125,7 +124,7 @@ class Boot { for (i in 0...__dollar__asize(v)) if (__dollar__typeof(v[i]) != __dollar__tstring) throw "Invalid serialized class data"; - var cl = neko.Boot.__classes; + var cl:Dynamic = neko.Boot.__classes; for (i in 0...__dollar__asize(v)) { cl = __dollar__objget(cl, __dollar__hash(v[i])); if (cl == null) diff --git a/std/neko/_std/Sys.hx b/std/neko/_std/Sys.hx index ed519417..c5d6be10 100644 --- a/std/neko/_std/Sys.hx +++ b/std/neko/_std/Sys.hx @@ -69,7 +69,7 @@ import haxe.SysTools; return new String(v); } - public static function putEnv( s : String, v : String ) : Void { + public static function putEnv( s : String, v : Null ) : Void { untyped put_env(s.__s,if( v == null ) null else v.__s); } diff --git a/std/neko/_std/haxe/ds/IntMap.hx b/std/neko/_std/haxe/ds/IntMap.hx index 330a02ca..1bcff654 100644 --- a/std/neko/_std/haxe/ds/IntMap.hx +++ b/std/neko/_std/haxe/ds/IntMap.hx @@ -74,7 +74,7 @@ package haxe.ds; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -83,7 +83,7 @@ package haxe.ds; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/neko/_std/haxe/ds/ObjectMap.hx b/std/neko/_std/haxe/ds/ObjectMap.hx index c4d959c9..8786e945 100644 --- a/std/neko/_std/haxe/ds/ObjectMap.hx +++ b/std/neko/_std/haxe/ds/ObjectMap.hx @@ -94,7 +94,7 @@ class ObjectMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -103,7 +103,7 @@ class ObjectMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/neko/_std/haxe/ds/StringMap.hx b/std/neko/_std/haxe/ds/StringMap.hx index 4582e63e..c517517f 100644 --- a/std/neko/_std/haxe/ds/StringMap.hx +++ b/std/neko/_std/haxe/ds/StringMap.hx @@ -74,7 +74,7 @@ package haxe.ds; public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -83,7 +83,7 @@ package haxe.ds; if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/php/Boot.hx b/std/php/Boot.hx index 42086c27..cbd7f2ed 100644 --- a/std/php/Boot.hx +++ b/std/php/Boot.hx @@ -559,10 +559,8 @@ class Boot { /** Create Haxe-compatible anonymous structure of `data` associative array **/ - static public function createAnon(data:NativeArray):Dynamic { - var o = new HxAnon(); - Syntax.foreach(data, (field:String, value:Any) -> Syntax.setField(o, field, value)); - return o; + static public inline function createAnon(data:NativeArray):Dynamic { + return new HxAnon(data); } /** @@ -948,6 +946,13 @@ private class HxDynamicStr extends HxClosure { @:keep @:dox(hide) private class HxAnon extends StdClass { + public function new(fields:NativeArray = null) { + super(); + if (fields != null) { + Syntax.foreach(fields, function(name, value) Syntax.setField(this, name, value)); + } + } + @:phpMagic function __get(name:String) { return null; diff --git a/std/php/Const.hx b/std/php/Const.hx index 34a45ed0..fe3bdd37 100644 --- a/std/php/Const.hx +++ b/std/php/Const.hx @@ -619,4 +619,62 @@ extern class Const { static final FILEINFO_PRESERVE_ATIME: Int; static final FILEINFO_RAW: Int; static final FILEINFO_SYMLINK: Int; + + /** + @see http://php.net/manual/en/calendar.constants.php + **/ + static final CAL_DOW_DAYNO: Int; + static final CAL_DOW_LONG: Int; + static final CAL_DOW_SHORT: Int; + static final CAL_EASTER_ALWAYS_GREGORIAN: Int; + static final CAL_EASTER_ALWAYS_JULIAN: Int; + static final CAL_EASTER_DEFAULT: Int; + static final CAL_EASTER_ROMAN: Int; + static final CAL_FRENCH: Int; + static final CAL_GREGORIAN: Int; + static final CAL_JEWISH: Int; + static final CAL_JEWISH_ADD_ALAFIM: Int; + static final CAL_JEWISH_ADD_ALAFIM_GERESH: Int; + static final CAL_JEWISH_ADD_GERESHAYIM: Int; + static final CAL_JULIAN: Int; + static final CAL_MONTH_FRENCH: Int; + static final CAL_MONTH_GREGORIAN_LONG: Int; + static final CAL_MONTH_GREGORIAN_SHORT: Int; + static final CAL_MONTH_JEWISH: Int; + static final CAL_MONTH_JULIAN_LONG: Int; + static final CAL_MONTH_JULIAN_SHORT: Int; + static final CAL_NUM_CALS: Int; + + /** + @see http://php.net/manual/en/ftp.constants.php + **/ + static final FTP_ASCII: Int; + static final FTP_AUTORESUME: Int; + static final FTP_AUTOSEEK: Int; + static final FTP_BINARY: Int; + static final FTP_FAILED: Int; + static final FTP_FINISHED: Int; + static final FTP_IMAGE: Int; + static final FTP_MOREDATA: Int; + static final FTP_TEXT: Int; + static final FTP_TIMEOUT_SEC: Int; + static final FTP_USEPASVADDRESS: Bool; + + /** + @see http://php.net/manual/en/misc.constants.php + **/ + static final CONNECTION_ABORTED: Int; + static final CONNECTION_NORMAL: Int; + static final CONNECTION_TIMEOUT: Int; + + /** + @see http://php.net/manual/fr/filesystem.constants.php + **/ + static final FILE_APPEND: Int; + static final FILE_BINARY: Int; + static final FILE_IGNORE_NEW_LINES: Int; + static final FILE_NO_DEFAULT_CONTEXT: Int; + static final FILE_SKIP_EMPTY_LINES: Int; + static final FILE_TEXT: Int; + static final FILE_USE_INCLUDE_PATH: Int; } diff --git a/std/php/DirectoryIterator.hx b/std/php/DirectoryIterator.hx new file mode 100644 index 00000000..00215b0f --- /dev/null +++ b/std/php/DirectoryIterator.hx @@ -0,0 +1,40 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +/** + The `DirectoryIterator` class provides a simple interface for viewing the contents of filesystem directories. + @see https://www.php.net/manual/en/class.directoryiterator.php +**/ +@:native("DirectoryIterator") +extern class DirectoryIterator extends SplFileInfo implements SeekableIterator { + function new(directory: String); + + function current(): DirectoryIterator; + function isDot(): Bool; + function key(): Int; + function next(): Void; + function rewind(): Void; + function seek(offset: Int): Void; + function valid(): Bool; +} diff --git a/std/php/FilesystemIterator.hx b/std/php/FilesystemIterator.hx new file mode 100644 index 00000000..8a6067f4 --- /dev/null +++ b/std/php/FilesystemIterator.hx @@ -0,0 +1,51 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; + +/** + The Filesystem iterator. + @see https://www.php.net/manual/en/class.filesystemiterator.php +**/ +@:native("FilesystemIterator") +extern class FilesystemIterator extends DirectoryIterator implements SeekableIterator { + @:phpClassConst static final CURRENT_AS_FILEINFO: Int; + @:phpClassConst static final CURRENT_AS_PATHNAME: Int; + @:phpClassConst static final CURRENT_AS_SELF: Int; + @:phpClassConst static final CURRENT_MODE_MASK: Int; + @:phpClassConst static final FOLLOW_SYMLINKS: Int; + @:phpClassConst static final KEY_AS_FILENAME: Int; + @:phpClassConst static final KEY_AS_PATHNAME: Int; + @:phpClassConst static final KEY_MODE_MASK: Int; + @:phpClassConst static final NEW_CURRENT_AND_KEY: Int; + @:phpClassConst static final SKIP_DOTS: Int; + @:phpClassConst static final UNIX_PATHS: Int; + + function new(directory: String, ?flags: Int); + + function current(): EitherType>; + function getFlags(): Int; + function key(): String; + function setFlags(flags: Int): Void; +} diff --git a/std/php/Global.hx b/std/php/Global.hx index 325ac131..005ecb77 100644 --- a/std/php/Global.hx +++ b/std/php/Global.hx @@ -9,6 +9,36 @@ import haxe.Constraints; **/ @:phpGlobal extern class Global { + /** + @see http://php.net/manual/en/function.get-browser.php + **/ + static function get_browser(?user_agent:String, ?return_array:Bool = false):EitherType>; + + /** + @see http://php.net/manual/en/function.get-defined-constants.php + **/ + static function get_defined_constants(categorize:Bool = false):NativeArray; + + /** + @see http://php.net/manual/en/function.finfo-file.php + **/ + static function finfo_file(finfo:Finfo, filename:String, flags:Int = 0, ?context:Resource):EitherType; + + /** + @see http://php.net/manual/en/function.finfo-open.php + **/ + static function finfo_open(flags:Int = 0, ?magic_database:String):EitherType; + + /** + @see http://php.net/manual/en/function.finfo-close.php + **/ + static function finfo_close(finfo:Finfo):Bool; + + /** + @see http://php.net/manual/en/function.fastcgi-finish-request.php + **/ + static function fastcgi_finish_request():Bool; + /** @see http://php.net/manual/en/function.exit.php **/ @@ -370,6 +400,11 @@ extern class Global { **/ static function strpos(haystack:String, needle:String, offset:Int = 0):EitherType; + /** + @see http://php.net/manual/en/function.stripos.php + **/ + static function stripos(haystack:String, needle:String, offset:Int = 0):EitherType; + /** @see http://php.net/manual/en/function.strrpos.php **/ @@ -390,6 +425,11 @@ extern class Global { **/ static function strcmp(str1:String, str2:String):Int; + /** + @see https://www.php.net/manual/en/function.strspn.php + **/ + static function strspn(string:String, characters:String, offset:Int = 0, ?length:Int):Int; + /** @see http://php.net/manual/en/function.strtr.php **/ @@ -407,6 +447,11 @@ extern class Global { static function str_replace(search:EitherType, replace:EitherType, subject:EitherType, ?count:Int):EitherType; + /** + @see https://www.php.net/manual/en/function.str-starts-with.php + **/ + static function str_starts_with(haystack:String, needle:String):Bool; + /** @see http://php.net/manual/en/function.explode.php **/ @@ -1197,7 +1242,7 @@ extern class Global { /** @see http://php.net/manual/en/function.mb-strlen.php **/ - static function mb_strlen(str:String, ?encoding:String):EitherType; + static function mb_strlen(str:String, ?encoding:String):Int; /** @see http://php.net/manual/en/function.mb-substr.php @@ -1454,6 +1499,11 @@ extern class Global { **/ static function base64_decode(data:String, strict:Bool = false):EitherType; + /** + @see http://php.net/manual/en/function.gethostname.php + **/ + static function gethostname():EitherType; + /** @see http://php.net/manual/en/function.gethostbyname.php **/ @@ -1868,4 +1918,244 @@ extern class Global { @see http://php.net/manual/en/function.empty.php **/ static function empty(variable:Any):Bool; + + /** + @see http://php.net/manual/en/function.quoted-printable-decode.php + **/ + static function quoted_printable_decode(string:String):String; + + /** + @see http://php.net/manual/en/function.quoted-printable-encode.php + **/ + static function quoted_printable_encode(string:String):String; + + /** + @see http://php.net/manual/en/function.easter-date.php + **/ + static function easter_date(?year:Int, ?mode:Int): Int; + + /** + @see http://php.net/manual/en/function.easter-days.php + **/ + static function easter_days(?year:Int, ?mode:Int): Int; + + /** + @see http://php.net/manual/en/function.sys-get-temp-dir.php + **/ + static function sys_get_temp_dir():String; + + /** + @see http://php.net/manual/en/function.tempnam.php + **/ + static function tempnam(directory:String, prefix:String):EitherType; + + /** + @see http://php.net/manual/en/function.tmpfile.php + **/ + static function tmpfile():EitherType; + + /** + @see http://php.net/manual/en/function.ftp-alloc.php + **/ + static function ftp_alloc(ftp: Resource, size: Int, ?response: Ref): Bool; + + /** + @see http://php.net/manual/en/function.ftp-append.php + **/ + static function ftp_append(ftp: Resource, remote_filename: String, local_filename: String, ?mode: Int): Bool; + + /** + @see http://php.net/manual/en/function.ftp-cdup.php + **/ + static function ftp_cdup(ftp: Resource): Bool; + + /** + @see http://php.net/manual/en/function.ftp-chdir.php + **/ + static function ftp_chdir(ftp: Resource, directory: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-chmod.php + **/ + static function ftp_chmod(ftp: Resource, permissions: Int, filename: String): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-close.php + **/ + static function ftp_close(ftp: Resource): Bool; + + /** + @see http://php.net/manual/en/function.ftp-connect.php + **/ + static function ftp_connect(hostname: String, port: Int = 21, timeout: Int = 90): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-delete.php + **/ + static function ftp_delete(ftp: Resource, filename: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-exec.php + **/ + static function ftp_exec(ftp: Resource, command: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-fget.php + **/ + static function ftp_fget(ftp: Resource, stream: Resource, remote_filename: String, ?mode: Int, offset: Int = 0): Bool; + + /** + @see http://php.net/manual/en/function.ftp-fput.php + **/ + static function ftp_fput(ftp: Resource, remote_filename: String, stream: Resource, ?mode: Int, offset: Int = 0): Bool; + + /** + @see http://php.net/manual/en/function.ftp-get.php + **/ + static function ftp_get(ftp: Resource, local_filename: String, remote_filename: String, offset: Int = 0): Bool; + + /** + @see http://php.net/manual/en/function.ftp-get-option.php + **/ + static function ftp_get_option(ftp: Resource, option: Int): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-login.php + **/ + static function ftp_login(ftp: Resource, username: String, password: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-mdtm.php + **/ + static function ftp_mdtm(ftp: Resource, filename: String): Int; + + /** + @see http://php.net/manual/en/function.ftp-mkdir.php + **/ + static function ftp_mkdir(ftp: Resource, directory: String): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-mlsd.php + **/ + static function ftp_mlsd(ftp: Resource, directory: String): EitherType>, Bool>; + + /** + @see http://php.net/manual/en/function.ftp-nb-continue.php + **/ + static function ftp_nb_continue(ftp: Resource): Int; + + /** + @see http://php.net/manual/en/function.ftp-nb-fget.php + **/ + static function ftp_nb_fget(ftp: Resource, stream: Resource, remote_filename: String, ?mode: Int, offset: Int = 0): Int; + + /** + @see http://php.net/manual/en/function.ftp-nb-fput.php + **/ + static function ftp_nb_fput(ftp: Resource, remote_filename: String, stream: Resource, ?mode: Int, offset: Int = 0): Int; + + /** + @see http://php.net/manual/en/function.ftp-nb-get.php + **/ + static function ftp_nb_get(ftp: Resource, local_filename: String, remote_filename: String, offset: Int = 0): Int; + + /** + @see http://php.net/manual/en/function.ftp-nb-put.php + **/ + static function ftp_nb_put(ftp: Resource, remote_filename: String, local_filename: String, offset: Int = 0): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-nlist.php + **/ + static function ftp_nlist(ftp: Resource, directory: String): EitherType, Bool>; + + /** + @see http://php.net/manual/en/function.ftp-pasv.php + **/ + static function ftp_pasv(ftp: Resource, enable: Bool): Bool; + + /** + @see http://php.net/manual/en/function.ftp-put.php + **/ + static function ftp_put(ftp: Resource, remote_filename: String, local_filename: String, offset: Int = 0): Bool; + + /** + @see http://php.net/manual/en/function.ftp-pwd.php + **/ + static function ftp_pwd(ftp: Resource): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-raw.php + **/ + static function ftp_raw(ftp: Resource, command: String): NativeIndexedArray; + + /** + @see http://php.net/manual/en/function.ftp-rawlist.php + **/ + static function ftp_rawlist(ftp: Resource, directory: String, recursive: Bool = false): EitherType, Bool>; + + /** + @see http://php.net/manual/en/function.ftp-rename.php + **/ + static function ftp_rename(ftp: Resource, from: String, to: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-rmdir.php + **/ + static function ftp_rmdir(ftp: Resource, directory: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-set-option.php + **/ + static function ftp_set_option(ftp: Resource, option: Int, value: EitherType): Bool; + + /** + @see http://php.net/manual/en/function.ftp-site.php + **/ + static function ftp_site(ftp: Resource, command: String): Bool; + + /** + @see http://php.net/manual/en/function.ftp-size.php + **/ + static function ftp_size(ftp: Resource, filename: String): Int; + + /** + @see http://php.net/manual/en/function.ftp-ssl-connect.php + **/ + static function ftp_ssl_connect(hostname: String, port: Int = 21, timeout: Int = 90): EitherType; + + /** + @see http://php.net/manual/en/function.ftp-systype.php + **/ + static function ftp_systype(ftp: Resource): EitherType; + + /** + @see http://php.net/manual/en/function.connection-aborted.php + **/ + static function connection_aborted(): Int; + + /** + @see http://php.net/manual/en/function.connection-status.php + **/ + static function connection_status(): Int; + + /** + @see http://php.net/manual/en/function.ignore-user-abort.php + **/ + static function ignore_user_abort(enable: Null = null): Int; + + /** + @see http://php.net/manual/en/function.highlight-file.php + **/ + static function highlight_file(filename: String, returns: Bool = false): EitherType; + + /** + @see http://php.net/manual/en/function.highlight-string.php + **/ + static function highlight_string(string: String, returns: Bool = false): EitherType; + + /** + @see http://php.net/manual/en/function.php-strip-whitespace.php + **/ + static function php_strip_whitespace(filename: String): String; } diff --git a/std/php/Lib.hx b/std/php/Lib.hx index a235388a..40cc520b 100644 --- a/std/php/Lib.hx +++ b/std/php/Lib.hx @@ -98,7 +98,7 @@ class Lib { return @:privateAccess Array.wrap(a); } - public static function hashOfAssociativeArray(arr:NativeAssocArray):Map { + public static function hashOfAssociativeArray(arr:NativeAssocArray):StringMap { var result = new StringMap(); @:privateAccess result.data = arr; return result; diff --git a/std/php/Phar.hx b/std/php/Phar.hx new file mode 100644 index 00000000..9395d43f --- /dev/null +++ b/std/php/Phar.hx @@ -0,0 +1,104 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; + +/** + The `Phar` class provides a high-level interface to accessing and creating PHAR archives. + @see https://www.php.net/manual/en/class.phar.php +**/ +@:native("Phar") +extern class Phar extends RecursiveDirectoryIterator implements Countable implements php.ArrayAccess { + @:phpClassConst static final BZ2: Int; + @:phpClassConst static final COMPRESSED: Int; + @:phpClassConst static final GZ: Int; + @:phpClassConst static final MD5: Int; + @:phpClassConst static final NONE: Int; + @:phpClassConst static final OPENSSL: Int; + @:phpClassConst static final PHAR: Int; + @:phpClassConst static final PHP: Int; + @:phpClassConst static final PHPS: Int; + @:phpClassConst static final SHA1: Int; + @:phpClassConst static final SHA256: Int; + @:phpClassConst static final SHA512: Int; + @:phpClassConst static final TAR: Int; + @:phpClassConst static final ZIP: Int; + + final static function apiVersion(): String; + final static function canCompress(type: Int = 0): Bool; + final static function canWrite(): Bool; + final static function createDefaultStub(?index: String, ?webIndex: String): String; + final static function getSupportedCompression(): NativeIndexedArray; + final static function getSupportedSignatures(): NativeIndexedArray; + final static function isValidPharFilename(filename: String, executable: Bool = true): Bool; + final static function loadPhar(filename: String, ?alias: String): Bool; + final static function mapPhar(?alias: String, dataoffset: Int = 0): Bool; + final static function mount(pharpath: String, externalpath: String): Void; + final static function mungServer(munglist: NativeIndexedArray): Void; + final static function running(retphar: Bool = true): String; + final static function unlinkArchive(archive: String): Bool; + final static function webPhar(?alias: String, index: String = "index.php", ?f404: String, ?mimetypes: NativeAssocArray, ?callable: String -> EitherType): Void; + + function new(fname: String, ?flags: Int, ?alias: String); + + function addEmptyDir(dirname: String): Void; + function addFile(file: String, ?localname: String): Void; + function addFromString(localname: String, contents: String): Void; + function buildFromDirectory(base_dir: String, ?regex: String): NativeAssocArray; + function buildFromIterator(iter: EitherType, NativeIterator>, ?base_directory: String): NativeAssocArray; + function compress(compression: Int, ?extension: String): Phar; + function compressFiles(compression: Int): Void; + function convertToData(?format: Int, ?compression: Int, ?extension: String): PharData; + function convertToExecutable(?format: Int, ?compression: Int, ?extension: String): Phar; + function copy(oldfile: String, newfile: String): Bool; + function count(): Int; + function decompress(?extension: String): PharData; + function decompressFiles(): Void; + function delete(entry: String): Bool; + function delMetadata(): Bool; + function extractTo(pathto: String, ?files: EitherType>, overwrite: Bool = false): Bool; + function getAlias(): String; + function getMetadata(): Dynamic; + function getModified(): Bool; + function getPath(): String; + function getSignature(): NativeAssocArray; + function getStub(): String; + function getVersion(): String; + function hasMetadata(): Bool; + function isBuffering(): Bool; + function isCompressed(): EitherType; + function isFileFormat(format: Int): Bool; + function isWritable(): Bool; + function offsetExists(offset: String): Bool; + function offsetGet(offset: String): PharFileInfo; + function offsetSet(offset: String, value: Dynamic): Void; + function offsetUnset(offset: String): Void; + function setAlias(alias: String): Bool; + function setDefaultStub(?index: String, ?webindex: String): Bool; + function setMetadata(metadata: Any): Void; + function setSignatureAlgorithm(sigtype: Int, ?privatekey: String): Void; + function setStub(stub: String, len: Int = -1): Bool; + function startBuffering(): Void; + function stopBuffering(): Void; +} diff --git a/std/php/PharData.hx b/std/php/PharData.hx new file mode 100644 index 00000000..f0f1e01e --- /dev/null +++ b/std/php/PharData.hx @@ -0,0 +1,59 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; + +/** + The `PharData` class provides a high-level interface to accessing and creating non-executable TAR and ZIP archives. + @see https://www.php.net/manual/en/class.phardata.php +**/ +@:native("PharData") +extern class PharData extends RecursiveDirectoryIterator implements Countable implements php.ArrayAccess { + function new(fname: String, ?flags: Int, ?alias: String, ?format: Int); + + function addEmptyDir(dirname: String): Void; + function addFile(file: String, ?localname: String): Void; + function addFromString(localname: String, contents: String): Void; + function buildFromDirectory(base_dir: String, ?regex: String): NativeAssocArray; + function buildFromIterator(iter: EitherType, NativeIterator>, ?base_directory: String): NativeAssocArray; + function compress(compression: Int, ?extension: String): PharData; + function compressFiles(compression: Int): Void; + function copy(oldfile: String, newfile: String): Bool; + function count(): Int; + function decompress(?extension: String): PharData; + function decompressFiles(): Void; + function delete(entry: String): Bool; + function delMetadata(): Bool; + function extractTo(pathto: String, ?files: EitherType>, overwrite: Bool = false): Bool; + function isWritable(): Bool; + function offsetExists(offset: String): Bool; + function offsetGet(offset: String): PharFileInfo; + function offsetSet(offset: String, value: Dynamic): Void; + function offsetUnset(offset: String): Void; + function setAlias(alias: String): Bool; + function setDefaultStub(?index: String, ?webindex: String): Bool; + function setMetadata(metadata: Any): Void; + function setSignatureAlgorithm(sigtype: Int): Void; + function setStub(stub: String, len: Int = -1): Bool; +} diff --git a/std/php/PharException.hx b/std/php/PharException.hx new file mode 100644 index 00000000..c66cbc5b --- /dev/null +++ b/std/php/PharException.hx @@ -0,0 +1,30 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +/** + The `PharException` class provides a phar-specific exception class for try/catch blocks. + @see https://www.php.net/manual/en/class.pharexception.php +**/ +@:native("PharException") +extern class PharException extends Exception {} diff --git a/std/php/PharFileInfo.hx b/std/php/PharFileInfo.hx new file mode 100644 index 00000000..1ddae2ca --- /dev/null +++ b/std/php/PharFileInfo.hx @@ -0,0 +1,46 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +/** + The `PharFileInfo` class provides a high-level interface to the contents and attributes of a single file within a PHAR archive. + @see https://www.php.net/manual/en/class.pharfileinfo +**/ +@:native("PharFileInfo") +extern class PharFileInfo extends SplFileInfo { + function new(entry: String); + + function chmod(permissions: Int): Void; + function compress(compression: Int): Bool; + function decompress(): Bool; + function delMetadata(): Bool; + function getCompressedSize(): Int; + function getContent(): String; + function getCRC32(): Int; + function getMetadata(): Dynamic; + function getPharFlags(): Int; + function hasMetadata(): Bool; + function isCompressed(?compression_type: Int): Bool; + function isCRCChecked(): Bool; + function setMetadata(metadata: Any): Void; +} diff --git a/std/php/RecursiveDirectoryIterator.hx b/std/php/RecursiveDirectoryIterator.hx new file mode 100644 index 00000000..0f52ec79 --- /dev/null +++ b/std/php/RecursiveDirectoryIterator.hx @@ -0,0 +1,39 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +/** + The `RecursiveDirectoryIterator` provides an interface for iterating recursively over filesystem directories. + @see https://www.php.net/manual/en/class.recursivedirectoryiterator.php +**/ +@:native("RecursiveDirectoryIterator") +extern class RecursiveDirectoryIterator extends FilesystemIterator implements RecursiveIterator implements SeekableIterator { + function new(directory: String, ?flags: Int); + + function getChildren(): Null; + function getSubPath(): String; + function getSubPathname(): String; + + @:overload(function(allowLinks: Bool): Bool {}) + function hasChildren(): Bool; +} diff --git a/std/php/RecursiveIterator.hx b/std/php/RecursiveIterator.hx new file mode 100644 index 00000000..160297f1 --- /dev/null +++ b/std/php/RecursiveIterator.hx @@ -0,0 +1,33 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +/** + Classes implementing `RecursiveIterator` can be used to iterate over iterators recursively. + @see https://www.php.net/manual/en/class.recursiveiterator.php +**/ +@:native("RecursiveIterator") +extern interface RecursiveIterator extends NativeIterator { + function getChildren(): Null>; + function hasChildren(): Bool; +} diff --git a/std/php/ResourceBundle.hx b/std/php/ResourceBundle.hx new file mode 100644 index 00000000..5fc55a47 --- /dev/null +++ b/std/php/ResourceBundle.hx @@ -0,0 +1,42 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; + +/** + The `ResourceBundle` class implements access to ICU resource data files. + @see https://www.php.net/manual/en/class.resourcebundle.php +**/ +@:native("ResourceBundle") +extern class ResourceBundle { + function new(locale: Null, bundle: Null, fallback: Bool = true); + + static function create(locale: Null, bundle: Null, fallback: Bool = true): Null; + static function getLocales(bundle: String): EitherType, Bool>; + + function count(): Int; + function get(index: EitherType, fallback: Bool = true): Dynamic; + function getErrorCode(): Int; + function getErrorMessage(): String; +} diff --git a/std/php/SplFileInfo.hx b/std/php/SplFileInfo.hx new file mode 100644 index 00000000..6243b578 --- /dev/null +++ b/std/php/SplFileInfo.hx @@ -0,0 +1,73 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; + +/** + The `SplFileInfo` class offers a high-level object-oriented interface to information for an individual file. + @see https://www.php.net/manual/en/class.splfileinfo.php +**/ +@:native("SplFileInfo") +extern class SplFileInfo { + function new(filename: String); + + function getATime(): EitherType; + function getBasename(suffix: String = ""): String; + function getCTime(): EitherType; + function getExtension(): String; + function getFileInfo(?className: String): SplFileInfo; + function getFilename(): String; + function getGroup(): EitherType; + function getInode(): EitherType; + function getLinkTarget(): EitherType; + function getMTime(): EitherType; + function getOwner(): EitherType; + function getPath(): String; + function getPathInfo(?className: String): Null; + function getPathname(): String; + function getPerms(): EitherType; + function getRealPath(): EitherType; + function getSize(): EitherType; + function getType(): EitherType; + function isDir(): Bool; + function isExecutable(): Bool; + function isFile(): Bool; + function isLink(): Bool; + function isReadable(): Bool; + function isWritable(): Bool; + function openFile(mode: String = "r", useIncludePath: Bool = false, ?context: Resource): SplFileObject; + function setFileClass(?className: String): Void; + function setInfoClass(?className: String): Void; +} + +enum abstract SplFileInfoType(String) to String { + var Block = "block"; + var Char = "char"; + var Dir = "dir"; + var Fifo = "fifo"; + var File = "file"; + var Link = "link"; + var Socket = "socket"; + var Unknown = "unknown"; +} diff --git a/std/php/SplFileObject.hx b/std/php/SplFileObject.hx new file mode 100644 index 00000000..1dab2050 --- /dev/null +++ b/std/php/SplFileObject.hx @@ -0,0 +1,71 @@ +/* + * Copyright (C)2005-2021 Haxe Foundation + * + * 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. + */ + +package php; + +import haxe.extern.EitherType; +import haxe.extern.Rest; + +/** + The `SplFileObject` class offers an object-oriented interface for a file. + @see https://www.php.net/manual/en/class.splfileobject.php +**/ +@:native("SplFileObject") +extern class SplFileObject extends SplFileInfo implements RecursiveIterator implements SeekableIterator { + @:phpClassConst static final DROP_NEW_LINE: Int; + @:phpClassConst static final READ_AHEAD: Int; + @:phpClassConst static final READ_CSV: Int; + @:phpClassConst static final SKIP_EMPTY: Int; + + function new(filename: String, mode: String = "r", useIncludePath: Bool = false, ?context: Resource); + + function current(): EitherType, Bool>>; + function eof(): Bool; + function fflush(): Bool; + function fgetc(): EitherType; + function fgetcsv(separator: String = ",", enclosure: String = "\"", escape: String = "\\"): EitherType, Bool>; + function fgets(): String; + function fgetss(?allowable_tags: String): String; + function flock(operation: Int, ?wouldBlock: Ref): Bool; + function fpassthru(): Int; + function fputcsv(fields: NativeIndexedArray, separator: String = ",", enclosure: String = "\"", escape: String = "\\"): EitherType; + function fread(length: Int): EitherType; + function fscanf(format: String, vars: Rest>): EitherType>; + function fseek(offset: Int, ?whence: Int): Int; + function fstat(): NativeArray; + function ftell(): EitherType; + function ftruncate(size: Int): Bool; + function fwrite(data: String, length: Int = 0): EitherType; + function getChildren(): Null>; + function getCsvControl(): NativeIndexedArray; + function getFlags(): Int; + function getMaxLineLen(): Int; + function hasChildren(): Bool; + function key(): Int; + function next(): Void; + function rewind(): Void; + function seek(line: Int): Void; + function setCsvControl(separator: String = ",", enclosure: String = "\"", escape: String = "\\"): Void; + function setFlags(flags: Int): Void; + function setMaxLineLen(maxLength: Int): Void; + function valid(): Bool; +} diff --git a/std/php/_polyfills.php b/std/php/_polyfills.php index 714a7dd0..74726f0d 100644 --- a/std/php/_polyfills.php +++ b/std/php/_polyfills.php @@ -60,4 +60,12 @@ namespace { //Namespace declaration is required because this file is included un } } + /** + * @see https://www.php.net/manual/en/function.str-starts-with.php + */ + if (!function_exists('str_starts_with')) { + function str_starts_with($str, $start) { + return (@substr_compare($str, $start, 0, strlen($start))==0); + } + } } \ No newline at end of file diff --git a/std/php/_std/Array.hx b/std/php/_std/Array.hx index f76434e5..f2002d1d 100644 --- a/std/php/_std/Array.hx +++ b/std/php/_std/Array.hx @@ -215,11 +215,14 @@ final class Array implements ArrayAccess implements IteratorAggregate @:noCompletion @:keep @:php.attribute('\\ReturnTypeWillChange') function offsetGet(offset:Int):Ref { - try { - return arr[offset]; - } catch (e:Dynamic) { - return null; + if(offset < 0 || offset >= length) { + //This var is required in generated php code + //because only variables can be returned by reference. + final result = null; + Syntax.keepVar(result); + return result; } + return arr[offset]; } @:noCompletion @:keep diff --git a/std/php/_std/Std.hx b/std/php/_std/Std.hx index 00df60bd..f4aaa639 100644 --- a/std/php/_std/Std.hx +++ b/std/php/_std/Std.hx @@ -52,22 +52,27 @@ import php.Syntax; } public static function parseInt(x:String):Null { - if (Global.is_numeric(x)) { - return Global.intval(x, 10); - } else { - x = Global.ltrim(x); - var firstCharIndex = (x.charAt(0) == '-' ? 1 : 0); - var firstCharCode = x.charCodeAt(firstCharIndex); - if (!isDigitCode(firstCharCode)) { + x = Global.ltrim(x, " \t\n\x0b\x0c\r"); + final digitsOnly = Global.ltrim(x, "+-"); + if (Global.str_starts_with(digitsOnly, '0x') || Global.str_starts_with(digitsOnly, '0X')) { + final val = Global.intval(x, 16); // hexadecimal + // if the value was 0, ensure there is only a maximum of one + or - sign + if (val == 0 && digitsOnly.length + 1 < x.length) return null; - } - var secondChar = x.charAt(firstCharIndex + 1); - if (secondChar == 'x' || secondChar == 'X') { - return Global.intval(x, 0); - } else { - return Global.intval(x, 10); - } + return val; + } + + switch Global.stripos(x, 'e') { + case false: + case ePos: x = Global.substr(x, 0, ePos); } + + final val = Global.intval(x, 10); + // if the value was 0, make sure it wasn't because the string had no valid digits + // last check ensures there is only a maximum of one + or - sign + if (val == 0 && (Global.strspn(digitsOnly, "0123456789", 0, 1) == 0 || digitsOnly.length + 1 < x.length)) + return null; + return val; } public static function parseFloat(x:String):Float { diff --git a/std/php/_std/Sys.hx b/std/php/_std/Sys.hx index 5de11a48..6160cdaa 100644 --- a/std/php/_std/Sys.hx +++ b/std/php/_std/Sys.hx @@ -29,6 +29,43 @@ import haxe.SysTools; /** Environment variables set by `Sys.putEnv()` */ static var customEnvVars = new NativeAssocArray(); + // we need to keep track of capitalization for windows + static final isWindows = systemName() == "Windows"; + + static var envCapitalization(get, null):Map; + + static function get_envCapitalization():Map { + if (envCapitalization == null) + return envCapitalization = [for (k => _ in SuperGlobal._SERVER) k.toUpperCase() => k]; + return envCapitalization; + } + + static inline function addCustom(name:String, value:String):Void { + customEnvVars[name] = value; + if (!isWindows) + return; + final upperCase = name.toUpperCase(); + if (envCapitalization.exists(upperCase)) + return; + envCapitalization[upperCase] = name; + } + + static inline function removeCustom(name:String):Void { + Global.unset(customEnvVars[name]); + if (!isWindows) + return; + envCapitalization.remove(name.toUpperCase()); + } + + static inline function getCapitalization(name:String):String { + if (!isWindows) + return name; + final existing = envCapitalization[name.toUpperCase()]; + if (existing != null) + return existing; + return name; + } + public static inline function print(v:Dynamic):Void { Global.echo(Std.string(v)); } @@ -50,9 +87,14 @@ import haxe.SysTools; return value == false ? null : value; } - public static function putEnv(s:String, v:String):Void { - customEnvVars[s] = '$v'; // in case of `null` it should become `"null"` - Global.putenv('$s=$v'); + public static function putEnv(s:String, v:Null):Void { + if (v == null) { + removeCustom(s); + Global.putenv('$s'); + } else { + addCustom(s, v); + Global.putenv('$s=$v'); + } } public static inline function sleep(seconds:Float):Void { @@ -124,7 +166,8 @@ import haxe.SysTools; public static function environment():Map { var env = SuperGlobal._SERVER; Syntax.foreach(customEnvVars, function(name:String, value:String) { - env[name] = value; + final actualName = getCapitalization(name); + env[actualName] = value; }); return php.Lib.hashOfAssociativeArray(env); } diff --git a/std/php/_std/haxe/Rest.hx b/std/php/_std/haxe/Rest.hx index e217ae24..426dcea5 100644 --- a/std/php/_std/haxe/Rest.hx +++ b/std/php/_std/haxe/Rest.hx @@ -17,6 +17,11 @@ abstract Rest(NativeRest) { static public inline function of(array:Array):Rest return new Rest(@:privateAccess array.arr); + @:noDoc + @:from + static inline function ofNative(array:NativeIndexedArray):Rest + return new Rest(array); + inline function new(a:NativeIndexedArray):Void this = a; diff --git a/std/php/_std/haxe/crypto/Base64.hx b/std/php/_std/haxe/crypto/Base64.hx index abc5915c..7d769c64 100644 --- a/std/php/_std/haxe/crypto/Base64.hx +++ b/std/php/_std/haxe/crypto/Base64.hx @@ -44,15 +44,18 @@ class Base64 { public static inline function decode(str:String, complement = true):Bytes { if (!complement) { - switch (strlen(str) % 3) { - case 1: - str += "=="; + switch (strlen(str) % 4) { case 2: + str += "=="; + case 3: str += "="; default: } } - return Bytes.ofString(base64_decode(str, true)); + return switch base64_decode(str, true) { + case false: throw new Exception("Base64.decode : invalid encoded string"); + case s: Bytes.ofString(s); + } } public static inline function urlEncode(bytes:Bytes, complement = false):String { @@ -62,14 +65,17 @@ class Base64 { public static inline function urlDecode(str:String, complement = false):Bytes { if (complement) { - switch (strlen(str) % 3) { - case 1: - str += "=="; + switch (strlen(str) % 4) { case 2: + str += "=="; + case 3: str += "="; default: } } - return Bytes.ofString(base64_decode(str_replace(URL_62_63, NORMAL_62_63, str), true)); + return switch base64_decode(str_replace(URL_62_63, NORMAL_62_63, str), true) { + case false: throw new Exception("Base64.urlDecode : invalid encoded string"); + case s: Bytes.ofString(s); + } } } diff --git a/std/php/_std/haxe/ds/IntMap.hx b/std/php/_std/haxe/ds/IntMap.hx index cd150ff8..8468b1a7 100644 --- a/std/php/_std/haxe/ds/IntMap.hx +++ b/std/php/_std/haxe/ds/IntMap.hx @@ -79,7 +79,7 @@ import php.NativeIndexedArray; Global.array_push(parts, '$key => ' + Std.string(value)); }); - return '{' + Global.implode(', ', parts) + '}'; + return "[" + Global.implode(", ", parts) + "]"; } public inline function clear():Void { diff --git a/std/php/_std/haxe/ds/ObjectMap.hx b/std/php/_std/haxe/ds/ObjectMap.hx index bf56073a..64d2f98a 100644 --- a/std/php/_std/haxe/ds/ObjectMap.hx +++ b/std/php/_std/haxe/ds/ObjectMap.hx @@ -78,7 +78,7 @@ class ObjectMap implements haxe.Constraints.IMap { } public function toString():String { - var s = "{"; + var s = "["; var it = keys(); for (i in it) { s += Std.string(i); @@ -87,7 +87,7 @@ class ObjectMap implements haxe.Constraints.IMap { if (it.hasNext()) s += ", "; } - return s + "}"; + return s + "]"; } public inline function clear():Void { diff --git a/std/php/_std/haxe/ds/StringMap.hx b/std/php/_std/haxe/ds/StringMap.hx index 3d323445..b924fd38 100644 --- a/std/php/_std/haxe/ds/StringMap.hx +++ b/std/php/_std/haxe/ds/StringMap.hx @@ -80,7 +80,7 @@ import haxe.Constraints; Global.array_push(parts, '$key => ' + Std.string(value)); }); - return '{' + Global.implode(', ', parts) + '}'; + return "[" + Global.implode(", ", parts) + "]"; } public inline function clear():Void { diff --git a/std/php/_std/haxe/ds/Vector.hx b/std/php/_std/haxe/ds/Vector.hx index 26126d27..d14ee989 100644 --- a/std/php/_std/haxe/ds/Vector.hx +++ b/std/php/_std/haxe/ds/Vector.hx @@ -28,19 +28,24 @@ private class PhpVectorData { public var length:Int; public var data:NativeIndexedArray; - public inline function new(length:Int) { + public inline function new(length:Int, data:NativeIndexedArray) { this.length = length; - data = new NativeIndexedArray(); + this.data = data; } } private typedef VectorData = PhpVectorData; +@:coreApi abstract Vector(VectorData) { public var length(get, never):Int; - public inline function new(length:Int) { - this = new VectorData(length); + extern overload public inline function new(length:Int) { + this = new VectorData(length, new NativeIndexedArray()); + } + + extern overload public inline function new(length:Int, defaultValue:T):Vector { + this = new VectorData(length, Global.array_fill(0, length, defaultValue)); } @:op([]) public inline function get(index:Int):T { @@ -55,6 +60,9 @@ abstract Vector(VectorData) { return this.length; } + public inline function fill(value:T):Void + this.data = Global.array_fill(0, length, value); + public static function blit(src:Vector, srcPos:Int, dest:Vector, destPos:Int, len:Int):Void { if (src == dest) { if (srcPos < destPos) { @@ -99,8 +107,7 @@ abstract Vector(VectorData) { } static public inline function fromArrayCopy(array:Array):Vector { - var vectorData = new VectorData(array.length); - vectorData.data = @:privateAccess array.arr; + var vectorData = new VectorData(array.length, @:privateAccess array.arr); return cast vectorData; } @@ -127,7 +134,7 @@ abstract Vector(VectorData) { return result; } - public inline function sort(f:T->T->Int):Void { + public inline function sort(f:T->T->Int):Void { Global.usort(this.data, f); } } diff --git a/std/php/db/PDO.hx b/std/php/db/PDO.hx index 1dd0fa66..08158063 100644 --- a/std/php/db/PDO.hx +++ b/std/php/db/PDO.hx @@ -99,7 +99,7 @@ extern class PDO { function errorInfo():NativeArray; function exec(statement:String):Int; function getAttribute(attribute:Int):Dynamic; - function getAvailableDrivers():NativeArray; + static function getAvailableDrivers():NativeIndexedArray; function lastInsertId(?name:String):String; function prepare(statement:String, ?driver_options:NativeArray):PDOStatement; function query(statement:String, ?mode:Int):PDOStatement; diff --git a/std/python/_std/Std.hx b/std/python/_std/Std.hx index 630bc630..6794b614 100644 --- a/std/python/_std/Std.hx +++ b/std/python/_std/Std.hx @@ -130,57 +130,74 @@ import python.Syntax; } } + static inline function isSpaceChar(char:String):Bool + return Syntax.isIn(char, " \n\r\t\x0b\x0c"); + + static inline function isHexPrefix(cur:String, next:String):Bool + return cur == '0' && (next == 'x' || next == 'X'); + + static inline function isDecimalDigit(char:String):Bool + return Syntax.isIn(char, "0123456789"); + + static inline function isHexadecimalDigit(char:String):Bool + return Syntax.isIn(char, "0123456789abcdefABCDEF"); + public static function parseInt(x:String):Null { if (x == null) return null; - try { - return UBuiltins.int(x); - } catch (e:Dynamic) { - var base = 10; - var len = x.length; - var foundCount = 0; - var sign = 0; - var firstDigitIndex = 0; - var lastDigitIndex = -1; - var previous = 0; - - for(i in 0...len) { - var c = StringTools.fastCodeAt(x, i); - switch c { - case _ if((c > 8 && c < 14) || c == 32): - if(foundCount > 0) { - return null; - } - continue; - case '-'.code if(foundCount == 0): - sign = -1; - case '+'.code if(foundCount == 0): - sign = 1; - case '0'.code if(foundCount == 0 || (foundCount == 1 && sign != 0)): - case 'x'.code | 'X'.code if(previous == '0'.code && ((foundCount == 1 && sign == 0) || (foundCount == 2 && sign != 0))): - base = 16; - case _ if('0'.code <= c && c <= '9'.code): - case _ if(base == 16 && (('a'.code <= c && c <= 'z'.code) || ('A'.code <= c && c <= 'Z'.code))): - case _: + + final len = x.length; + var index = 0; + + inline function hasIndex(index:Int) + return index < len; + + // skip whitespace + while (hasIndex(index)) { + if (!isSpaceChar(Syntax.arrayAccess(x, index))) + break; + ++index; + } + + // handle sign + final isNegative = hasIndex(index) && { + final sign = Syntax.arrayAccess(x, index); + if (sign == '-' || sign == '+') { + ++index; + } + sign == '-'; + } + + // handle base + final isHexadecimal = hasIndex(index + 1) && isHexPrefix(Syntax.arrayAccess(x, index), Syntax.arrayAccess(x, index + 1)); + if (isHexadecimal) + index += 2; // skip prefix + + // handle digits + final firstInvalidIndex = { + var cur = index; + if (isHexadecimal) { + while (hasIndex(cur)) { + if (!isHexadecimalDigit(Syntax.arrayAccess(x, cur))) break; + ++cur; } - if((foundCount == 0 && sign == 0) || (foundCount == 1 && sign != 0)) { - firstDigitIndex = i; - } - foundCount++; - lastDigitIndex = i; - previous = c; - } - if(firstDigitIndex <= lastDigitIndex) { - var digits = x.substring(firstDigitIndex, lastDigitIndex + 1); - return try { - (sign == -1 ? -1 : 1) * UBuiltins.int(digits, base); - } catch(e:Dynamic) { - null; + } else { + while (hasIndex(cur)) { + if (!isDecimalDigit(Syntax.arrayAccess(x, cur))) + break; + ++cur; } } - return null; + cur; } + + // no valid digits + if (index == firstInvalidIndex) + return null; + + final result = python.internal.UBuiltins.int(x.substring(index, firstInvalidIndex), if (isHexadecimal) 16 else 10); + return if (isNegative) -result else result; } static function shortenPossibleNumber(x:String):String { diff --git a/std/python/_std/Sys.hx b/std/python/_std/Sys.hx index 2da89ec8..90fcf39a 100644 --- a/std/python/_std/Sys.hx +++ b/std/python/_std/Sys.hx @@ -28,20 +28,6 @@ import haxe.ds.StringMap; @:coreApi class Sys { - static var environ(get,default):StringMap; - static function get_environ():StringMap { - return switch environ { - case null: - var environ = new StringMap(); - var env = Os.environ; - for (key in env.keys()) { - environ.set(key, env.get(key, null)); - } - Sys.environ = environ; - case env: env; - } - } - public static inline function time():Float { return Time.time(); } @@ -64,15 +50,27 @@ class Sys { } public static function getEnv(s:String):String { - return environ.get(s); + return Os.environ.get(s, null); } - public static function putEnv(s:String, v:String):Void { - python.lib.Os.putenv(s, v); - environ.set(s, v); + public static function putEnv(s:String, v:Null):Void { + if (v == null) { + try { + Os.environ.remove(s); + } catch(e:python.Exceptions.KeyError) { + // the variable didn't exist + } + return; + } + Os.environ.set(s, v); } public static function environment():Map { + final environ = new StringMap(); + final env = Os.environ; + for (key in env.keys()) { + environ.set(key, env.get(key, null)); + } return environ; } @@ -85,7 +83,7 @@ class Sys { } public static function getCwd():String { - return python.lib.Os.getcwd(); + return haxe.io.Path.addTrailingSlash(python.lib.Os.getcwd()); } public static function setCwd(s:String):Void { diff --git a/std/python/_std/haxe/ds/IntMap.hx b/std/python/_std/haxe/ds/IntMap.hx index c63eb6d4..14ce5f89 100644 --- a/std/python/_std/haxe/ds/IntMap.hx +++ b/std/python/_std/haxe/ds/IntMap.hx @@ -72,7 +72,7 @@ class IntMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -81,7 +81,7 @@ class IntMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/python/_std/haxe/ds/ObjectMap.hx b/std/python/_std/haxe/ds/ObjectMap.hx index 5dd5aa3f..2f5be7a4 100644 --- a/std/python/_std/haxe/ds/ObjectMap.hx +++ b/std/python/_std/haxe/ds/ObjectMap.hx @@ -71,7 +71,7 @@ class ObjectMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(Std.string(i)); @@ -80,7 +80,7 @@ class ObjectMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/python/_std/haxe/ds/StringMap.hx b/std/python/_std/haxe/ds/StringMap.hx index 110e07c4..96abf92c 100644 --- a/std/python/_std/haxe/ds/StringMap.hx +++ b/std/python/_std/haxe/ds/StringMap.hx @@ -73,7 +73,7 @@ class StringMap implements haxe.Constraints.IMap { public function toString():String { var s = new StringBuf(); - s.add("{"); + s.add("["); var it = keys(); for (i in it) { s.add(i); @@ -82,7 +82,7 @@ class StringMap implements haxe.Constraints.IMap { if (it.hasNext()) s.add(", "); } - s.add("}"); + s.add("]"); return s.toString(); } diff --git a/std/python/_std/sys/net/Socket.hx b/std/python/_std/sys/net/Socket.hx index a4f507ab..31efa7de 100644 --- a/std/python/_std/sys/net/Socket.hx +++ b/std/python/_std/sys/net/Socket.hx @@ -126,6 +126,11 @@ private class SocketOutput extends haxe.io.Output { __s = new PSocket(); } + function __rebuildIoStreams():Void { + input = new SocketInput(__s); + output = new SocketOutput(__s); + } + public function close():Void { __s.close(); } diff --git a/std/python/_std/sys/thread/Condition.hx b/std/python/_std/sys/thread/Condition.hx new file mode 100644 index 00000000..1c5a5782 --- /dev/null +++ b/std/python/_std/sys/thread/Condition.hx @@ -0,0 +1,34 @@ +package sys.thread; + +@:coreApi +class Condition { + final cond:python.lib.threading.Condition; + + public function new():Void { + this.cond = new python.lib.threading.Condition(); + } + + public function acquire():Void { + cond.acquire(); + } + + public function tryAcquire():Bool { + return cond.acquire(false); + } + + public function release():Void { + cond.release(); + } + + public function wait():Void { + cond.wait(); + } + + public function signal():Void { + cond.notify(); + } + + public function broadcast():Void { + cond.notify_all(); + } +} diff --git a/std/python/_std/sys/thread/Deque.hx b/std/python/_std/sys/thread/Deque.hx index 8a1664db..d20a485f 100644 --- a/std/python/_std/sys/thread/Deque.hx +++ b/std/python/_std/sys/thread/Deque.hx @@ -22,15 +22,17 @@ package sys.thread; +import python.lib.threading.Condition; + using python.internal.UBuiltins; class Deque { var deque:NativeDeque; - var lock:NativeCondition; + var lock:Condition; public function new() { deque = new NativeDeque(); - lock = new NativeCondition(); + lock = new Condition(); } public function add(i:T) { @@ -63,21 +65,9 @@ class Deque { @:pythonImport("collections", "deque") @:native("deque") -extern class NativeDeque { +private extern class NativeDeque { function new(); function append(x:T):Void; function appendleft(x:T):Void; function popleft():T; } - -@:pythonImport("threading", "Condition") -@:native("Condition") -private extern class NativeCondition { - function new(?lock:Dynamic); - function acquire(blocking:Bool = true, timeout:Float = -1):Bool; - function release():Void; - function wait(?timeout:Float):Bool; - function wait_for(predicate:()->Bool, ?timeout:Float):Bool; - function notify(n:Int = 1):Void; - function notify_all():Void; -} \ No newline at end of file diff --git a/std/python/_std/sys/thread/Lock.hx b/std/python/_std/sys/thread/Lock.hx index 724fe546..03736cfc 100644 --- a/std/python/_std/sys/thread/Lock.hx +++ b/std/python/_std/sys/thread/Lock.hx @@ -22,12 +22,14 @@ package sys.thread; +import python.lib.threading.Semaphore; + @:coreApi class Lock { - final semaphore:NativeSemaphore; + final semaphore:Semaphore; public inline function new() { - semaphore = new NativeSemaphore(0); + semaphore = new Semaphore(0); } public inline function wait(?timeout:Float):Bool { @@ -38,11 +40,3 @@ class Lock { semaphore.release(); } } - -@:pythonImport("threading", "Semaphore") -@:native("Lock") -private extern class NativeSemaphore { - function new(value:Int); - function acquire(blocking:Bool = true, ?timeout:Float):Bool; - function release():Void; -} \ No newline at end of file diff --git a/std/python/_std/sys/thread/Mutex.hx b/std/python/_std/sys/thread/Mutex.hx index 87a9a307..38283c0f 100644 --- a/std/python/_std/sys/thread/Mutex.hx +++ b/std/python/_std/sys/thread/Mutex.hx @@ -22,12 +22,14 @@ package sys.thread; +import python.lib.threading.RLock; + @:coreApi class Mutex { - final lock:NativeRLock; + final lock:RLock; inline public function new():Void { - lock = new NativeRLock(); + lock = new RLock(); } inline public function acquire():Void { @@ -42,10 +44,3 @@ class Mutex { lock.release(); } } - -@:pythonImport("threading", "RLock") -private extern class NativeRLock { - function new():Void; - function acquire(blocking:Bool):Bool; - function release():Void; -} \ No newline at end of file diff --git a/std/python/_std/sys/thread/Semaphore.hx b/std/python/_std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..8dd58817 --- /dev/null +++ b/std/python/_std/sys/thread/Semaphore.hx @@ -0,0 +1,24 @@ +package sys.thread; + +import python.lib.threading.Semaphore as NativeSemaphore; + +@:coreApi +class Semaphore { + final semaphore:NativeSemaphore; + + public function new(value:Int):Void { + this.semaphore = new NativeSemaphore(value); + } + + public function acquire():Void { + semaphore.acquire(); + } + + public function tryAcquire(?timeout:Float):Bool { + return timeout == null ? semaphore.acquire(false) : semaphore.acquire(true, timeout); + } + + public function release():Void { + semaphore.release(); + } +} diff --git a/std/python/_std/sys/thread/Thread.hx b/std/python/_std/sys/thread/Thread.hx index ea728a3a..547e6eb2 100644 --- a/std/python/_std/sys/thread/Thread.hx +++ b/std/python/_std/sys/thread/Thread.hx @@ -22,6 +22,9 @@ package sys.thread; +import python.lib.threading.Thread as NativeThread; +import python.lib.Threading; + import haxe.ds.ObjectMap; private typedef ThreadImpl = HxThread; @@ -33,8 +36,8 @@ abstract Thread(ThreadImpl) from ThreadImpl { return HxThread.current(); } - public static inline function create(callb:Void->Void):Thread { - return HxThread.create(callb, false); + public static inline function create(job:Void->Void):Thread { + return HxThread.create(job, false); } public static inline function runWithEventLoop(job:()->Void):Void { @@ -78,7 +81,7 @@ private class HxThread { static function __init__() { threads = new ObjectMap(); threadsMutex = new Mutex(); - mainThread = new HxThread(PyThreadingAPI.current_thread()); + mainThread = new HxThread(Threading.current_thread()); mainThread.events = new EventLoop(); } @@ -92,8 +95,8 @@ private class HxThread { public static function current():HxThread { threadsMutex.acquire(); - var ct = PyThreadingAPI.current_thread(); - if (ct == PyThreadingAPI.main_thread()) { + var ct = Threading.current_thread(); + if (ct == Threading.main_thread()) { threadsMutex.release(); return mainThread; } @@ -121,7 +124,7 @@ private class HxThread { } dropThread(nt); } - nt = new NativeThread(null, wrappedCallB); + nt = new NativeThread({target:wrappedCallB}); t = new HxThread(nt); if(withEventLoop) t.events = new EventLoop(); @@ -159,17 +162,3 @@ private class HxThread { return current().messages.pop(block); } } - -@:pythonImport("threading", "Thread") -@:native("Thread") -private extern class NativeThread { - function new(group:Dynamic, target:Void->Void); - function start():Void; -} - -@:pythonImport("threading") -@:native("threading") -private extern class PyThreadingAPI { - static function current_thread():NativeThread; - static function main_thread():NativeThread; -} \ No newline at end of file diff --git a/std/python/lib/Os.hx b/std/python/lib/Os.hx index 61de669c..bf8a9839 100644 --- a/std/python/lib/Os.hx +++ b/std/python/lib/Os.hx @@ -57,6 +57,12 @@ extern class Os { static function putenv(name:String, value:String):Void; + /** Removes the value for the environment variable `name`. + + When targeting python versions prior to 3.9, this function may not exist on some platforms. + **/ + static function unsetenv(name:String):Void; + static function chdir(path:String):Void; static function unlink(path:String):Void; diff --git a/std/python/lib/Subprocess.hx b/std/python/lib/Subprocess.hx index 8384217a..807921a2 100644 --- a/std/python/lib/Subprocess.hx +++ b/std/python/lib/Subprocess.hx @@ -30,6 +30,14 @@ extern class StartupInfo { var wShowWindow:Int; } +extern class CompletedProcess { + var args:EitherType>; + var returncode:Int; + var stdout:Null>; + var stderr:Null>; + function check_returncode():Void; +} + @:pythonImport("subprocess") extern class Subprocess { static function STARTUPINFO():StartupInfo; @@ -49,4 +57,5 @@ extern class Subprocess { static var STDOUT:Int; static function call(args:EitherType>, ?kwArgs:python.KwArgs):Int; + static function run(args:EitherType>, ?kwArgs:python.KwArgs):CompletedProcess; } diff --git a/std/python/lib/socket/Socket.hx b/std/python/lib/socket/Socket.hx index 2a5087a8..3050c067 100644 --- a/std/python/lib/socket/Socket.hx +++ b/std/python/lib/socket/Socket.hx @@ -80,6 +80,11 @@ extern class Socket { **/ function getsockname():python.lib.socket.Address; + /** + Return the timeout for the socket or null if no timeout has been set. + **/ + function gettimeout():Null; + /** Gives a timeout after which blocking socket operations (such as reading and writing) will abort and throw an exception. **/ @@ -90,14 +95,26 @@ extern class Socket { **/ function waitForRead():Void; + /** + Return the current blocking mode of the socket. + **/ + @:require(python_version >= 3.7) + function getblocking():Bool; + /** Change the blocking mode of the socket. A blocking socket is the default behavior. A non-blocking socket will abort blocking operations immediately by throwing a haxe.io.Error.Blocked value. **/ function setblocking(b:Bool):Void; /** + Return the current value of the sockopt as an Int or a Bytes buffer (if buflen is specified). + **/ + function getsockopt(family:Int, option:Int):Int; + /** + Change the value of the given socket option. **/ + @:overload(function(family:Int, option:Int, value:Int):Void {}) function setsockopt(family:Int, option:Int, value:Bool):Void; function fileno():Int; diff --git a/std/python/lib/threading/Condition.hx b/std/python/lib/threading/Condition.hx new file mode 100644 index 00000000..37024163 --- /dev/null +++ b/std/python/lib/threading/Condition.hx @@ -0,0 +1,12 @@ +package python.lib.threading; + +@:pythonImport("threading", "Condition") +extern class Condition { + function new(?lock:haxe.extern.EitherType):Void; + function acquire(?blocking:Bool, ?timeout:Float):Bool; + function release():Void; + function wait(?timeout:Float):Bool; + function wait_for(predicate:()->Bool, ?timeout:Float):Bool; + function notify(n:Int = 1):Void; + function notify_all():Void; +} diff --git a/std/python/lib/threading/Semaphore.hx b/std/python/lib/threading/Semaphore.hx new file mode 100644 index 00000000..92bf137f --- /dev/null +++ b/std/python/lib/threading/Semaphore.hx @@ -0,0 +1,9 @@ +package python.lib.threading; + +@:noDoc +@:pythonImport("threading", "Semaphore") +extern class Semaphore { + function new(value:Int); + function acquire(blocking:Bool = true, ?timeout:Float):Bool; + function release(n:Int = 1):Void; +} \ No newline at end of file diff --git a/std/python/net/SslSocket.hx b/std/python/net/SslSocket.hx index e36b1bb3..e0e7ff00 100644 --- a/std/python/net/SslSocket.hx +++ b/std/python/net/SslSocket.hx @@ -25,12 +25,15 @@ package python.net; import python.lib.Ssl; import python.lib.ssl.Purpose; import python.lib.socket.Socket as PSocket; +import python.lib.Socket in PSocketModule; import sys.net.Host; class SslSocket extends sys.net.Socket { - var hostName:String; + var _timeout:Null = null; + var _blocking:Null = null; + var _fastSend:Null = null; - override function __initSocket():Void { + function wrapSocketWithSslContext(hostName:String):Void { #if (python_version >= 3.4) var context = Ssl.create_default_context(Purpose.SERVER_AUTH); #else @@ -43,16 +46,42 @@ class SslSocket extends sys.net.Socket { context.options |= Ssl.OP_NO_COMPRESSION; #end context.options |= Ssl.OP_NO_TLSv1 #if (python_version >= 3.4) | Ssl.OP_NO_TLSv1_1 #end; // python 3.4 | Ssl.OP_NO_TLSv1_1; - __s = new PSocket(); - __s = context.wrap_socket(__s, false, true, true, this.hostName); + __s = context.wrap_socket(__s, false, true, true, hostName); + if (_timeout != null) { + super.setTimeout(_timeout); + } + + if (_blocking != null) { + super.setBlocking(_blocking); + } + + if (_fastSend != null) { + super.setFastSend(_fastSend); + } + __rebuildIoStreams(); } public override function connect(host:Host, port:Int):Void { - this.hostName = host.host; + wrapSocketWithSslContext(host.host); super.connect(host, port); } public override function bind(host:Host, port:Int):Void { throw new haxe.exceptions.NotImplementedException(); } + + public override function setTimeout(timeout:Float):Void { + _timeout = timeout; + super.setTimeout(_timeout); + } + + public override function setBlocking(b:Bool):Void { + _blocking = b; + super.setBlocking(_blocking); + } + + public override function setFastSend(b:Bool):Void { + _fastSend = b; + super.setFastSend(_fastSend); + } } diff --git a/std/sys/Http.hx b/std/sys/Http.hx index b704dda8..a4fc7f10 100644 --- a/std/sys/Http.hx +++ b/std/sys/Http.hx @@ -33,6 +33,8 @@ class Http extends haxe.http.HttpBase { public var cnxTimeout:Float; public var responseHeaders:Map; + private var responseHeadersSameKey:Map>; + var chunk_size:Null; var chunk_buf:haxe.io.Bytes; var file:{ @@ -104,7 +106,7 @@ class Http extends haxe.http.HttpBase { sock = new java.net.SslSocket(); #elseif python sock = new python.net.SslSocket(); - #elseif (!no_ssl && (hxssl || hl || cpp || (neko && !(macro || interp) || eval))) + #elseif (!no_ssl && (hxssl || hl || cpp || (neko && !(macro || interp) || eval) || (lua && !lua_vanilla))) sock = new sys.ssl.Socket(); #elseif (neko || cpp) throw "Https is only supported with -lib hxssl"; @@ -252,6 +254,23 @@ class Http extends haxe.http.HttpBase { } } + /** + Returns an array of values for a single response header or returns + null if no such header exists. + This method can be useful when you need to get a multiple headers with + the same name (e.g. `Set-Cookie`), that are unreachable via the + `responseHeaders` variable. + **/ + public function getResponseHeaderValues(key:String):Null> { + var array = responseHeadersSameKey.get(key); + if (array == null) { + var singleValue = responseHeaders.get(key); + return (singleValue == null) ? null : [ singleValue ]; + } else { + return array; + } + } + function writeBody(body:Null, fileInput:Null, fileSize:Int, boundary:Null, sock:Socket) { if (body != null) { var bytes = body.getBytes(); @@ -284,9 +303,13 @@ class Http extends haxe.http.HttpBase { var s = haxe.io.Bytes.alloc(4); sock.setTimeout(cnxTimeout); while (true) { - var p = sock.input.readBytes(s, 0, k); - while (p != k) - p += sock.input.readBytes(s, p, k - p); + var p = 0; + while (p != k) { + try { + p += sock.input.readBytes(s, p, k - p); + } + catch (e:haxe.io.Eof) { } + } b.addBytes(s, 0, k); switch (k) { case 1: @@ -362,6 +385,22 @@ class Http extends haxe.http.HttpBase { var hname = a.shift(); var hval = if (a.length == 1) a[0] else a.join(": "); hval = StringTools.ltrim(StringTools.rtrim(hval)); + + { + var previousValue = responseHeaders.get(hname); + if (previousValue != null) { + if (responseHeadersSameKey == null) { + responseHeadersSameKey = new haxe.ds.Map>(); + } + var array = responseHeadersSameKey.get(hname); + if (array == null) { + array = new Array(); + array.push(previousValue); + responseHeadersSameKey.set(hname, array); + } + array.push(hval); + } + } responseHeaders.set(hname, hval); switch (hname.toLowerCase()) { case "content-length": diff --git a/std/sys/thread/Condition.hx b/std/sys/thread/Condition.hx new file mode 100644 index 00000000..4bad9df1 --- /dev/null +++ b/std/sys/thread/Condition.hx @@ -0,0 +1,58 @@ +package sys.thread; + +#if (!target.threaded) +#error "This class is not available on this target" +#end + +/** + Creates a new condition variable. + Conditions variables can be used to block one or more threads at the same time, + until another thread modifies a shared variable (the condition) + and signals the condition variable. +**/ +@:coreApi extern class Condition { + /** + Create a new condition variable. + A thread that waits on a newly created condition variable will block. + **/ + function new():Void; + + /** + Acquires the internal mutex. + **/ + function acquire():Void; + + /** + Tries to acquire the internal mutex. + @see `Mutex.tryAcquire` + **/ + function tryAcquire():Bool; + + /*** + Releases the internal mutex. + **/ + function release():Void; + + /** + Atomically releases the mutex and blocks until the condition variable pointed is signaled by a call to + `signal` or to `broadcast`. When the calling thread becomes unblocked it + acquires the internal mutex. + The internal mutex should be locked before this function is called. + **/ + function wait():Void; + + /** + Unblocks one of the threads that are blocked on the + condition variable at the time of the call. If no threads are blocked + on the condition variable at the time of the call, the function does nothing. + **/ + function signal():Void; + + /** + Unblocks all of the threads that are blocked on the + condition variable at the time of the call. If no threads are blocked + on the condition variable at the time of the call, the function does + nothing. + **/ + function broadcast():Void; +} diff --git a/std/sys/thread/EventLoop.hx b/std/sys/thread/EventLoop.hx index 577050e8..4806c778 100644 --- a/std/sys/thread/EventLoop.hx +++ b/std/sys/thread/EventLoop.hx @@ -28,8 +28,13 @@ class EventLoop { final waitLock = new Lock(); var promisedEventsCount = 0; var regularEvents:Null; + var isMainThread:Bool; + static var CREATED : Bool; - public function new():Void {} + public function new():Void { + isMainThread = !CREATED; + CREATED = true; + } /** Schedule event for execution every `intervalMs` milliseconds in current loop. @@ -93,13 +98,14 @@ class EventLoop { case null: case e: e.next = event.next; } + event.next = event.previous = null; mutex.release(); } /** Notify this loop about an upcoming event. - This makes the thread to stay alive and wait for as many events as many times - `.promise()` was called. These events should be added via `.runPromised()` + This makes the thread stay alive and wait for as many events as the number of + times `.promise()` was called. These events should be added via `.runPromised()`. **/ public function promise():Void { mutex.acquire(); @@ -164,8 +170,8 @@ class EventLoop { /** Execute all pending events. - Wait and execute as many events as many times `promiseEvent()` was called. - Runs until all repeating events are cancelled and no more events is expected. + Wait and execute as many events as the number of times `promise()` was called. + Runs until all repeating events are cancelled and no more events are expected. Depending on a target platform this method may be non-reentrant. It must not be called from event callbacks. @@ -221,7 +227,7 @@ class EventLoop { // Run regular events for(i in 0...eventsToRunIdx) { - if(!regularsToRun[i].cancelled) + if(!regularsToRun[i].cancelled) regularsToRun[i].run(); regularsToRun[i] = null; } @@ -249,6 +255,16 @@ class EventLoop { oneTimersToRun[i] = null; } + // run main events + if( isMainThread ) { + var next = @:privateAccess haxe.MainLoop.tick(); + if( haxe.MainLoop.hasEvents() ) { + eventsToRunIdx++; + if( nextEventAt > next ) + nextEventAt = next; + } + } + // Some events were executed. They could add new events to run. if(eventsToRunIdx > 0) { nextEventAt = -2; @@ -272,4 +288,4 @@ private class RegularEvent { this.nextRunTime = nextRunTime; this.interval = interval; } -} \ No newline at end of file +} diff --git a/std/sys/thread/Semaphore.hx b/std/sys/thread/Semaphore.hx new file mode 100644 index 00000000..6d0f474d --- /dev/null +++ b/std/sys/thread/Semaphore.hx @@ -0,0 +1,33 @@ +package sys.thread; + +#if (!target.threaded) +#error "This class is not available on this target" +#end +@:coreApi extern class Semaphore { + /** + Creates a new semaphore with an initial value. + **/ + public function new(value:Int):Void; + + /** + Locks the semaphore. + If the value of the semaphore is zero, then the thread will block until it is able to lock the semaphore. + If the value is non-zero, it is decreased by one. + **/ + public function acquire():Void; + + /** + Try to lock the semaphore. + If the value of the semaphore is zero, `false` is returned, else the value is increased. + + If `timeout` is specified, this function will block until the thread is able to acquire the semaphore, or the timout expires. + `timeout` is in seconds. + **/ + public function tryAcquire(?timeout:Float):Bool; + + /** + Release the semaphore. + The value of the semaphore is increased by one. + **/ + public function release():Void; +} diff --git a/tests/PHP.ini b/tests/PHP.ini new file mode 100644 index 00000000..fa830648 --- /dev/null +++ b/tests/PHP.ini @@ -0,0 +1,5 @@ +; PHP.ini to use on Windows +extension=php_sqlite3.dll +extension=php_pdo_sqlite.dll +extension=php_sockets.dll +extension=php_mbstring.dll diff --git a/tests/README.md b/tests/README.md index 40cbc8a2..f55ccff6 100644 --- a/tests/README.md +++ b/tests/README.md @@ -4,14 +4,13 @@ We have a number of test suites, which are placed in their own folders in this d "RunCi.hx" is the script used by our CIs to run all the test suites. -### Local testing +## Local testing It is possible to run it in local machines too: 1. Change to this directory. - 2. Compile the script: `haxe RunCi.hxml`. - 3. Define the test target by `export TEST=$TARGET` (or `set "TEST=$TARGET"` on Windows), where `$TARGET` should be a comma-seperated list of targets, e.g. `neko,macro`. Possible targets are `macro`, `neko`, `js`, `lua`, `php`, `cpp`, `flash9`, `java`, `cs`, `python`, and `third-party`. However, `flash9` and `third-party` are not likely to work on local machines (TODO). - 4. Run it: `neko RunCi.n`. + 2. Define the test target by `export TEST=$TARGET` (or `set "TEST=$TARGET"` on Windows), where `$TARGET` should be a comma-seperated list of targets, e.g. `neko,macro`. Possible targets are `macro`, `neko`, `js`, `lua`, `php`, `cpp`, `cppia`, `flash`, `java`, `jvm`, `cs`, `python`, and `hl`. + 3. Run the script: `haxe RunCi.hxml`. Note that the script will try to look for test dependencies and install them if they are not found. Look at the `getXXXDependencies` functions for the details. diff --git a/tests/RunCi.hx b/tests/RunCi.hx index 7c61bb7f..309b514b 100644 --- a/tests/RunCi.hx +++ b/tests/RunCi.hx @@ -27,6 +27,10 @@ class RunCi { changeDirectory(cwd); } + final downloadPath = getDownloadPath(); + if (!sys.FileSystem.exists(downloadPath)) + sys.FileSystem.createDirectory(downloadPath); + for (test in tests) { switch (systemName) { case "Windows": @@ -40,10 +44,9 @@ class RunCi { var echoServer = new sys.io.Process('nekotools', ['server', '-d', 'echoServer/www/', '-p', '20200']); infoMsg('test $test'); - var success = true; try { changeDirectory(unitDir); - haxelibInstallGit("haxe-utest", "utest", "master"); + haxelibInstallGit("haxe-utest", "utest", "master", "--always"); var args = switch (ci) { case null: @@ -75,32 +78,24 @@ class RunCi { runci.targets.Jvm.run(args); case Cs: runci.targets.Cs.run(args); - case Flash9: + case Flash: runci.targets.Flash.run(args); case Hl: runci.targets.Hl.run(args); case t: throw new Exception("unknown target: " + t); } - } catch(f:Failure) { - success = false; - } - - if (success) { - successMsg('test ${test} succeeded'); - } else { + } catch(f:CommandFailure) { failMsg('test ${test} failed'); - break; + Sys.exit(f.exitCode); } + successMsg('test ${test} succeeded'); + echoServer.kill(); echoServer.close(); } - if (success) { - deploy(); - } else { - Sys.exit(1); - } + deploy(); } } diff --git a/tests/display/src/Diagnostic.hx b/tests/display/src/Diagnostic.hx index aff232ea..697e7039 100644 --- a/tests/display/src/Diagnostic.hx +++ b/tests/display/src/Diagnostic.hx @@ -1,17 +1,58 @@ // from vshaxe +import haxe.display.Position.Location; import haxe.display.Position.Range; +import haxe.display.JsonModuleTypes; enum abstract UnresolvedIdentifierSuggestion(Int) { var UISImport; var UISTypo; } +enum abstract MissingFieldCauseKind(String) { + final AbstractParent:MissingFieldCauseKind<{parent:JsonTypePathWithParams}>; + final ImplementedInterface:MissingFieldCauseKind<{parent:JsonTypePathWithParams}>; + final PropertyAccessor:MissingFieldCauseKind<{property:JsonClassField, isGetter:Bool}>; + final FieldAccess:MissingFieldCauseKind<{}>; + final FinalFields:MissingFieldCauseKind<{fields:Array}>; +} + +typedef MissingFieldCause = { + var kind:MissingFieldCauseKind; + var args:T; +} + +typedef MissingField = { + var field:JsonClassField; + var type:JsonType; + + /** + When implementing multiple interfaces, there can be field duplicates among them. This flag is only + true for the first such occurrence of a field, so that the "Implement all" code action doesn't end + up implementing the same field multiple times. + **/ + var unique:Bool; +} + +typedef MissingFieldDiagnostic = { + var fields:Array; + var cause:MissingFieldCause; +} + +typedef MissingFieldDiagnostics = { + var moduleType:JsonModuleType; + var moduleFile:String; + var entries:Array; +} + enum abstract DiagnosticKind(Int) from Int to Int { - var DKUnusedImport:DiagnosticKind; - var DKUnresolvedIdentifier:DiagnosticKind>; - var DKCompilerError:DiagnosticKind; - var DKRemovableCode:DiagnosticKind<{description:String, range:Range}>; - var DKParserError:DiagnosticKind; + final DKUnusedImport:DiagnosticKind; + final DKUnresolvedIdentifier:DiagnosticKind>; + final DKCompilerError:DiagnosticKind; + final DKRemovableCode:DiagnosticKind<{description:String, range:Range}>; + final DKParserError:DiagnosticKind; + final DeprecationWarning:DiagnosticKind; + final InactiveBlock:DiagnosticKind; + final MissingFields:DiagnosticKind; } enum abstract DiagnosticSeverity(Int) { @@ -26,4 +67,11 @@ typedef Diagnostic = { var range:Range; var severity:DiagnosticSeverity; var args:T; + var relatedInformation:Array; +} + +typedef DiagnosticRelatedInformation = { + var location:Location; + var message:String; + var depth:Int; } diff --git a/tests/display/src/cases/Abstract.hx b/tests/display/src/cases/Abstract.hx index b6ac3329..ec121a4e 100644 --- a/tests/display/src/cases/Abstract.hx +++ b/tests/display/src/cases/Abstract.hx @@ -44,4 +44,39 @@ class Abstract extends DisplayTestCase { eq(false, hasField(fields, "instanceField", "() -> Void")); eq(true, hasField(fields, "staticField", "() -> Void")); } + + /** + abstract MyAbstract(String) { + public function new() this = "foo"; + + public function instanceField():Void { + {-1-} + } + static public function staticField():Void { + {-2-} + } + public function instanceField2():Void { + ab{-3-}stract; + } + } + abstract AbGeneric(T) { + public function new(a:T) this = a; + public function foo() { + return ab{-4-}stract.bar(); + } + public function bar() { + return th{-5-}is; + } + } + **/ + function test3():Void { + final fields = toplevel(pos(1)); + eq(true, hasToplevel(fields, "literal", "abstract")); + final fields = toplevel(pos(2)); + eq(false, hasToplevel(fields, "literal", "abstract")); + // TODO: improve display hints + eq("cases.MyAbstract", type(pos(3))); + eq("cases.AbGeneric", type(pos(4))); + eq("cases.AbGeneric.T", type(pos(5))); + } } diff --git a/tests/display/src/cases/DocumentSymbols.hx b/tests/display/src/cases/DocumentSymbols.hx index 09a5f8c3..92e9d2c3 100644 --- a/tests/display/src/cases/DocumentSymbols.hx +++ b/tests/display/src/cases/DocumentSymbols.hx @@ -120,9 +120,14 @@ class DocumentSymbols extends DisplayTestCase { function main() {} **/ function testModuleLevelFields() { - checkDocumentSymbols([ - {name: "main", kind: Method, containerName: null}, - ], ctx.documentSymbols()); + checkDocumentSymbols([{name: "main", kind: Method, containerName: null},], ctx.documentSymbols()); + } + + /*// Test + function main() {} + */ + function testLeadingLineComment() { + checkDocumentSymbols([{name: "main", kind: Method, containerName: null},], ctx.documentSymbols()); } function checkDocumentSymbols(expected:Array, actual:Array, ?pos:haxe.PosInfos) { diff --git a/tests/display/src/cases/Issue10004.hx b/tests/display/src/cases/Issue10004.hx new file mode 100644 index 00000000..39eb3122 --- /dev/null +++ b/tests/display/src/cases/Issue10004.hx @@ -0,0 +1,49 @@ +package cases; + +class Issue10004 extends DisplayTestCase { + /** + using Issue10004.Foo; + + class Main { + static function main() { + 0.{-1-} + } + } + + class Foo { + public static overload extern inline function foo():Void {} + + public static overload extern inline function foo(i:Int):Void {} + } + **/ + function testGama() { + var fields = fields(pos(1)); + eq(true, hasField(fields, "foo", "() -> Void")); + } + + /** + using Issue10004.Tools; + + class Tools { + public static extern inline overload function getOrZero(map:Map, key:K):Int { + var value = map.get(key); + return if (value != null) value else 0; + } + + public static extern inline overload function getOrZero(map:Map, key:K):Float { + var value = map.get(key); + return if (value != null) value else 0.0; + } + + } + + function main() { + var m = ["a" => 1.1, "b" => 2.3]; + m.{-1-} + } + **/ + function testNotGama() { + var fields = fields(pos(1)); + eq(true, hasField(fields, "getOrZero", "(key : String) -> Float")); + } +} diff --git a/tests/display/src/cases/Issue10194.hx b/tests/display/src/cases/Issue10194.hx new file mode 100644 index 00000000..361eafdb --- /dev/null +++ b/tests/display/src/cases/Issue10194.hx @@ -0,0 +1,23 @@ +package cases; + +class Issue10194 extends DisplayTestCase { + /** + function f(a:Int, b:Int) {} + + function main() { + var v = 10; // reported as "unused variable" + f({-1-}""{-2-}, v); + } + **/ + function test() { + arrayEq([ + { + kind: DKCompilerError, + severity: Error, + range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], + args: "String should be Int\nFor function argument 'a'" + } + ], diagnostics()); + } +} diff --git a/tests/display/src/cases/Issue10412.hx b/tests/display/src/cases/Issue10412.hx new file mode 100644 index 00000000..de971dab --- /dev/null +++ b/tests/display/src/cases/Issue10412.hx @@ -0,0 +1,78 @@ +package cases; + +class Issue10412 extends DisplayTestCase { + /** + class Main { + static function main() { + new Game(); + } + } + + class Scene { + public var scale:Float; + } + + class Game extends Scene { + + public function new():Void {} + + function itWorks():Void { + // There is completion: + // Res.|.bar; + Res.foo.bar; + } + + function itDoesNot():Void { + // There is not: + // Res.|.bar; + Res.{-1-}.bar; + + // because of `final scale` + // changing `final scale` to other name fixes it + var strangeBust = 5 / 10; + final scale = 5 / 10; + // if you comment out `strangeBust` it will also work, + // but not if you change `final scale` to `var scale` + } + } + + class Res { + public static var foo = {bar: "hello"}; + } + **/ + function testOriginal() { + var fields = fields(pos(1)); + eq(true, hasField(fields, "foo", "{ bar : String }")); + } + + /** + class Base { + final x:String; + } + + class Child extends Base { + final x:String; + function test() { + "".{-1-}; + } + } + **/ + function testActualIssue() { + var fields = fields(pos(1)); + eq(true, hasField(fields, "length", "Int")); + } + + /** + class Test { + var a:Int; + function test() { + this.{-1-}.resume; + + final x:String; + } + **/ + function testFinalResume() { + var fields = fields(pos(1)); + eq(true, hasField(fields, "x", "String")); + } +} diff --git a/tests/display/src/cases/Issue10429.hx b/tests/display/src/cases/Issue10429.hx new file mode 100644 index 00000000..8bcaa1ed --- /dev/null +++ b/tests/display/src/cases/Issue10429.hx @@ -0,0 +1,29 @@ +package cases; + +class Issue10429 extends DisplayTestCase { + /** + import haxe.macro.Expr; + + typedef ArgType = { + var f:String; + } + + class Main { + public static function main() { + f{-1-}oo({-2-}); + {-3-} + } + + static macro function foo(s:String, e:ExprOf, o:ArgType) { + return macro {}; + } + } + **/ + function test() { + var expectedType = "(s : String, e : String, o : cases.ArgType) -> haxe.macro.Expr"; + var fields = toplevel(pos(1)); + eq(true, hasToplevel(fields, "static", "foo", expectedType)); + eq(expectedType, type(pos(1))); + sigEq(0, [["s:String", "e:String", "o:cases.ArgType"]], signature(pos(2))); + } +} diff --git a/tests/display/src/cases/Issue10633.hx b/tests/display/src/cases/Issue10633.hx new file mode 100644 index 00000000..4ad9f32b --- /dev/null +++ b/tests/display/src/cases/Issue10633.hx @@ -0,0 +1,31 @@ +package cases; + +class Issue10633 extends DisplayTestCase { + /** + enum Value { + Multiple(a:Array); + } + + class Main { + var values(get, set):Array; + + var whatever:Value; + + function get_values() { + return switch (whatever) { + case Multiple(values): values; + default: []; + } + } + + function set_values(a:Array) { + return []; + } + + static function main() {} + } + **/ + function test() { + arrayEq([], diagnostics()); + } +} diff --git a/tests/display/src/cases/Issue10634.hx b/tests/display/src/cases/Issue10634.hx new file mode 100644 index 00000000..257bb52c --- /dev/null +++ b/tests/display/src/cases/Issue10634.hx @@ -0,0 +1,14 @@ +package cases; + +class Issue10634 extends DisplayTestCase { + /** + function test(?x:String, ?e:haxe.macro.Expr.ExprDef) {} + + function main() { + test(EBreak); + } + **/ + // function test() { + // arrayEq([], diagnostics()); + // } +} diff --git a/tests/display/src/cases/Issue10691.hx b/tests/display/src/cases/Issue10691.hx new file mode 100644 index 00000000..4ec58ab2 --- /dev/null +++ b/tests/display/src/cases/Issue10691.hx @@ -0,0 +1,63 @@ +package cases; + +class Issue10691 extends DisplayTestCase { + /** + class Main { + static public function main() { + function hello() {} + function {-1-} + } + } + **/ + function test1() { + eq(true, noCompletionPoint(toplevel.bind((pos(1))))); + } + + /** + class Main { + static public function main() { + function hello() {} + function a{-1-} + } + } + **/ + function test2() { + eq(true, noCompletionPoint(toplevel.bind((pos(1))))); + } + + /** + class Main { + static public function main() { + function hello() {} + function a{-1-}b + } + } + **/ + function test3() { + eq(true, noCompletionPoint(toplevel.bind((pos(1))))); + } + + /** + class Main { + static public function main() { + function hello() {} + function a{-1-}b() + } + } + **/ + function test4() { + eq(true, noCompletionPoint(toplevel.bind((pos(1))))); + } + + /** + class Main { + static public function main() { + function hello() {} + function ab{-1-}() + } + } + **/ + function test5() { + eq(true, noCompletionPoint(toplevel.bind((pos(1))))); + } +} diff --git a/tests/display/src/cases/Issue10704.hx b/tests/display/src/cases/Issue10704.hx new file mode 100644 index 00000000..fc55958a --- /dev/null +++ b/tests/display/src/cases/Issue10704.hx @@ -0,0 +1,17 @@ +package cases; + +class Issue10704 extends DisplayTestCase { + /** + import misc.issue10704.Statics.*; + class Main { + static function main() { + foo{-1-} + } + } + **/ + function test() { + eq(true, hasToplevel(toplevel(pos(1)), "static", "fooPublic")); + eq(false, hasToplevel(toplevel(pos(1)), "static", "fooNoCompletion")); + eq(false, hasToplevel(toplevel(pos(1)), "static", "fooPrivate")); + } +} diff --git a/tests/display/src/cases/Issue10713.hx b/tests/display/src/cases/Issue10713.hx new file mode 100644 index 00000000..06d77e18 --- /dev/null +++ b/tests/display/src/cases/Issue10713.hx @@ -0,0 +1,22 @@ +package cases; + +using Lambda; + +class Issue10713 extends DisplayTestCase { + /** + class Main { + static function main() { + loadEverything(() -> { + {-1-}foo{-2-} = 1; + }); + } + + static function loadEverything(cb:() -> Void, ?what):Void {} + } + **/ + function test() { + var d = diagnostics(); + var range = diagnosticsRange(pos(1), pos(2)); + utest.Assert.isTrue(d.exists(d -> d.kind == MissingFields && utest.Assert.same(d.range, range))); + } +} diff --git a/tests/display/src/cases/Issue5172.hx.disabled b/tests/display/src/cases/Issue5172.hx similarity index 100% rename from tests/display/src/cases/Issue5172.hx.disabled rename to tests/display/src/cases/Issue5172.hx diff --git a/tests/display/src/cases/Issue5306.hx b/tests/display/src/cases/Issue5306.hx index e2c972a0..941379ac 100644 --- a/tests/display/src/cases/Issue5306.hx +++ b/tests/display/src/cases/Issue5306.hx @@ -7,7 +7,7 @@ class Issue5306 extends DisplayTestCase { class Main { static function main() { var ib:Array; - ib[0] = 0; ib[1] = 1; {-7-}ib[2]{-8-} + ib[0] = 0; ib[1] = 1; ib[2] {-5-}trace{-6-}("test"); } } @@ -20,23 +20,19 @@ class Issue5306 extends DisplayTestCase { // severity: Warning, // args: [] // }, - { - kind: DKCompilerError, - range: diagnosticsRange(pos(3), pos(4)), - severity: Error, - args: "Type not found : InvalidType" - }, { kind: DKParserError, range: diagnosticsRange(pos(5), pos(6)), severity: Error, + relatedInformation: [], args: "Missing ;" }, { kind: DKCompilerError, - range: diagnosticsRange(pos(7), pos(8)), - severity: Warning, - args: "This code has no effect" + range: diagnosticsRange(pos(3), pos(4)), + severity: Error, + relatedInformation: [], + args: "Type not found : InvalidType" } ]; arrayEq(expected, diagnostics()); diff --git a/tests/display/src/cases/Issue7328.hx b/tests/display/src/cases/Issue7328.hx index 276ecc60..64d30add 100644 --- a/tests/display/src/cases/Issue7328.hx +++ b/tests/display/src/cases/Issue7328.hx @@ -7,13 +7,13 @@ class Issue7328 extends DisplayTestCase { class Main { public static function main() { switch (null:ExprDef) { - case EConst(CInt({-1-}v)): + case EConst(CIdent({-1-}s)): case _: } } } **/ function test() { - sigEq(0, [["v:String"]], signature(pos(1))); + sigEq(0, [["s:String"]], signature(pos(1))); } } diff --git a/tests/display/src/cases/Issue7777.hx b/tests/display/src/cases/Issue7777.hx index 065cdd64..265b388c 100644 --- a/tests/display/src/cases/Issue7777.hx +++ b/tests/display/src/cases/Issue7777.hx @@ -19,6 +19,7 @@ class Issue7777 extends DisplayTestCase { kind: DKUnusedImport, severity: Warning, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: [] } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7932.hx b/tests/display/src/cases/Issue7932.hx index a98fbb50..3627184c 100644 --- a/tests/display/src/cases/Issue7932.hx +++ b/tests/display/src/cases/Issue7932.hx @@ -12,6 +12,7 @@ class Issue7932 extends DisplayTestCase { kind: DKParserError, range: diagnosticsRange(pos(1), pos(2)), severity: Error, + relatedInformation: [], args: "Expected type parameter" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7935.hx b/tests/display/src/cases/Issue7935.hx index 1b033b90..1b978fa6 100644 --- a/tests/display/src/cases/Issue7935.hx +++ b/tests/display/src/cases/Issue7935.hx @@ -40,6 +40,7 @@ class Issue7935 extends DisplayTestCase { kind: DKParserError, range: diagnosticsRange(pos(1), pos(2)), severity: Error, + relatedInformation: [], args: message } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7938.hx b/tests/display/src/cases/Issue7938.hx index 5cd6c3a2..439aca62 100644 --- a/tests/display/src/cases/Issue7938.hx +++ b/tests/display/src/cases/Issue7938.hx @@ -12,6 +12,7 @@ class Issue7938 extends DisplayTestCase { kind: DKParserError, range: diagnosticsRange(pos(1), pos(2)), severity: Error, + relatedInformation: [], args: "Unexpected fuction" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7939.hx b/tests/display/src/cases/Issue7939.hx index 9b75195a..46ebeb1c 100644 --- a/tests/display/src/cases/Issue7939.hx +++ b/tests/display/src/cases/Issue7939.hx @@ -10,6 +10,7 @@ class Issue7939 extends DisplayTestCase { kind: DKParserError, range: diagnosticsRange(pos(1), pos(2)), severity: Error, + relatedInformation: [], args: "Unexpected {" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7940.hx b/tests/display/src/cases/Issue7940.hx index b8a6ca42..9c53075e 100644 --- a/tests/display/src/cases/Issue7940.hx +++ b/tests/display/src/cases/Issue7940.hx @@ -14,6 +14,7 @@ class Issue7940 extends DisplayTestCase { kind: DKParserError, range: diagnosticsRange(pos(1), pos(1)), severity: Error, + relatedInformation: [], args: "Unterminated string" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7943.hx b/tests/display/src/cases/Issue7943.hx index 34cfb838..4f355ce5 100644 --- a/tests/display/src/cases/Issue7943.hx +++ b/tests/display/src/cases/Issue7943.hx @@ -14,19 +14,8 @@ class Issue7943 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(3), pos(4)), + relatedInformation: [], args: "Missing ;" - }, - { - kind: DKCompilerError, - severity: Warning, - range: diagnosticsRange(pos(3), pos(4)), - args: "This code has no effect" - }, - { - kind: DKCompilerError, - severity: Warning, - range: diagnosticsRange(pos(1), pos(2)), - args: "This code has no effect" } ], diagnostics()); } diff --git a/tests/display/src/cases/Issue7944.hx b/tests/display/src/cases/Issue7944.hx index 91461258..7a651586 100644 --- a/tests/display/src/cases/Issue7944.hx +++ b/tests/display/src/cases/Issue7944.hx @@ -14,6 +14,7 @@ class Issue7944 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Unexpected fun" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7945.hx b/tests/display/src/cases/Issue7945.hx index 6ee2c3c3..bb3b853d 100644 --- a/tests/display/src/cases/Issue7945.hx +++ b/tests/display/src/cases/Issue7945.hx @@ -10,6 +10,7 @@ class Issue7945 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Expected { or to or from" } ], diagnostics()); @@ -24,6 +25,7 @@ class Issue7945 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Expected extends or implements or {" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7946.hx b/tests/display/src/cases/Issue7946.hx index 95240e4d..00ae0291 100644 --- a/tests/display/src/cases/Issue7946.hx +++ b/tests/display/src/cases/Issue7946.hx @@ -10,6 +10,7 @@ class Issue7946 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Unexpected open" } ], diagnostics()); @@ -24,6 +25,7 @@ class Issue7946 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Unexpected clas" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7947.hx b/tests/display/src/cases/Issue7947.hx index 93d1eaaa..abd7a681 100644 --- a/tests/display/src/cases/Issue7947.hx +++ b/tests/display/src/cases/Issue7947.hx @@ -22,6 +22,7 @@ class Issue7947 extends DisplayTestCase { kind: DKCompilerError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Void should be Bool" } ], diagnostics()); @@ -48,6 +49,7 @@ class Issue7947 extends DisplayTestCase { kind: DKCompilerError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Bool should be Void" } ], diagnostics()); diff --git a/tests/display/src/cases/Issue7948.hx b/tests/display/src/cases/Issue7948.hx index 9c2bdceb..d9027a52 100644 --- a/tests/display/src/cases/Issue7948.hx +++ b/tests/display/src/cases/Issue7948.hx @@ -14,7 +14,8 @@ class Issue7948 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), - args: "Unexpected class" + relatedInformation: [], + args: 'Unexpected keyword "class"' } ], diagnostics()); } @@ -32,6 +33,7 @@ class Issue7948 extends DisplayTestCase { kind: DKParserError, severity: Error, range: diagnosticsRange(pos(1), pos(2)), + relatedInformation: [], args: "Unexpected }" } ], diagnostics()); diff --git a/tests/display/src/cases/Metadata.hx b/tests/display/src/cases/Metadata.hx index 46dae246..db2014b5 100644 --- a/tests/display/src/cases/Metadata.hx +++ b/tests/display/src/cases/Metadata.hx @@ -109,4 +109,85 @@ class Metadata extends DisplayTestCase { eq("Marks a class or class field as generic so each type parameter combination generates its own type/field.", metadataDoc(pos(2))); eq("Marks a class or class field as generic so each type parameter combination generates its own type/field.", metadataDoc(pos(3))); } + + /** + #if !macro + @:build(cases.Metadata.Main.build()) + #end + class Main { + #if !macro + @{-1-} + static var dummy = 123; + + static function main() {} + #else + static function build() { + haxe.macro.Context.getBuildFields(); + return null; + } + #end + } + **/ + function test9853() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } + + /** + @{-1-} + class Main { + static function main() {} + } + @{-2-} + class Test {} + + **/ + function test7864() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + eq(true, hasPath(fields(pos(2)), "@:generic")); + } + + /** + function main() { + var @{-1-} + } + **/ + function test9639_1() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } + + /** + function main() { + var @{-1-} local + } + **/ + function test9639_2() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } + + /** + function main() { + var @{-1-} local : Type + } + **/ + function test9639_3() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } + + /** + function main() { + var @{-1-} local = + } + **/ + function test9639_4() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } + + /** + function main() { + var @{-1-} local = 10 + } + **/ + function test9639_5() { + eq(true, hasPath(fields(pos(1)), "@:generic")); + } } diff --git a/tests/display/src/cases/Signature.hx b/tests/display/src/cases/Signature.hx index a6d77ac2..d8914e51 100644 --- a/tests/display/src/cases/Signature.hx +++ b/tests/display/src/cases/Signature.hx @@ -232,4 +232,54 @@ class Signature extends DisplayTestCase { sigEq(4, sig, signature(pos(14))); sigEq(4, sig, signature(pos(15))); } + + /** + class NotMain { + overload public function new(s:String) {} + overload function new(i:Int) {} + } + + class Main { + static function main() { + new NotMain({-1-} + } + } + **/ + function testCtorVisibility() { + sigEq(0, [["s:String"]], signature(pos(1))); + } + + /** + class NotMain { + overload public function new() {} + + overload public function f(s:String) {} + overload function f(i:Int) {} + } + + class Main { + static function main() { + new NotMain().f({-1-}) + } + } + **/ + function testMemberVisibility() { + sigEq(0, [["s:String"]], signature(pos(1))); + } + + /** + class NotMain { + overload static public function f(s:String) {} + overload static function f(i:Int) {} + } + + class Main { + static function main() { + NotMain.f({-1-}) + } + } + **/ + function testStaticVisibility() { + sigEq(0, [["s:String"]], signature(pos(1))); + } } diff --git a/tests/display/src/cases/StructureCompletion.hx b/tests/display/src/cases/StructureCompletion.hx index e06f48e5..d9660cdd 100644 --- a/tests/display/src/cases/StructureCompletion.hx +++ b/tests/display/src/cases/StructureCompletion.hx @@ -32,7 +32,7 @@ class StructureCompletion extends DisplayTestCase { test({{-1-} } - static function test(o:{a:Float, b:String}) { } + public static function test(o:{a:Float, b:String}) { } } **/ function testStructureCompletion4() { @@ -46,7 +46,7 @@ class StructureCompletion extends DisplayTestCase { test(0, {{-1-} } - static function test(x, o:{a:Float, b:String}) { } + public static function test(x, o:{a:Float, b:String}) { } } **/ function testStructureCompletion5() { @@ -168,12 +168,12 @@ class StructureCompletion extends DisplayTestCase { var field2:String; } class Main { - static function test1():Foo return { f{-1-}ie{-2-} - static function test2():Foo return { f{-3-}ie{-4-}: - static function test3():Foo return { f{-5-}ie{-6-} } - static function test4():Foo return { f{-7-}ie{-8-} : } - static function test5():Foo return { f{-9-}ie{-10-} : null } - static function test6():Foo return { f{-11-}ie{-12-} : null + public static function test1():Foo return { f{-1-}ie{-2-} + public static function test2():Foo return { f{-3-}ie{-4-}: + public static function test3():Foo return { f{-5-}ie{-6-} } + public static function test4():Foo return { f{-7-}ie{-8-} : } + public static function test5():Foo return { f{-9-}ie{-10-} : null } + public static function test6():Foo return { f{-11-}ie{-12-} : null **/ function testStructureVsToplevel9() { for (i in 1...13) { diff --git a/tests/display/src/misc/issue10704/Statics.hx b/tests/display/src/misc/issue10704/Statics.hx new file mode 100644 index 00000000..73ec0afe --- /dev/null +++ b/tests/display/src/misc/issue10704/Statics.hx @@ -0,0 +1,10 @@ +package misc.issue10704; + +class Statics { + public static final fooPublic = 0; + + @:noCompletion + public static final fooNoCompletion = 0; + + static final fooPrivate = 0; +} diff --git a/tests/docgen/dox.hxml b/tests/docgen/dox.hxml new file mode 100644 index 00000000..9c054dbe --- /dev/null +++ b/tests/docgen/dox.hxml @@ -0,0 +1,8 @@ +-lib dox +-lib hxtemplo +-lib hxparse +-lib hxargs +-lib markdown +-dce no +-main dox.Dox +-cpp cpp diff --git a/tests/echoServer/www/crossdomain.xml b/tests/echoServer/www/crossdomain.xml index 03ada61e..b14a6a16 100644 --- a/tests/echoServer/www/crossdomain.xml +++ b/tests/echoServer/www/crossdomain.xml @@ -1,7 +1,7 @@ - + - - + + - \ No newline at end of file + diff --git a/tests/misc/compile.hxml b/tests/misc/compile.hxml index 665d17d2..36ba173c 100644 --- a/tests/misc/compile.hxml +++ b/tests/misc/compile.hxml @@ -1,4 +1,4 @@ -p src -#-D MISC_TEST_FILTER=9619 +#-D MISC_TEST_FILTER=4270 -main Main --interp \ No newline at end of file diff --git a/tests/misc/cpp/projects/Issue10184/CheckOptionsTxt.hx b/tests/misc/cpp/projects/Issue10184/CheckOptionsTxt.hx new file mode 100644 index 00000000..34374862 --- /dev/null +++ b/tests/misc/cpp/projects/Issue10184/CheckOptionsTxt.hx @@ -0,0 +1,28 @@ +import sys.FileSystem; + +function deleteDirectory(path:String) { + if (!FileSystem.isDirectory(path)) + return FileSystem.deleteFile(path); + + for (item in FileSystem.readDirectory(path)) + deleteDirectory('$path/$item'); + + FileSystem.deleteDirectory(path); +} + +function main() { + final definePairs = sys.io.File.getContent("out/Options.txt").split("\n"); + + for (definePair in definePairs) + for (index in 0...definePair.length) { + final char = definePair.charAt(index); + if (char == "=") break; + if (char == "-"){ + deleteDirectory("out"); + Sys.stderr().writeString("Generated `Options.txt` contains raw version of define flag: " + definePair + "\n"); + Sys.exit(1); + } + } + deleteDirectory("out"); + Sys.exit(0); +} diff --git a/tests/misc/cpp/projects/Issue10184/options-txt.hxml b/tests/misc/cpp/projects/Issue10184/options-txt.hxml new file mode 100644 index 00000000..65ea4f35 --- /dev/null +++ b/tests/misc/cpp/projects/Issue10184/options-txt.hxml @@ -0,0 +1,5 @@ +# test Options.txt defines +--main CheckOptionsTxt +--cpp out +-D no-compilation +--cmd haxe --run CheckOptionsTxt diff --git a/tests/misc/cpp/run.hxml b/tests/misc/cpp/run.hxml new file mode 100644 index 00000000..ddd91b1b --- /dev/null +++ b/tests/misc/cpp/run.hxml @@ -0,0 +1,2 @@ +-cp ../src +--run Main diff --git a/tests/misc/eventLoop/Main.hx b/tests/misc/eventLoop/Main.hx index bc184e53..5e6b3294 100644 --- a/tests/misc/eventLoop/Main.hx +++ b/tests/misc/eventLoop/Main.hx @@ -48,10 +48,7 @@ class Main { trace(String.fromCharCode("A".code + count++)); if( count == 5 ) event.stop(); }); - #if sys - Sys.sleep(3); - #end }); } -} \ No newline at end of file +} diff --git a/tests/misc/eventLoop/build-cpp.hxml b/tests/misc/eventLoop/build-cpp.hxml new file mode 100644 index 00000000..744567b2 --- /dev/null +++ b/tests/misc/eventLoop/build-cpp.hxml @@ -0,0 +1,3 @@ +--main Main +--dce full +-cpp cpp diff --git a/tests/misc/eventLoop/build-cpp.hxml.stdout.disabled b/tests/misc/eventLoop/build-cpp.hxml.stdout.disabled new file mode 100644 index 00000000..5c4f3fa6 --- /dev/null +++ b/tests/misc/eventLoop/build-cpp.hxml.stdout.disabled @@ -0,0 +1,23 @@ +Main.hx:18: BEFORE1 +Main.hx:35: BEFORE2 +Main.hx:26: T0 +Main.hx:48: A +Main.hx:7: 0 +Main.hx:48: B +Main.hx:7: 1 +Main.hx:48: C +Main.hx:7: 2 +Main.hx:48: D +Main.hx:7: 3 +Main.hx:48: E +Main.hx:7: 4 +Main.hx:7: 5 +Main.hx:7: 6 +Main.hx:7: 7 +Main.hx:7: 8 +Main.hx:7: 9 +Main.hx:10: false +Main.hx:26: T1 +Main.hx:26: T2 +Main.hx:26: T3 +Main.hx:26: T4 diff --git a/tests/misc/eventLoop/build-hl.hxml b/tests/misc/eventLoop/build-hl.hxml new file mode 100644 index 00000000..c3f890e5 --- /dev/null +++ b/tests/misc/eventLoop/build-hl.hxml @@ -0,0 +1,3 @@ +--main Main +--dce full +-hl eventLoop.hl diff --git a/tests/misc/eventLoop/build-hl.hxml.stdout.disabled b/tests/misc/eventLoop/build-hl.hxml.stdout.disabled new file mode 100644 index 00000000..5c4f3fa6 --- /dev/null +++ b/tests/misc/eventLoop/build-hl.hxml.stdout.disabled @@ -0,0 +1,23 @@ +Main.hx:18: BEFORE1 +Main.hx:35: BEFORE2 +Main.hx:26: T0 +Main.hx:48: A +Main.hx:7: 0 +Main.hx:48: B +Main.hx:7: 1 +Main.hx:48: C +Main.hx:7: 2 +Main.hx:48: D +Main.hx:7: 3 +Main.hx:48: E +Main.hx:7: 4 +Main.hx:7: 5 +Main.hx:7: 6 +Main.hx:7: 7 +Main.hx:7: 8 +Main.hx:7: 9 +Main.hx:10: false +Main.hx:26: T1 +Main.hx:26: T2 +Main.hx:26: T3 +Main.hx:26: T4 diff --git a/tests/misc/hl/projects/Issue10184/CheckJson.hx b/tests/misc/hl/projects/Issue10184/CheckJson.hx new file mode 100644 index 00000000..1a21b536 --- /dev/null +++ b/tests/misc/hl/projects/Issue10184/CheckJson.hx @@ -0,0 +1,28 @@ +import sys.FileSystem; + +function deleteDirectory(path:String) { + if (!FileSystem.isDirectory(path)) + return FileSystem.deleteFile(path); + + for (item in FileSystem.readDirectory(path)) + deleteDirectory('$path/$item'); + + FileSystem.deleteDirectory(path); +} + +function main() { + final json = haxe.Json.parse(sys.io.File.getContent("out/hlc.json")); + + final defines:haxe.DynamicAccess = json.defines; + + final success = Lambda.foreach(defines.keys(), function(define:String) { + if (!StringTools.contains(define, "-")) + return true; + + Sys.stderr().writeString("Generated `hlc.json` contains raw version of define flag: " + define + "\n"); + return false; + }); + + deleteDirectory("out"); + Sys.exit(if (success) 0 else 1); +} diff --git a/tests/misc/hl/projects/Issue10184/hlc-json.hxml b/tests/misc/hl/projects/Issue10184/hlc-json.hxml new file mode 100644 index 00000000..5e83e858 --- /dev/null +++ b/tests/misc/hl/projects/Issue10184/hlc-json.hxml @@ -0,0 +1,5 @@ +# test hlc.json defines +--main CheckJson +--hl out/main.c +-D no-compilation +--cmd haxe --run CheckJson diff --git a/tests/misc/hl/projects/Issue10376/CheckSourceHeader.hx b/tests/misc/hl/projects/Issue10376/CheckSourceHeader.hx new file mode 100644 index 00000000..4a6d69e1 --- /dev/null +++ b/tests/misc/hl/projects/Issue10376/CheckSourceHeader.hx @@ -0,0 +1,56 @@ +import sys.FileSystem; + +final FILE = "out/main.c"; + +function deleteDirectory(path:String) { + if (!FileSystem.isDirectory(path)) + return FileSystem.deleteFile(path); + + for (item in FileSystem.readDirectory(path)) + deleteDirectory('$path/$item'); + + FileSystem.deleteDirectory(path); +} + +function main() { + final args = Sys.args(); + + final code = + try { + switch args { + case [""]: + checkForEmptySourceHeader(FILE); + case [expected]: + checkSourceHeader(FILE, expected); + case _: + throw "Incorrect number of arguments to script."; + } + 0; + } catch (e){ + Sys.stderr().writeString(e + "\n"); + 1; + } + + deleteDirectory("out"); + Sys.exit(code); +} + +function checkForEmptySourceHeader(path:String) { + final content = getCSourceContent(path); + + if (StringTools.startsWith(content, "// ")) + throw "File has a source header when none was expected: " + content.split("\n")[0]; +} + +function checkSourceHeader(path:String, expected:String) { + final content = getCSourceContent(path); + + if (!StringTools.startsWith(content, "// " + expected)) + throw "File source header does not start with expected: // " + expected + + "\nSource header: " + content.split("\n")[0]; +} + +function getCSourceContent(path:String) { + // have to skip the BOM character + return sys.io.File.getContent(path).substr(1); +} diff --git a/tests/misc/hl/projects/Issue10376/custom-header.hxml b/tests/misc/hl/projects/Issue10376/custom-header.hxml new file mode 100644 index 00000000..bf6d6453 --- /dev/null +++ b/tests/misc/hl/projects/Issue10376/custom-header.hxml @@ -0,0 +1,5 @@ +--main CheckSourceHeader +--hl out/main.c +-D no-compilation +-D source-header=custom +--cmd haxe --run CheckSourceHeader custom diff --git a/tests/misc/hl/projects/Issue10376/default-header.hxml b/tests/misc/hl/projects/Issue10376/default-header.hxml new file mode 100644 index 00000000..553e7bd3 --- /dev/null +++ b/tests/misc/hl/projects/Issue10376/default-header.hxml @@ -0,0 +1,5 @@ +--main CheckSourceHeader +--hl out/main.c +-D no-compilation + +--cmd haxe --run CheckSourceHeader "Generated by HLC" diff --git a/tests/misc/hl/projects/Issue10376/no-header-dash.hxml b/tests/misc/hl/projects/Issue10376/no-header-dash.hxml new file mode 100644 index 00000000..afce2ea3 --- /dev/null +++ b/tests/misc/hl/projects/Issue10376/no-header-dash.hxml @@ -0,0 +1,5 @@ +--main CheckSourceHeader +--hl out/main.c +-D no-compilation +-D source-header= +--cmd haxe --run CheckSourceHeader "" diff --git a/tests/misc/hl/run.hxml b/tests/misc/hl/run.hxml new file mode 100644 index 00000000..ddd91b1b --- /dev/null +++ b/tests/misc/hl/run.hxml @@ -0,0 +1,2 @@ +-cp ../src +--run Main diff --git a/tests/misc/java/projects/Issue10280/jvm.jar b/tests/misc/java/projects/Issue10280/jvm.jar deleted file mode 100644 index 236cbe7667330d4857cfcb9e6b14b859319b8ab7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 88000 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

84Sl6Bpva^ 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 N0` a + b; + static public final subtract:MathOperation = (a, b) -> a - b; + + static public function performMathOperation(operation:MathOperation) { + return operation.perform(8, 4); + } +} + +class Main { + static function main() { + var result = Ops.performMathOperation(Ops.add); + trace('Add: ${result}'); + + result = Ops.performMathOperation(Ops.subtract); + trace('Subtract: ${result}'); + + result = Ops.performMathOperation(multiply); + trace('Multiply: ${result}'); + + result = Ops.performMathOperation(function(a, b):Int { + return Std.int(a / b); + }); + trace('Divide: ${result}'); + } + + static function multiply(a, b):Int { + return a * b; + } +} \ No newline at end of file diff --git a/tests/misc/java/projects/Issue11014/compile.hxml b/tests/misc/java/projects/Issue11014/compile.hxml new file mode 100644 index 00000000..1fd72ce4 --- /dev/null +++ b/tests/misc/java/projects/Issue11014/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--jvm bin/run.jar +--cmd java -jar bin/run.jar \ No newline at end of file diff --git a/tests/misc/java/projects/Issue11014/compile.hxml.stdout b/tests/misc/java/projects/Issue11014/compile.hxml.stdout new file mode 100644 index 00000000..8b12518f --- /dev/null +++ b/tests/misc/java/projects/Issue11014/compile.hxml.stdout @@ -0,0 +1,4 @@ +Main.hx:17: Add: 12 +Main.hx:20: Subtract: 4 +Main.hx:23: Multiply: 32 +Main.hx:28: Divide: 2 \ No newline at end of file diff --git a/tests/misc/java/projects/Issue11054/Main.hx b/tests/misc/java/projects/Issue11054/Main.hx new file mode 100644 index 00000000..bb2e04db --- /dev/null +++ b/tests/misc/java/projects/Issue11054/Main.hx @@ -0,0 +1,9 @@ +import pack.ItemGroupEvents; + +function test(m:ItemGroupEvents_ModifyEntries) { + m.modifyEntries(12); +} + +function main() { + test(i -> trace(12)); +} diff --git a/tests/misc/java/projects/Issue11054/Setup.hx b/tests/misc/java/projects/Issue11054/Setup.hx new file mode 100644 index 00000000..095043b5 --- /dev/null +++ b/tests/misc/java/projects/Issue11054/Setup.hx @@ -0,0 +1,4 @@ +function main() { + Sys.command("javac", ["pack/ItemGroupEvents.java", "-g"]); + Sys.command("jar", ["cf", "test.jar", "pack/ItemGroupEvents.class", "pack/ItemGroupEvents$ModifyEntries.class"]); +} diff --git a/tests/misc/java/projects/Issue11054/compile.hxml b/tests/misc/java/projects/Issue11054/compile.hxml new file mode 100644 index 00000000..334f8c24 --- /dev/null +++ b/tests/misc/java/projects/Issue11054/compile.hxml @@ -0,0 +1,12 @@ +--main Setup +--interp + +--next + +--main Main +--java-lib test.jar +--jvm run.jar + +--next + +--cmd java -jar run.jar diff --git a/tests/misc/java/projects/Issue11054/compile.hxml.stdout b/tests/misc/java/projects/Issue11054/compile.hxml.stdout new file mode 100644 index 00000000..9ff9a925 --- /dev/null +++ b/tests/misc/java/projects/Issue11054/compile.hxml.stdout @@ -0,0 +1 @@ +Main.hx:8: 12 \ No newline at end of file diff --git a/tests/misc/java/projects/Issue11054/pack/ItemGroupEvents.java b/tests/misc/java/projects/Issue11054/pack/ItemGroupEvents.java new file mode 100644 index 00000000..e60a9d6a --- /dev/null +++ b/tests/misc/java/projects/Issue11054/pack/ItemGroupEvents.java @@ -0,0 +1,7 @@ +package pack; + +public final class ItemGroupEvents { + public interface ModifyEntries { + void modifyEntries(int entries); + } +} \ No newline at end of file diff --git a/tests/misc/js/projects/Issue10184/CheckSourceHeader.hx b/tests/misc/js/projects/Issue10184/CheckSourceHeader.hx new file mode 100644 index 00000000..2f86a552 --- /dev/null +++ b/tests/misc/js/projects/Issue10184/CheckSourceHeader.hx @@ -0,0 +1,35 @@ +final FILE = "main.js"; + +function main() { + final args = Sys.args(); + + switch args { + case [""]: + checkForEmptySourceHeader(FILE); + case [expected]: + checkSourceHeader(FILE, expected); + case _: + Tools.exitWithError("Incorrect number of arguments to script."); + } + + Tools.exit(0); +} + +function checkForEmptySourceHeader(path:String) { + final content = getJsSourceContent(path); + + if (StringTools.startsWith(content, "// ")) + Tools.exitWithError("File has a source header when none was expected: " + content.split("\n")[0]); +} + +function checkSourceHeader(path:String, expected:String) { + final content = getJsSourceContent(path); + + if (!StringTools.startsWith(content, "// " + expected)) + Tools.exitWithError("File source header does not start with expected: // " + expected + + "\nSource header: " + content.split("\n")[0]); +} + +function getJsSourceContent(path:String) { + return sys.io.File.getContent(path); +} diff --git a/tests/misc/js/projects/Issue10184/Main.hx b/tests/misc/js/projects/Issue10184/Main.hx new file mode 100644 index 00000000..243960f3 --- /dev/null +++ b/tests/misc/js/projects/Issue10184/Main.hx @@ -0,0 +1,3 @@ +function main() { + js.Browser.console.log("Hello, World!"); +} diff --git a/tests/misc/js/projects/Issue10184/Tools.hx b/tests/misc/js/projects/Issue10184/Tools.hx new file mode 100644 index 00000000..c2abb90c --- /dev/null +++ b/tests/misc/js/projects/Issue10184/Tools.hx @@ -0,0 +1,12 @@ +import sys.FileSystem; + +function exit(code:Int) { + FileSystem.deleteFile("main.js"); + Sys.exit(code); +} + +function exitWithError(msg:String, code:Int = 1) { + FileSystem.deleteFile("main.js"); + Sys.stderr().writeString(msg + "\n"); + Sys.exit(code); +} diff --git a/tests/misc/js/projects/Issue10184/custom-header-dash.hxml b/tests/misc/js/projects/Issue10184/custom-header-dash.hxml new file mode 100644 index 00000000..4bc79584 --- /dev/null +++ b/tests/misc/js/projects/Issue10184/custom-header-dash.hxml @@ -0,0 +1,4 @@ +--main Main +--js main.js +-D source-header=custom +--cmd haxe --run CheckSourceHeader custom diff --git a/tests/misc/js/projects/Issue10184/custom-header-underscore.hxml b/tests/misc/js/projects/Issue10184/custom-header-underscore.hxml new file mode 100644 index 00000000..01970b26 --- /dev/null +++ b/tests/misc/js/projects/Issue10184/custom-header-underscore.hxml @@ -0,0 +1,4 @@ +--main Main +--js main.js +-D source_header=custom +--cmd haxe --run CheckSourceHeader custom diff --git a/tests/misc/js/projects/Issue10184/default-header.hxml b/tests/misc/js/projects/Issue10184/default-header.hxml new file mode 100644 index 00000000..47b078aa --- /dev/null +++ b/tests/misc/js/projects/Issue10184/default-header.hxml @@ -0,0 +1,4 @@ +# test source headers +--main Main +--js main.js +--cmd haxe --run CheckSourceHeader "Generated by Haxe" diff --git a/tests/misc/js/projects/Issue10184/no-header-dash.hxml b/tests/misc/js/projects/Issue10184/no-header-dash.hxml new file mode 100644 index 00000000..ae5bd0be --- /dev/null +++ b/tests/misc/js/projects/Issue10184/no-header-dash.hxml @@ -0,0 +1,4 @@ +--main Main +--js main.js +-D source-header= +--cmd haxe --run CheckSourceHeader "" diff --git a/tests/misc/js/projects/Issue10184/no-header-underscore.hxml b/tests/misc/js/projects/Issue10184/no-header-underscore.hxml new file mode 100644 index 00000000..b5e13781 --- /dev/null +++ b/tests/misc/js/projects/Issue10184/no-header-underscore.hxml @@ -0,0 +1,4 @@ +--main Main +--js main.js +-D source_header= +--cmd haxe --run CheckSourceHeader "" diff --git a/tests/misc/js/run.hxml b/tests/misc/js/run.hxml new file mode 100644 index 00000000..ddd91b1b --- /dev/null +++ b/tests/misc/js/run.hxml @@ -0,0 +1,2 @@ +-cp ../src +--run Main diff --git a/tests/misc/lua/projects/Issue10979/HaxeException.hx b/tests/misc/lua/projects/Issue10979/HaxeException.hx new file mode 100644 index 00000000..0d1aceb2 --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/HaxeException.hx @@ -0,0 +1,5 @@ +class HaxeException { + static function main() { + throw "Exception thrown from Haxe"; + } +} diff --git a/tests/misc/lua/projects/Issue10979/NativeError.hx b/tests/misc/lua/projects/Issue10979/NativeError.hx new file mode 100644 index 00000000..4bd4bf2b --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/NativeError.hx @@ -0,0 +1,6 @@ +class NativeError { + static function main() { + final object:Dynamic = null; + trace(object.value); + } +} diff --git a/tests/misc/lua/projects/Issue10979/RunScript.hx b/tests/misc/lua/projects/Issue10979/RunScript.hx new file mode 100644 index 00000000..adfd292b --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/RunScript.hx @@ -0,0 +1,38 @@ +using StringTools; + +function isLua5_1() { + final proc = new sys.io.Process("lua", ["-v"]); + final out = proc.stderr.readLine(); + proc.close(); + return out.startsWith("Lua 5.1."); +} + +function matchesExpectedMessage(actual:String) { + // lua 5.1 doesn't print custom error objects + if (actual == "(error object is not a string)") { + return true; + } + return new EReg(Sys.args()[1], "").match(actual); +} + +function main() { + final proc = new sys.io.Process("lua", [Sys.args()[0]]); + + // ignore "lua: " + final exceptionMessage = proc.stderr.readLine().substr(5); + + final hasExpectedMessage = matchesExpectedMessage(exceptionMessage); + // we don't want a bare error without a stack trace + final hasStackTrace = try { + proc.stderr.readLine().contains('stack traceback'); + } catch (_:haxe.io.Eof) { + false; + }; + + Sys.println('Error code: ${proc.exitCode()}'); + Sys.println('Has expected exception message: ${hasExpectedMessage}'); + // 5.1 interpreter doesn't handle custom objects + Sys.println('Has call stack: ${hasStackTrace || isLua5_1()}'); + + proc.close(); +} diff --git a/tests/misc/lua/projects/Issue10979/embedded-native.hxml b/tests/misc/lua/projects/Issue10979/embedded-native.hxml new file mode 100644 index 00000000..b9acfcbd --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/embedded-native.hxml @@ -0,0 +1,4 @@ +--lua bin/native-error.lua +--main NativeError + +--cmd lua runner.lua 'bin/native-error' '%./bin/native%-error%.lua:%d+: attempt to index .+' diff --git a/tests/misc/lua/projects/Issue10979/embedded-native.hxml.stdout b/tests/misc/lua/projects/Issue10979/embedded-native.hxml.stdout new file mode 100644 index 00000000..b530e9b2 --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/embedded-native.hxml.stdout @@ -0,0 +1,3 @@ +Success: false +Has expected exception message: true +Has call stack: true diff --git a/tests/misc/lua/projects/Issue10979/embedded.hxml b/tests/misc/lua/projects/Issue10979/embedded.hxml new file mode 100644 index 00000000..7e648a43 --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/embedded.hxml @@ -0,0 +1,4 @@ +--lua bin/haxe-exception.lua +--main HaxeException + +--cmd lua runner.lua 'bin/haxe-exception' 'Exception thrown from Haxe' diff --git a/tests/misc/lua/projects/Issue10979/embedded.hxml.stdout b/tests/misc/lua/projects/Issue10979/embedded.hxml.stdout new file mode 100644 index 00000000..b530e9b2 --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/embedded.hxml.stdout @@ -0,0 +1,3 @@ +Success: false +Has expected exception message: true +Has call stack: true diff --git a/tests/misc/lua/projects/Issue10979/runner.lua b/tests/misc/lua/projects/Issue10979/runner.lua new file mode 100644 index 00000000..56c6baeb --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/runner.lua @@ -0,0 +1,10 @@ +-- error in loaded script should be caught here, instead of exiting everything +local success, err = pcall(require, arg[1]) + +local exception_message = string.match(tostring(err), '^[^\n]+') +local has_expected_message = string.match(exception_message, arg[2]) ~= nil +local has_stack_trace = string.match(tostring(err), 'stack traceback') ~= nil + +print('Success: '..tostring(success)) +print('Has expected exception message: '..tostring(has_expected_message)) +print('Has call stack: '..tostring(has_stack_trace)) diff --git a/tests/misc/lua/projects/Issue10979/standalone-native.hxml b/tests/misc/lua/projects/Issue10979/standalone-native.hxml new file mode 100644 index 00000000..65d3ea8c --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/standalone-native.hxml @@ -0,0 +1,7 @@ +--lua bin/native-error.lua +--main NativeError + +--next +--run RunScript +bin/native-error.lua +bin/native-error\.lua:\d+: attempt to index .* diff --git a/tests/misc/lua/projects/Issue10979/standalone-native.hxml.stdout b/tests/misc/lua/projects/Issue10979/standalone-native.hxml.stdout new file mode 100644 index 00000000..5cdbdc6e --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/standalone-native.hxml.stdout @@ -0,0 +1,3 @@ +Error code: 1 +Has expected exception message: true +Has call stack: true diff --git a/tests/misc/lua/projects/Issue10979/standalone.hxml b/tests/misc/lua/projects/Issue10979/standalone.hxml new file mode 100644 index 00000000..1505c230 --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/standalone.hxml @@ -0,0 +1,7 @@ +--lua bin/haxe-exception.lua +--main HaxeException + +--next +--run RunScript +bin/haxe-exception.lua +Exception thrown from Haxe diff --git a/tests/misc/lua/projects/Issue10979/standalone.hxml.stdout b/tests/misc/lua/projects/Issue10979/standalone.hxml.stdout new file mode 100644 index 00000000..5cdbdc6e --- /dev/null +++ b/tests/misc/lua/projects/Issue10979/standalone.hxml.stdout @@ -0,0 +1,3 @@ +Error code: 1 +Has expected exception message: true +Has call stack: true diff --git a/tests/misc/lua/projects/Issue9530/Main.hx b/tests/misc/lua/projects/Issue9530/Main.hx new file mode 100644 index 00000000..c5b0f0cc --- /dev/null +++ b/tests/misc/lua/projects/Issue9530/Main.hx @@ -0,0 +1,21 @@ +using StringTools; + +@:nullSafety(Strict) +class Main { + static var f = "field"; + static function main() { + final sclass = new String("foo"); + final scl = sclass.toUpperCase(); + trace(scl); + final f2 = f.toUpperCase(); + trace(f2); + final str = "str".toUpperCase(); + trace(str); + final isS = "sss".startsWith("s"); + trace(isS); + // test internal static call + final i = "foo".indexOf(""); + trace(i); + trace(("dyn" : Dynamic).toUpperCase()); + } +} diff --git a/tests/misc/lua/projects/Issue9530/compile.hxml b/tests/misc/lua/projects/Issue9530/compile.hxml new file mode 100644 index 00000000..4c128112 --- /dev/null +++ b/tests/misc/lua/projects/Issue9530/compile.hxml @@ -0,0 +1,4 @@ +-main Main +-lua bin/test.lua +--cmd lua bin/test.lua +-D no-inline diff --git a/tests/misc/lua/projects/Issue9530/compile.hxml.stdout b/tests/misc/lua/projects/Issue9530/compile.hxml.stdout new file mode 100644 index 00000000..87e39a39 --- /dev/null +++ b/tests/misc/lua/projects/Issue9530/compile.hxml.stdout @@ -0,0 +1,6 @@ +Main.hx:9: FOO +Main.hx:11: FIELD +Main.hx:13: STR +Main.hx:15: true +Main.hx:18: 0 +Main.hx:19: DYN diff --git a/tests/misc/neko/projects/Issue10937/Main.hx b/tests/misc/neko/projects/Issue10937/Main.hx new file mode 100644 index 00000000..52afe81b --- /dev/null +++ b/tests/misc/neko/projects/Issue10937/Main.hx @@ -0,0 +1,70 @@ +import sys.io.Process; + +using StringTools; + +enum abstract Arch(String) { + final Arm64; + final Arm; + final X86; + final X86_64; + + public function getNdllSuffix():String { + return switch abstract { + case Arm64: "Arm64"; + case Arm: "Arm"; + case X86_64: "64"; + case X86: ""; + }; + } +} + +function getArchWindows() { + return switch Sys.getEnv("PROCESSOR_ARCHITECTURE") { + case "x86": X86; + case "AMD64": X86_64; + case "ARM64": Arm64; + case other: throw 'Unknown CPU architecture: $other'; + }; +} + +function getArchUnix() { + final uname = new Process("uname", ["-m"]); + + final arch = try { + uname.stdout.readLine(); + } catch (e:haxe.io.Eof) { + ""; + }; + + uname.kill(); + uname.close(); + + return switch arch { + case "x86_64" | "amd64": X86_64; + case "i386" | "x86": X86; + case "arm64" | "aarch64": Arm64; + case "arm": Arm; + case other: throw 'Unknown CPU architecture: "$other"'; + }; +} + +function getArch() { + return switch Sys.systemName() { + case "Windows": getArchWindows(); + default: getArchUnix(); + }; +} + +function main() { + final arch = getArch(); + + final expectedNdllSubDir = Sys.systemName() + arch.getNdllSuffix() + "/"; + + final ndllPath = neko.vm.Loader.local().getPath()[0]; + + if (ndllPath.endsWith(expectedNdllSubDir)) { + Sys.println("Success"); + } else { + Sys.println('Failure: Expected $ndllPath to end with $expectedNdllSubDir'); + } +} diff --git a/tests/misc/neko/projects/Issue10937/aaa-setup.hxml b/tests/misc/neko/projects/Issue10937/aaa-setup.hxml new file mode 100644 index 00000000..4df36699 --- /dev/null +++ b/tests/misc/neko/projects/Issue10937/aaa-setup.hxml @@ -0,0 +1 @@ +--cmd haxelib dev dummy_ndll dummy_ndll diff --git a/tests/misc/neko/projects/Issue10937/compile.hxml b/tests/misc/neko/projects/Issue10937/compile.hxml new file mode 100644 index 00000000..dacc92e2 --- /dev/null +++ b/tests/misc/neko/projects/Issue10937/compile.hxml @@ -0,0 +1,4 @@ +--main Main +--neko bin/main.n +-lib dummy_ndll +--cmd neko bin/main.n diff --git a/tests/misc/neko/projects/Issue10937/compile.hxml.stdout b/tests/misc/neko/projects/Issue10937/compile.hxml.stdout new file mode 100644 index 00000000..35821117 --- /dev/null +++ b/tests/misc/neko/projects/Issue10937/compile.hxml.stdout @@ -0,0 +1 @@ +Success diff --git a/tests/misc/neko/projects/Issue10937/dummy_ndll/ndll/.exists b/tests/misc/neko/projects/Issue10937/dummy_ndll/ndll/.exists new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/neko/run.hxml b/tests/misc/neko/run.hxml new file mode 100644 index 00000000..ddd91b1b --- /dev/null +++ b/tests/misc/neko/run.hxml @@ -0,0 +1,2 @@ +-cp ../src +--run Main diff --git a/tests/misc/php/projects/Issue10736/compile.hxml b/tests/misc/php/projects/Issue10736/compile.hxml new file mode 100644 index 00000000..c6c32c38 --- /dev/null +++ b/tests/misc/php/projects/Issue10736/compile.hxml @@ -0,0 +1,18 @@ +-cp src + +--each + +-D php-prefix=app1 +-main App1 +-php bin/app1 + +--next + +-D php-prefix=app2 +-main App2 +-php bin/app2 + +--next + +--cmd php bin/app1/index.php +--cmd php bin/app2/index.php \ No newline at end of file diff --git a/tests/misc/php/projects/Issue10736/src/App1.hx b/tests/misc/php/projects/Issue10736/src/App1.hx new file mode 100644 index 00000000..1d8a934a --- /dev/null +++ b/tests/misc/php/projects/Issue10736/src/App1.hx @@ -0,0 +1,5 @@ +class App1 { + static function main() { + sys.io.File.saveContent('bin/serialized.data', php.Global.serialize({a:1})); + } +} \ No newline at end of file diff --git a/tests/misc/php/projects/Issue10736/src/App2.hx b/tests/misc/php/projects/Issue10736/src/App2.hx new file mode 100644 index 00000000..87cf4c8e --- /dev/null +++ b/tests/misc/php/projects/Issue10736/src/App2.hx @@ -0,0 +1,5 @@ +class App2 { + static function main() { + php.Global.unserialize(sys.io.File.getContent('bin/serialized.data')); + } +} \ No newline at end of file diff --git a/tests/misc/php/projects/Issue10820/Foo.hx b/tests/misc/php/projects/Issue10820/Foo.hx new file mode 100644 index 00000000..c55a2da7 --- /dev/null +++ b/tests/misc/php/projects/Issue10820/Foo.hx @@ -0,0 +1,8 @@ +class PAckFoo extends pack.Foo {} + +class Foo{ + public var bar : PAckFoo; + public function new(){ + bar = new PAckFoo(); + } +} \ No newline at end of file diff --git a/tests/misc/php/projects/Issue10820/Main.hx b/tests/misc/php/projects/Issue10820/Main.hx new file mode 100644 index 00000000..e2da5fac --- /dev/null +++ b/tests/misc/php/projects/Issue10820/Main.hx @@ -0,0 +1,8 @@ +class Main { + static var tmp:Any; + + static function main() { + // pack.Foo.test(); + tmp = new Foo().bar; + } +} diff --git a/tests/misc/php/projects/Issue10820/compile.hxml b/tests/misc/php/projects/Issue10820/compile.hxml new file mode 100644 index 00000000..c5e1b63a --- /dev/null +++ b/tests/misc/php/projects/Issue10820/compile.hxml @@ -0,0 +1,4 @@ +--main Main +--php bin + +--cmd php bin/index.php diff --git a/tests/misc/php/projects/Issue10820/pack/Foo.hx b/tests/misc/php/projects/Issue10820/pack/Foo.hx new file mode 100644 index 00000000..9442d2fb --- /dev/null +++ b/tests/misc/php/projects/Issue10820/pack/Foo.hx @@ -0,0 +1,5 @@ +package pack; + +class Foo { + public function new() {} +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10005/compile-fail.hxml.stderr b/tests/misc/projects/Issue10005/compile-fail.hxml.stderr index 3669def2..85717c84 100644 --- a/tests/misc/projects/Issue10005/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue10005/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:5: characters 16-24 : Only extern functions may be overloaded on this target \ No newline at end of file +Main.hx:5: characters 16-24 : Invalid modifier: overload is only supported on extern functions \ No newline at end of file diff --git a/tests/misc/projects/Issue10034/Main.hx b/tests/misc/projects/Issue10034/Main.hx new file mode 100644 index 00000000..37d66b18 --- /dev/null +++ b/tests/misc/projects/Issue10034/Main.hx @@ -0,0 +1,13 @@ +#if macro +class Foo { + public static function build(f:() -> Void) + return []; + + public static function other():Void {} +} +#else +@:build(Foo.build(Foo.other)) +class Main {} + +class Foo {} +#end diff --git a/tests/misc/projects/Issue10205/compile-fail.hxml b/tests/misc/projects/Issue10034/compile-fail.hxml similarity index 100% rename from tests/misc/projects/Issue10205/compile-fail.hxml rename to tests/misc/projects/Issue10034/compile-fail.hxml diff --git a/tests/misc/projects/Issue10034/compile-fail.hxml.stderr b/tests/misc/projects/Issue10034/compile-fail.hxml.stderr new file mode 100644 index 00000000..ce00834f --- /dev/null +++ b/tests/misc/projects/Issue10034/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:9: characters 19-28 : haxe.macro.Expr should be () -> Void +Main.hx:9: characters 19-28 : ... For function argument 'f' \ No newline at end of file diff --git a/tests/misc/projects/Issue10039/compile-fail.hxml.stderr b/tests/misc/projects/Issue10039/compile-fail.hxml.stderr index 6b6b8d3e..0ab49260 100644 --- a/tests/misc/projects/Issue10039/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue10039/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 41-43 : Type parameters of multi type abstracts must be known (for haxe.IMap : String, Int>) \ No newline at end of file +Main.hx:2: characters 41-43 : Type parameters of multi type abstracts must be known (for haxe.IMap, Int>) \ No newline at end of file diff --git a/tests/misc/projects/Issue10052/compile-fail.hxml.stderr b/tests/misc/projects/Issue10052/compile-fail.hxml.stderr index 74bf2bfc..b7c0e197 100644 --- a/tests/misc/projects/Issue10052/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue10052/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:20: characters 3-37 : Cannot access field or identifier position for writing \ No newline at end of file +Main.hx:20: characters 10-18 : This expression cannot be accessed for writing \ No newline at end of file diff --git a/tests/misc/projects/Issue10107/Main.hx b/tests/misc/projects/Issue10107/Main.hx new file mode 100644 index 00000000..8ad327c2 --- /dev/null +++ b/tests/misc/projects/Issue10107/Main.hx @@ -0,0 +1,7 @@ +@:using(Main) +typedef B = { + > A, +}; + +typedef A = {}; +function main() {} diff --git a/tests/misc/projects/Issue10107/compile.hxml b/tests/misc/projects/Issue10107/compile.hxml new file mode 100644 index 00000000..a711e039 --- /dev/null +++ b/tests/misc/projects/Issue10107/compile.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp diff --git a/tests/misc/projects/Issue10139/Main.hx b/tests/misc/projects/Issue10139/Main.hx new file mode 100644 index 00000000..5e63e232 --- /dev/null +++ b/tests/misc/projects/Issue10139/Main.hx @@ -0,0 +1,11 @@ +abstract class A { + final x:Int; + final y:Int; +} + +class B extends A { + final z:Int; + public function new() {} +} + +function main() {} diff --git a/tests/misc/projects/Issue10139/compile-fail.hxml b/tests/misc/projects/Issue10139/compile-fail.hxml new file mode 100644 index 00000000..c717a287 --- /dev/null +++ b/tests/misc/projects/Issue10139/compile-fail.hxml @@ -0,0 +1,2 @@ +-main Main +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10139/compile-fail.hxml.stderr b/tests/misc/projects/Issue10139/compile-fail.hxml.stderr new file mode 100644 index 00000000..3cb5a003 --- /dev/null +++ b/tests/misc/projects/Issue10139/compile-fail.hxml.stderr @@ -0,0 +1,4 @@ +Main.hx:6: characters 7-8 : Some final fields are uninitialized in this class +Main.hx:7: characters 8-9 : Uninitialized field +Main.hx:2: characters 8-9 : Uninitialized field +Main.hx:3: characters 8-9 : Uninitialized field \ No newline at end of file diff --git a/tests/misc/projects/Issue10147/Main.hx b/tests/misc/projects/Issue10147/Main.hx new file mode 100644 index 00000000..f7a2d0f6 --- /dev/null +++ b/tests/misc/projects/Issue10147/Main.hx @@ -0,0 +1,28 @@ +typedef HasAString = { + text:String +} +typedef HasANullString = { + text:Null +} + +@:nullSafety(StrictThreaded) +class Main { + static function main() { + final has:HasAString = {text: null}; + + final tmp = {text: null}; + final typed:HasAString = tmp; + + final tmp = {text: null}; + final typed:{text:String} = tmp; + + final tmp = {text: null}; + final arr:Array = [tmp.text]; + + // should pass + final tmp = {text: null}; + final typed:{text:Null} = tmp; + final typed2:HasANullString = tmp; + final typed3:HasANullString = typed; + } +} diff --git a/tests/misc/projects/Issue10147/compile-fail.hxml b/tests/misc/projects/Issue10147/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10147/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10147/compile-fail.hxml.stderr b/tests/misc/projects/Issue10147/compile-fail.hxml.stderr new file mode 100644 index 00000000..e20f632e --- /dev/null +++ b/tests/misc/projects/Issue10147/compile-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main.hx:14: characters 3-32 : Null safety: Cannot unify { text : Null } with HasAString +Main.hx:17: characters 3-35 : Null safety: Cannot unify { text : Null } with { text : String } +Main.hx:20: characters 30-38 : Null safety: Cannot use nullable value of Null as an item in Array diff --git a/tests/misc/projects/Issue10152/Main.hx b/tests/misc/projects/Issue10152/Main.hx new file mode 100644 index 00000000..0ab798a5 --- /dev/null +++ b/tests/misc/projects/Issue10152/Main.hx @@ -0,0 +1,9 @@ +class Main { + static function main () { + #if (foo != "bar") + trace("NOT EQUAL"); + #else + trace("EQUAL"); + #end + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10152/compile-defined.hxml b/tests/misc/projects/Issue10152/compile-defined.hxml new file mode 100644 index 00000000..e2b8edb0 --- /dev/null +++ b/tests/misc/projects/Issue10152/compile-defined.hxml @@ -0,0 +1,3 @@ +--main Main +-D foo=bar +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10152/compile-defined.hxml.stdout b/tests/misc/projects/Issue10152/compile-defined.hxml.stdout new file mode 100644 index 00000000..ee45c236 --- /dev/null +++ b/tests/misc/projects/Issue10152/compile-defined.hxml.stdout @@ -0,0 +1 @@ +Main.hx:6: EQUAL diff --git a/tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml b/tests/misc/projects/Issue10152/compile-undefined.hxml similarity index 100% rename from tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml rename to tests/misc/projects/Issue10152/compile-undefined.hxml diff --git a/tests/misc/projects/Issue10152/compile-undefined.hxml.stdout b/tests/misc/projects/Issue10152/compile-undefined.hxml.stdout new file mode 100644 index 00000000..43b4342a --- /dev/null +++ b/tests/misc/projects/Issue10152/compile-undefined.hxml.stdout @@ -0,0 +1 @@ +Main.hx:4: NOT EQUAL diff --git a/tests/misc/projects/Issue10170/Main.hx b/tests/misc/projects/Issue10170/Main.hx new file mode 100644 index 00000000..7c43f3d4 --- /dev/null +++ b/tests/misc/projects/Issue10170/Main.hx @@ -0,0 +1,7 @@ +function f():Int { + throw invalid; +} + +function main() { + +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10170/compile-fail.hxml b/tests/misc/projects/Issue10170/compile-fail.hxml new file mode 100644 index 00000000..cafa855d --- /dev/null +++ b/tests/misc/projects/Issue10170/compile-fail.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10170/compile-fail.hxml.stderr b/tests/misc/projects/Issue10170/compile-fail.hxml.stderr new file mode 100644 index 00000000..ef78c72e --- /dev/null +++ b/tests/misc/projects/Issue10170/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:2: characters 8-15 : Unknown identifier : invalid \ No newline at end of file diff --git a/tests/misc/projects/Issue10171/Main.hx b/tests/misc/projects/Issue10171/Main.hx new file mode 100644 index 00000000..9f6eb5b7 --- /dev/null +++ b/tests/misc/projects/Issue10171/Main.hx @@ -0,0 +1,14 @@ +/** + I'm a library! +**/ + +/** + a random number generator (only works once) +**/ +function randomNumber() { + return 7; +} + +function main() { + randomNumber(); +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10171/compile.hxml b/tests/misc/projects/Issue10171/compile.hxml new file mode 100644 index 00000000..ce850fd6 --- /dev/null +++ b/tests/misc/projects/Issue10171/compile.hxml @@ -0,0 +1,4 @@ +-cp src +-main Main +--interp +--display "Main.hx@150@signature" diff --git a/tests/misc/projects/Issue10171/compile.hxml.stderr b/tests/misc/projects/Issue10171/compile.hxml.stderr new file mode 100644 index 00000000..76173d1c --- /dev/null +++ b/tests/misc/projects/Issue10171/compile.hxml.stderr @@ -0,0 +1 @@ +{"signatures":[{"documentation":"\n\ta random number generator (only works once)\n","label":"():Int","parameters":[]}],"activeParameter":0,"activeSignature":0} \ No newline at end of file diff --git a/tests/misc/projects/Issue10184/BuiltIn.hx b/tests/misc/projects/Issue10184/BuiltIn.hx new file mode 100644 index 00000000..b9166197 --- /dev/null +++ b/tests/misc/projects/Issue10184/BuiltIn.hx @@ -0,0 +1,12 @@ +import haxe.macro.Context; + +function main() { + // test that built in defines can be accessed by macros + // either with a dash or underscore + + if (!Context.defined("haxe-ver")) + throw "`haxe-ver` flag is missing"; + + if (!Context.defined("haxe_ver")) + throw "`haxe_ver` flag is missing"; +} diff --git a/tests/misc/projects/Issue10184/Deprecated.hx b/tests/misc/projects/Issue10184/Deprecated.hx new file mode 100644 index 00000000..17017e00 --- /dev/null +++ b/tests/misc/projects/Issue10184/Deprecated.hx @@ -0,0 +1,4 @@ +function main() { + // usage of a deprecated function to generate warning + Std.is("string", String); +} diff --git a/tests/misc/projects/Issue10184/Flags.hx b/tests/misc/projects/Issue10184/Flags.hx new file mode 100644 index 00000000..8a30548a --- /dev/null +++ b/tests/misc/projects/Issue10184/Flags.hx @@ -0,0 +1,27 @@ +import haxe.macro.Context; + +function main() { + // set with dash + if (!Context.defined("f-dash")) + throw "`f-dash` flag is missing"; + if (!Context.defined("f_dash")) + throw "`f_dash` flag is missing"; + + // set with underscore + if (!Context.defined("f-underscore")) + throw "`f-underscore` flag is missing"; + if (!Context.defined("f_underscore")) + throw "`f_underscore` flag is missing"; + + // value set with dash + if (Context.definedValue("v-dash") != "value") + throw "`v-dash` flag has incorrect value: " + Context.definedValue("v-dash"); + if (Context.definedValue("v_dash") != "value") + throw "`v_dash` flag has incorrect value" + Context.definedValue("v_dash"); + + // value set with underscore + if (Context.definedValue("v-underscore") != "value") + throw "`v-underscore` flag has incorrect value" + Context.definedValue("v-underscore"); + if (Context.definedValue("v_underscore") != "value") + throw "`v_underscore` flag has incorrect value" + Context.definedValue("v-underscore"); +} diff --git a/tests/misc/projects/Issue10184/GetDefines.hx b/tests/misc/projects/Issue10184/GetDefines.hx new file mode 100644 index 00000000..d498bfe4 --- /dev/null +++ b/tests/misc/projects/Issue10184/GetDefines.hx @@ -0,0 +1,27 @@ +function main() { + final defines = haxe.macro.Context.getDefines(); + + // -D f-dash + if (!defines.exists("f-dash")) + throw "`f-dash` flag is missing"; + if (!defines.exists("f_dash")) + throw "`f_dash` flag is missing"; + + // -D f_underscore + if (!defines.exists("f-underscore")) + throw "`f-underscore` flag is missing"; + if (!defines.exists("f_underscore")) + throw "`f_underscore` flag is missing"; + + // -D v-dash=value + if (defines["v-dash"] != "value") + throw "`v-dash` flag value is incorrect: " + defines["v-dash"]; + if (defines["v_dash"] != "value") + throw "`v_dash` flag value is incorrect: " + defines["v_dash"]; + + // -D v_underscore=value + if (defines["v-underscore"] != "value") + throw "`v-underscore` flag value is incorrect: " + defines["v-underscore"]; + if (defines["v_underscore"] != "value") + throw "`v_underscore` flag value is incorrect: " + defines["v_underscore"]; +} diff --git a/tests/misc/projects/Issue10184/IfBlocks.hx b/tests/misc/projects/Issue10184/IfBlocks.hx new file mode 100644 index 00000000..9ea4fa0d --- /dev/null +++ b/tests/misc/projects/Issue10184/IfBlocks.hx @@ -0,0 +1,25 @@ +function throwInactive(flag:String) { + throw '`$flag` block is not active'; +} + +function throwInactiveValue(flag:String, expected:String, value:String) { + throw '`$flag` block is not active, as flag has value `$value` instead of `$expected`'; +} + +function main() { + #if !f_dash + throwInactive("f_dash"); + #end + + #if !f_underscore + throwInactive("f_underscore"); + #end + + #if (v_dash!="value") + throwInactiveValue("v_dash", "value", haxe.macro.Context.definedValue("v_dash")); + #end + + #if (v_underscore!="value") + throwInactiveValue("v_underscore", "value", haxe.macro.Context.definedValue("v_underscore")); + #end +} diff --git a/tests/misc/projects/Issue10184/Repeated.hx b/tests/misc/projects/Issue10184/Repeated.hx new file mode 100644 index 00000000..57297018 --- /dev/null +++ b/tests/misc/projects/Issue10184/Repeated.hx @@ -0,0 +1,25 @@ +import haxe.macro.Context; + +function main() { + // dash set then underscore + if (Context.definedValue("value-a") != "new value") + throw '`value-a` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value-a")}`'; + if (Context.definedValue("value_a") != "new value") + throw '`value_a` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value_a")}`'; + + if (Context.definedValue("value-b") != "new value") + throw '`value-b` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value-b")}`'; + if (Context.definedValue("value_b") != "new value") + throw '`value_b` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value_b")}`'; + + // underscore set then dash + if (Context.definedValue("value-c") != "new value") + throw '`value-c` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value-c")}`'; + if (Context.definedValue("value_c") != "new value") + throw '`value_c` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value_c")}`'; + + if (Context.definedValue("value-d") != "new value") + throw '`value-c` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value-d")}`'; + if (Context.definedValue("value_d") != "new value") + throw '`value_c` flag has incorrect value. Expected: `new value`, Got: `${Context.definedValue("value_d")}`'; +} diff --git a/tests/misc/projects/Issue10184/builtin.hxml b/tests/misc/projects/Issue10184/builtin.hxml new file mode 100644 index 00000000..33582f72 --- /dev/null +++ b/tests/misc/projects/Issue10184/builtin.hxml @@ -0,0 +1 @@ +--run BuiltIn diff --git a/tests/misc/projects/Issue10184/check-warning.hxml b/tests/misc/projects/Issue10184/check-warning.hxml new file mode 100644 index 00000000..2641e488 --- /dev/null +++ b/tests/misc/projects/Issue10184/check-warning.hxml @@ -0,0 +1,2 @@ +# deprecation warnings should show +--run Deprecated diff --git a/tests/misc/projects/Issue10184/check-warning.hxml.stderr b/tests/misc/projects/Issue10184/check-warning.hxml.stderr new file mode 100644 index 00000000..1f517a22 --- /dev/null +++ b/tests/misc/projects/Issue10184/check-warning.hxml.stderr @@ -0,0 +1 @@ +Deprecated.hx:3: characters 2-8 : Warning : (WDeprecated) Std.is is deprecated. Use Std.isOfType instead. diff --git a/tests/misc/projects/Issue10184/cli.hxml b/tests/misc/projects/Issue10184/cli.hxml new file mode 100644 index 00000000..c92a09d9 --- /dev/null +++ b/tests/misc/projects/Issue10184/cli.hxml @@ -0,0 +1,7 @@ +# test defines set via -D +-D f-dash +-D f_underscore +-D v-dash=value +-D v_underscore=value + +--run Flags diff --git a/tests/misc/projects/Issue10184/get-defines.hxml b/tests/misc/projects/Issue10184/get-defines.hxml new file mode 100644 index 00000000..bc488195 --- /dev/null +++ b/tests/misc/projects/Issue10184/get-defines.hxml @@ -0,0 +1,18 @@ +# test that Context.getDefines works correctly + +--main GetDefines +--interp + +--each + +-D f-dash +-D f_underscore +-D v-dash=value +-D v_underscore=value + +--next + +--macro define("f-dash") +--macro define("f_underscore") +--macro define("v-dash", "value") +--macro define("v_underscore", "value") diff --git a/tests/misc/projects/Issue10184/if-blocks.hxml b/tests/misc/projects/Issue10184/if-blocks.hxml new file mode 100644 index 00000000..6edbebc3 --- /dev/null +++ b/tests/misc/projects/Issue10184/if-blocks.hxml @@ -0,0 +1,18 @@ +# test using if blocks + +--main IfBlocks +--interp + +--each + +-D f-dash +-D f_underscore +-D v-dash=value +-D v_underscore=value + +--next + +--macro define("f-dash") +--macro define("f_underscore") +--macro define("v-dash", "value") +--macro define("v_underscore", "value") diff --git a/tests/misc/projects/Issue10184/macro.hxml b/tests/misc/projects/Issue10184/macro.hxml new file mode 100644 index 00000000..e8e46cc6 --- /dev/null +++ b/tests/misc/projects/Issue10184/macro.hxml @@ -0,0 +1,7 @@ +# test defines set via define macro function +--macro define("f-dash") +--macro define("f_underscore") +--macro define("v-dash", "value") +--macro define("v_underscore", "value") + +--run Flags diff --git a/tests/misc/projects/Issue10184/no-warning-underscore.hxml b/tests/misc/projects/Issue10184/no-warning-underscore.hxml new file mode 100644 index 00000000..fe3cd2e8 --- /dev/null +++ b/tests/misc/projects/Issue10184/no-warning-underscore.hxml @@ -0,0 +1,3 @@ +# should have no deprecation warnings +-D no_deprecation_warnings +--run Deprecated diff --git a/tests/misc/projects/Issue10184/no-warning-underscore.hxml.stderr b/tests/misc/projects/Issue10184/no-warning-underscore.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10184/no-warning.hxml b/tests/misc/projects/Issue10184/no-warning.hxml new file mode 100644 index 00000000..745dc1ea --- /dev/null +++ b/tests/misc/projects/Issue10184/no-warning.hxml @@ -0,0 +1,3 @@ +# should have no deprecation warnings +-D no-deprecation-warnings +--run Deprecated diff --git a/tests/misc/projects/Issue10184/no-warning.hxml.stderr b/tests/misc/projects/Issue10184/no-warning.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10184/repeated.hxml b/tests/misc/projects/Issue10184/repeated.hxml new file mode 100644 index 00000000..33accb67 --- /dev/null +++ b/tests/misc/projects/Issue10184/repeated.hxml @@ -0,0 +1,18 @@ +# test defining the same define twice sets it to the most recent value, +# regardless of if it uses a dash or underscore. + +# dash first, then underscore +-D value-a=old value +-D value_a=new value + +--macro define("value-b", "old value") +--macro define("value_b", "new value") + +# underscore first, then dash +-D value_c=old value +-D value-c=new value + +--macro define("value_d", "old value") +--macro define("value-d", "new value") + +--run Repeated diff --git a/tests/misc/projects/Issue10186/Main.hx b/tests/misc/projects/Issue10186/Main.hx new file mode 100644 index 00000000..50aebe11 --- /dev/null +++ b/tests/misc/projects/Issue10186/Main.hx @@ -0,0 +1,6 @@ +abstract Main(Dynamic) { + static function main() {} + var bar(get, never):Int; + public function foo(?v = bar) {} + function get_bar() return 1; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10186/compile-fail.hxml b/tests/misc/projects/Issue10186/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10186/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10186/compile-fail.hxml.stderr b/tests/misc/projects/Issue10186/compile-fail.hxml.stderr new file mode 100644 index 00000000..7f2f7c7c --- /dev/null +++ b/tests/misc/projects/Issue10186/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 27-30 : Cannot reference this abstract here \ No newline at end of file diff --git a/tests/misc/projects/Issue10189/Main.hx b/tests/misc/projects/Issue10189/Main.hx new file mode 100644 index 00000000..2cc5c89d --- /dev/null +++ b/tests/misc/projects/Issue10189/Main.hx @@ -0,0 +1,17 @@ +enum E { + A; + B; + C(v:Int); +} + +class Main { + static function main() { + var e = A; + switch (e) { + case A: + case B: + case C(0): + case C(x) if (x > 0): + } + } +} diff --git a/tests/misc/projects/Issue10189/compile-fail.hxml b/tests/misc/projects/Issue10189/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10189/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10189/compile-fail.hxml.stderr b/tests/misc/projects/Issue10189/compile-fail.hxml.stderr new file mode 100644 index 00000000..b12d97bc --- /dev/null +++ b/tests/misc/projects/Issue10189/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:14: characters 11-12 : Unmatched patterns: C(_) \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/Main.hx b/tests/misc/projects/Issue10198/Main.hx new file mode 100644 index 00000000..00583f2a --- /dev/null +++ b/tests/misc/projects/Issue10198/Main.hx @@ -0,0 +1,7 @@ +class Main { + static function main() + var b:Int = fn(fn('s')); + + static public function fn(v:R):T + return null; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/Main2.hx b/tests/misc/projects/Issue10198/Main2.hx new file mode 100644 index 00000000..9a443415 --- /dev/null +++ b/tests/misc/projects/Issue10198/Main2.hx @@ -0,0 +1,31 @@ +class Main2 { + static function main() { + var foo:Vector = null; + var bar:Vector = Vector.fromIterable(foo); + } +} + +typedef Foo = { + final id:String; + final ?project:String; +} + +typedef Bar = { + final id:String; + final createDate:Date; +} + +abstract Vector(Array) { + + inline function new(a) + this = a; + + @:from static function fromVector(v:Vector):Vector + return cast v; + + static public function fromIterable(v:Iterable):Vector + return null; + + @:to public function toArray() + return this.copy(); +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/Main3.hx b/tests/misc/projects/Issue10198/Main3.hx new file mode 100644 index 00000000..87e7c374 --- /dev/null +++ b/tests/misc/projects/Issue10198/Main3.hx @@ -0,0 +1,12 @@ +function fn1(v:R1):T1 + return null; + +function fn2(v:R2):T2 + return null; + +function fn3(v:R3):T3 + return null; + +function main() { + var b:Array = fn1(fn2(fn3(['s']))); +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile-fail.hxml b/tests/misc/projects/Issue10198/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10198/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile-fail.hxml.stderr b/tests/misc/projects/Issue10198/compile-fail.hxml.stderr new file mode 100644 index 00000000..d19117b7 --- /dev/null +++ b/tests/misc/projects/Issue10198/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:3: characters 3-6 : Constraint check failure for fn.R +Main.hx:3: characters 3-6 : ... String should be Int \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile2-fail.hxml b/tests/misc/projects/Issue10198/compile2-fail.hxml new file mode 100644 index 00000000..9ef4475e --- /dev/null +++ b/tests/misc/projects/Issue10198/compile2-fail.hxml @@ -0,0 +1 @@ +--main Main2 \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile2-fail.hxml.stderr b/tests/misc/projects/Issue10198/compile2-fail.hxml.stderr new file mode 100644 index 00000000..d4a0d846 --- /dev/null +++ b/tests/misc/projects/Issue10198/compile2-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main2.hx:4: characters 3-50 : error: { ?project : Null, id : String } has no field createDate +Main2.hx:4: characters 3-50 : ... have: Vector +Main2.hx:4: characters 3-50 : ... want: Vector \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile3-fail.hxml b/tests/misc/projects/Issue10198/compile3-fail.hxml new file mode 100644 index 00000000..25e95386 --- /dev/null +++ b/tests/misc/projects/Issue10198/compile3-fail.hxml @@ -0,0 +1 @@ +--main Main3 \ No newline at end of file diff --git a/tests/misc/projects/Issue10198/compile3-fail.hxml.stderr b/tests/misc/projects/Issue10198/compile3-fail.hxml.stderr new file mode 100644 index 00000000..c1e1e6c1 --- /dev/null +++ b/tests/misc/projects/Issue10198/compile3-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main3.hx:11: characters 2-42 : error: String should be Int +Main3.hx:11: characters 2-42 : ... have: Array +Main3.hx:11: characters 2-42 : ... want: Array \ No newline at end of file diff --git a/tests/misc/projects/Issue10205/Main.hx b/tests/misc/projects/Issue10205/Main.hx deleted file mode 100644 index a7e9e98d..00000000 --- a/tests/misc/projects/Issue10205/Main.hx +++ /dev/null @@ -1,8 +0,0 @@ -class Main { - static function main() { - test('hello'); - } - - overload extern static inline function test(s:String) {} - overload extern static inline function test(s:String, ...r:String) {} -} \ No newline at end of file diff --git a/tests/misc/projects/Issue10205/compile-fail.hxml.stderr b/tests/misc/projects/Issue10205/compile-fail.hxml.stderr deleted file mode 100644 index 22102396..00000000 --- a/tests/misc/projects/Issue10205/compile-fail.hxml.stderr +++ /dev/null @@ -1,3 +0,0 @@ -Main.hx:3: characters 3-16 : Ambiguous overload, candidates follow -Main.hx:6: characters 41-45 : ... (s : String) -> Void -Main.hx:7: characters 41-45 : ... (s : String, r : haxe.Rest) -> Void \ No newline at end of file diff --git a/tests/misc/projects/Issue10219/Main.hx b/tests/misc/projects/Issue10219/Main.hx new file mode 100644 index 00000000..5ea44c96 --- /dev/null +++ b/tests/misc/projects/Issue10219/Main.hx @@ -0,0 +1,9 @@ +function main() { + var x:Int; + if (Math.random() < 0.5) { + x = 3; + } else { + var b = () -> 3; + } + trace(x); +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10219/compile-fail.hxml b/tests/misc/projects/Issue10219/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10219/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10219/compile-fail.hxml.stderr b/tests/misc/projects/Issue10219/compile-fail.hxml.stderr new file mode 100644 index 00000000..a76e995b --- /dev/null +++ b/tests/misc/projects/Issue10219/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:8: characters 8-9 : Local variable x used without being initialized \ No newline at end of file diff --git a/tests/misc/projects/Issue10229/Main.hx b/tests/misc/projects/Issue10229/Main.hx new file mode 100644 index 00000000..63d1965b --- /dev/null +++ b/tests/misc/projects/Issue10229/Main.hx @@ -0,0 +1,25 @@ +typedef Foo = { + final id:String; + final ?project:String; +} + +typedef Bar = { + final id:String; + final createDate:Date; + final ?project:String; +} + +class Main { + static function constrained(v:R):T + return null; + + static function fn(s:T):T { + return s; + } + + static function main() { + var A:Int = fn('s'); // Error "String should be Int" + var B:Int = constrained('s'); //Error "Int should be String" instead of "String should be Int" + var C:Bar = constrained((null:Foo)); // compiles, but should error "Foo should be Bar; { ?project : Null, id : String } has no field createDate" + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10229/compile-fail.hxml b/tests/misc/projects/Issue10229/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10229/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10229/compile-fail.hxml.stderr b/tests/misc/projects/Issue10229/compile-fail.hxml.stderr new file mode 100644 index 00000000..7f4fb370 --- /dev/null +++ b/tests/misc/projects/Issue10229/compile-fail.hxml.stderr @@ -0,0 +1,6 @@ +Main.hx:21: characters 3-23 : String should be Int +Main.hx:22: characters 3-32 : Constraint check failure for constrained.R +Main.hx:22: characters 3-32 : ... String should be Int +Main.hx:23: characters 3-39 : Constraint check failure for constrained.R +Main.hx:23: characters 3-39 : ... Foo should be Bar +Main.hx:23: characters 3-39 : ... { ?project : Null, id : String } has no field createDate \ No newline at end of file diff --git a/tests/misc/projects/Issue10245/Main.hx b/tests/misc/projects/Issue10245/Main.hx new file mode 100644 index 00000000..c3d6dc0c --- /dev/null +++ b/tests/misc/projects/Issue10245/Main.hx @@ -0,0 +1,2 @@ +@:generic +class Foo extends Foo {} \ No newline at end of file diff --git a/tests/misc/projects/Issue10245/compile-fail.hxml b/tests/misc/projects/Issue10245/compile-fail.hxml new file mode 100644 index 00000000..c849dc5d --- /dev/null +++ b/tests/misc/projects/Issue10245/compile-fail.hxml @@ -0,0 +1,8 @@ +--main Main +--interp +--macro addGlobalMetadata("", "@meta0", false) +--macro addGlobalMetadata("", "@meta1", true) +--macro addGlobalMetadata("Main", "@meta2", false) +--macro addGlobalMetadata("Main", "@meta3", true) +--macro addGlobalMetadata("Main.MainSub", "@meta4", false) +--macro addGlobalMetadata("Main.MainSub", "@meta5", true) \ No newline at end of file diff --git a/tests/misc/projects/Issue10245/compile-fail.hxml.stderr b/tests/misc/projects/Issue10245/compile-fail.hxml.stderr new file mode 100644 index 00000000..c050b7d0 --- /dev/null +++ b/tests/misc/projects/Issue10245/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:2: characters 22-28 : Recursive class \ No newline at end of file diff --git a/tests/misc/projects/Issue10264/Main.hx b/tests/misc/projects/Issue10264/Main.hx new file mode 100644 index 00000000..a231aba4 --- /dev/null +++ b/tests/misc/projects/Issue10264/Main.hx @@ -0,0 +1,10 @@ +function main() { + var d:Dynamic = null; + var v:MacroFrom = d; +} + +abstract MacroFrom(String) to String { + @:from + public static macro function fromExpr(e:haxe.macro.Expr) + return macro ($e:Main.MacroFrom); +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10264/compile-fail.hxml b/tests/misc/projects/Issue10264/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10264/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10264/compile-fail.hxml.stderr b/tests/misc/projects/Issue10264/compile-fail.hxml.stderr new file mode 100644 index 00000000..ba58352a --- /dev/null +++ b/tests/misc/projects/Issue10264/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:9: characters 16-35 : Recursive implicit cast \ No newline at end of file diff --git a/tests/misc/projects/Issue10291/Main.hx b/tests/misc/projects/Issue10291/Main.hx new file mode 100644 index 00000000..6dda4562 --- /dev/null +++ b/tests/misc/projects/Issue10291/Main.hx @@ -0,0 +1,15 @@ +import haxe.ds.Option; + +class Main { + static function main() { + var o = Some({b: null}); + switch (o) { + case Some({b: b}) if (b != null): + trace("Case 1: b=" + b); + case Some({b: null}): + trace("Case 2: b=null"); + case None: // Change None to _ and it works + trace("Default"); + } + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10291/compile-fail.hxml b/tests/misc/projects/Issue10291/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10291/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10291/compile-fail.hxml.stderr b/tests/misc/projects/Issue10291/compile-fail.hxml.stderr new file mode 100644 index 00000000..ded0e08c --- /dev/null +++ b/tests/misc/projects/Issue10291/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:9: characters 14-23 : Unmatched patterns: Some(_) \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/Main.hx b/tests/misc/projects/Issue10358/Main.hx new file mode 100644 index 00000000..5894212d --- /dev/null +++ b/tests/misc/projects/Issue10358/Main.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + var final; + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/Main2.hx b/tests/misc/projects/Issue10358/Main2.hx new file mode 100644 index 00000000..532520cd --- /dev/null +++ b/tests/misc/projects/Issue10358/Main2.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + var not, final; + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/Main3.hx b/tests/misc/projects/Issue10358/Main3.hx new file mode 100644 index 00000000..e649ff1c --- /dev/null +++ b/tests/misc/projects/Issue10358/Main3.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + var final = 0; + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile-fail.hxml b/tests/misc/projects/Issue10358/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10358/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile-fail.hxml.stderr b/tests/misc/projects/Issue10358/compile-fail.hxml.stderr new file mode 100644 index 00000000..8ff3c6f6 --- /dev/null +++ b/tests/misc/projects/Issue10358/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:3: characters 7-12 : Keyword final cannot be used as variable name \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile2-fail.hxml b/tests/misc/projects/Issue10358/compile2-fail.hxml new file mode 100644 index 00000000..9ef4475e --- /dev/null +++ b/tests/misc/projects/Issue10358/compile2-fail.hxml @@ -0,0 +1 @@ +--main Main2 \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile2-fail.hxml.stderr b/tests/misc/projects/Issue10358/compile2-fail.hxml.stderr new file mode 100644 index 00000000..d81ac3d5 --- /dev/null +++ b/tests/misc/projects/Issue10358/compile2-fail.hxml.stderr @@ -0,0 +1 @@ +Main2.hx:3: characters 12-17 : Keyword final cannot be used as variable name \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile3-fail.hxml b/tests/misc/projects/Issue10358/compile3-fail.hxml new file mode 100644 index 00000000..25e95386 --- /dev/null +++ b/tests/misc/projects/Issue10358/compile3-fail.hxml @@ -0,0 +1 @@ +--main Main3 \ No newline at end of file diff --git a/tests/misc/projects/Issue10358/compile3-fail.hxml.stderr b/tests/misc/projects/Issue10358/compile3-fail.hxml.stderr new file mode 100644 index 00000000..b6bf0e09 --- /dev/null +++ b/tests/misc/projects/Issue10358/compile3-fail.hxml.stderr @@ -0,0 +1 @@ +Main3.hx:3: characters 7-12 : Keyword final cannot be used as variable name \ No newline at end of file diff --git a/tests/misc/projects/Issue10426/Main.hx b/tests/misc/projects/Issue10426/Main.hx new file mode 100644 index 00000000..7400070c --- /dev/null +++ b/tests/misc/projects/Issue10426/Main.hx @@ -0,0 +1,7 @@ +class Main extends Abstr { + static function main() {} +} + +abstract class Abstr { + abstract function new():Void; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10426/compile-fail.hxml b/tests/misc/projects/Issue10426/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10426/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10426/compile-fail.hxml.stderr b/tests/misc/projects/Issue10426/compile-fail.hxml.stderr new file mode 100644 index 00000000..4695db76 --- /dev/null +++ b/tests/misc/projects/Issue10426/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:6: characters 2-10 : Invalid modifier: abstract on constructor \ No newline at end of file diff --git a/tests/misc/projects/Issue10482/Main.hx b/tests/misc/projects/Issue10482/Main.hx new file mode 100644 index 00000000..8f395e29 --- /dev/null +++ b/tests/misc/projects/Issue10482/Main.hx @@ -0,0 +1,7 @@ +class Main { + static function main() { + abstract; + abstract(); + abstract + 1; + } +} diff --git a/tests/misc/projects/Issue10482/compile-fail.hxml b/tests/misc/projects/Issue10482/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10482/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10482/compile-fail.hxml.stderr b/tests/misc/projects/Issue10482/compile-fail.hxml.stderr new file mode 100644 index 00000000..77473a5b --- /dev/null +++ b/tests/misc/projects/Issue10482/compile-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main.hx:3: characters 3-11 : Property 'abstract' is reserved and only available in abstracts +Main.hx:4: characters 3-11 : Property 'abstract' is reserved and only available in abstracts +Main.hx:5: characters 3-11 : Property 'abstract' is reserved and only available in abstracts diff --git a/tests/misc/projects/Issue10504/Main.hx b/tests/misc/projects/Issue10504/Main.hx new file mode 100644 index 00000000..85f6e7c3 --- /dev/null +++ b/tests/misc/projects/Issue10504/Main.hx @@ -0,0 +1,12 @@ +class Main { + static function main() { + var v = { a : 1, foo : null }; + $type(v); + parse1(v); + $type(v); + parse2(v); + } + + extern static function parse1( v : { a : Int, ?b:Int } ):Void; + extern static function parse2( v : { a : Int, ?b:String } ):Void; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10504/compile-fail.hxml b/tests/misc/projects/Issue10504/compile-fail.hxml new file mode 100644 index 00000000..fab0aeec --- /dev/null +++ b/tests/misc/projects/Issue10504/compile-fail.hxml @@ -0,0 +1 @@ +--main Main \ No newline at end of file diff --git a/tests/misc/projects/Issue10504/compile-fail.hxml.stderr b/tests/misc/projects/Issue10504/compile-fail.hxml.stderr new file mode 100644 index 00000000..7f2f5bd2 --- /dev/null +++ b/tests/misc/projects/Issue10504/compile-fail.hxml.stderr @@ -0,0 +1,5 @@ +Main.hx:4: characters 9-10 : Warning : { foo : Null>, a : Int } +Main.hx:6: characters 9-10 : Warning : { foo : Null>, ?b : Null, a : Int } +Main.hx:7: characters 10-11 : error: Int should be String +Main.hx:7: characters 10-11 : ... have: { b: Int } +Main.hx:7: characters 10-11 : ... want: { b: String } diff --git a/tests/misc/projects/Issue10521/Main.hx b/tests/misc/projects/Issue10521/Main.hx new file mode 100644 index 00000000..c29ffd05 --- /dev/null +++ b/tests/misc/projects/Issue10521/Main.hx @@ -0,0 +1,22 @@ +class Main { + static function main() { + new Bar(1, 2); + } +} + +class Foo { + var x:Int; + var y:Int; + + public function new(x:Int, y:Int) { + this.x = x; + this.y = y; + } +} + +class Bar extends Foo { + public function new(x:Int, y:Int) { + super(x, y); + var foo = new Foo(super.x, super.y); + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10521/compile-fail.hxml b/tests/misc/projects/Issue10521/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10521/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10521/compile-fail.hxml.stderr b/tests/misc/projects/Issue10521/compile-fail.hxml.stderr new file mode 100644 index 00000000..9f660eb1 --- /dev/null +++ b/tests/misc/projects/Issue10521/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:20: characters 27-28 : Normal variables cannot be accessed with 'super', use 'this' instead +Main.hx:20: characters 36-37 : Normal variables cannot be accessed with 'super', use 'this' instead \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/Macro.hx b/tests/misc/projects/Issue10587/Macro.hx new file mode 100644 index 00000000..e30554a8 --- /dev/null +++ b/tests/misc/projects/Issue10587/Macro.hx @@ -0,0 +1,3 @@ +class Macro { + public static macro function foo(e:Expr) {} +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/MainImport.hx b/tests/misc/projects/Issue10587/MainImport.hx new file mode 100644 index 00000000..55eb4b38 --- /dev/null +++ b/tests/misc/projects/Issue10587/MainImport.hx @@ -0,0 +1,7 @@ +import Macro; + +class Main { + static function main() { + Macro.foo( 1 ); + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/MainUsing.hx b/tests/misc/projects/Issue10587/MainUsing.hx new file mode 100644 index 00000000..e739782a --- /dev/null +++ b/tests/misc/projects/Issue10587/MainUsing.hx @@ -0,0 +1,7 @@ +using Macro; + +class Main { + static function main() { + 1.foo(); + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/compile-import-fail.hxml b/tests/misc/projects/Issue10587/compile-import-fail.hxml new file mode 100644 index 00000000..0ce6c407 --- /dev/null +++ b/tests/misc/projects/Issue10587/compile-import-fail.hxml @@ -0,0 +1,2 @@ +--main MainImport +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/compile-import-fail.hxml.stderr b/tests/misc/projects/Issue10587/compile-import-fail.hxml.stderr new file mode 100644 index 00000000..9506f064 --- /dev/null +++ b/tests/misc/projects/Issue10587/compile-import-fail.hxml.stderr @@ -0,0 +1 @@ +Macro.hx:2: characters 37-41 : Type not found : Expr \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/compile-using-fail.hxml b/tests/misc/projects/Issue10587/compile-using-fail.hxml new file mode 100644 index 00000000..d2a71e00 --- /dev/null +++ b/tests/misc/projects/Issue10587/compile-using-fail.hxml @@ -0,0 +1,2 @@ +--main MainUsing +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10587/compile-using-fail.hxml.stderr b/tests/misc/projects/Issue10587/compile-using-fail.hxml.stderr new file mode 100644 index 00000000..9506f064 --- /dev/null +++ b/tests/misc/projects/Issue10587/compile-using-fail.hxml.stderr @@ -0,0 +1 @@ +Macro.hx:2: characters 37-41 : Type not found : Expr \ No newline at end of file diff --git a/tests/misc/projects/Issue10591/Main.hx b/tests/misc/projects/Issue10591/Main.hx new file mode 100644 index 00000000..bdec4121 --- /dev/null +++ b/tests/misc/projects/Issue10591/Main.hx @@ -0,0 +1,27 @@ +import haxe.rtti.Meta; +using Lambda; + + +class MainSub {} + +class Main { + static function main() { + function hf(t, s) { + Sys.stderr().writeString('$s: ${Reflect.hasField(t, s)}\n'); + } + + final numMetas = 6; + + var check = [ + { name: "Main", meta: Meta.getType(Main) }, + { name: "MainSub", meta: Meta.getType(MainSub) } + ]; + + for (item in check) { + Sys.stderr().writeString(item.name + "\n"); + for (i in 0...numMetas) { + hf(item.meta, 'meta$i'); + } + } + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10591/compile.hxml b/tests/misc/projects/Issue10591/compile.hxml new file mode 100644 index 00000000..c849dc5d --- /dev/null +++ b/tests/misc/projects/Issue10591/compile.hxml @@ -0,0 +1,8 @@ +--main Main +--interp +--macro addGlobalMetadata("", "@meta0", false) +--macro addGlobalMetadata("", "@meta1", true) +--macro addGlobalMetadata("Main", "@meta2", false) +--macro addGlobalMetadata("Main", "@meta3", true) +--macro addGlobalMetadata("Main.MainSub", "@meta4", false) +--macro addGlobalMetadata("Main.MainSub", "@meta5", true) \ No newline at end of file diff --git a/tests/misc/projects/Issue10591/compile.hxml.stderr b/tests/misc/projects/Issue10591/compile.hxml.stderr new file mode 100644 index 00000000..4debff81 --- /dev/null +++ b/tests/misc/projects/Issue10591/compile.hxml.stderr @@ -0,0 +1,14 @@ +Main +meta0: true +meta1: true +meta2: true +meta3: true +meta4: false +meta5: false +MainSub +meta0: false +meta1: true +meta2: false +meta3: true +meta4: true +meta5: true \ No newline at end of file diff --git a/tests/misc/projects/Issue10623/Main.hx b/tests/misc/projects/Issue10623/Main.hx new file mode 100644 index 00000000..8872e391 --- /dev/null +++ b/tests/misc/projects/Issue10623/Main.hx @@ -0,0 +1,7 @@ +class Main { + static function main() { + var data = haxe.io.Bytes.alloc(2); + data.set(0, 0xFF); + data.toString().substr(0); + } +} diff --git a/tests/misc/projects/Issue10623/compile-fail.hxml b/tests/misc/projects/Issue10623/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10623/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10623/compile-fail.hxml.stderr b/tests/misc/projects/Issue10623/compile-fail.hxml.stderr new file mode 100644 index 00000000..9d78f618 --- /dev/null +++ b/tests/misc/projects/Issue10623/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Uncaught exception Invalid string +Main.hx:5: characters 3-18 : Called from here \ No newline at end of file diff --git a/tests/misc/projects/Issue10623/indent-fail.hxml b/tests/misc/projects/Issue10623/indent-fail.hxml new file mode 100644 index 00000000..1a2ff3dc --- /dev/null +++ b/tests/misc/projects/Issue10623/indent-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue10623/indent-fail.hxml.stderr b/tests/misc/projects/Issue10623/indent-fail.hxml.stderr new file mode 100644 index 00000000..0e5c4ba4 --- /dev/null +++ b/tests/misc/projects/Issue10623/indent-fail.hxml.stderr @@ -0,0 +1,2 @@ +Uncaught exception Invalid string + Main.hx:5: characters 3-18 : Called from here diff --git a/tests/misc/projects/Issue10623/pretty-fail.hxml b/tests/misc/projects/Issue10623/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue10623/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue10623/pretty-fail.hxml.stderr b/tests/misc/projects/Issue10623/pretty-fail.hxml.stderr new file mode 100644 index 00000000..f8ec7351 --- /dev/null +++ b/tests/misc/projects/Issue10623/pretty-fail.hxml.stderr @@ -0,0 +1,10 @@ +[ERROR] (unknown position) + + | Uncaught exception Invalid string + + -> Main.hx:5: characters 3-18 + + 5 | data.toString().substr(0); + | ^^^^^^^^^^^^^^^ + | Called from here + diff --git a/tests/misc/projects/Issue10687/Main.hx b/tests/misc/projects/Issue10687/Main.hx new file mode 100644 index 00000000..af1b823d --- /dev/null +++ b/tests/misc/projects/Issue10687/Main.hx @@ -0,0 +1,24 @@ +class Main { + var foo = new Foo(); + var fooExtern = new Foo(); + var fooAb = new FooExtern(); + var vector = new haxe.ds.Vector(1); + var vector2 = new haxe.ds.Vector(1, 0); + static function main() {} +} + +class Foo { + public var length = Std.random(1); + public inline function new() {} +} + +class FooExtern { + public var length = Std.random(1); + extern public inline function new() {} +} + +abstract FooAb({length:Int}) { + extern public inline function new() { + this = {length: Std.random(1)}; + } +} diff --git a/tests/misc/projects/Issue10687/compile-fail.hxml b/tests/misc/projects/Issue10687/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10687/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10687/compile-fail.hxml.stderr b/tests/misc/projects/Issue10687/compile-fail.hxml.stderr new file mode 100644 index 00000000..553b3b08 --- /dev/null +++ b/tests/misc/projects/Issue10687/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 14-29 : Extern constructor could not be inlined diff --git a/tests/misc/projects/Issue10711/Main.hx b/tests/misc/projects/Issue10711/Main.hx new file mode 100644 index 00000000..7c0f028c --- /dev/null +++ b/tests/misc/projects/Issue10711/Main.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + var x:X; + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10711/compile.hxml b/tests/misc/projects/Issue10711/compile.hxml new file mode 100644 index 00000000..4f47646c --- /dev/null +++ b/tests/misc/projects/Issue10711/compile.hxml @@ -0,0 +1,7 @@ +--cmd haxelib dev lib1 lib1 +--cmd haxelib dev lib2 lib2 +--next +--main Main +-lib lib1 +-lib lib2 +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10711/compile.hxml.stderr b/tests/misc/projects/Issue10711/compile.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10711/lib1/X.hx b/tests/misc/projects/Issue10711/lib1/X.hx new file mode 100644 index 00000000..6445722e --- /dev/null +++ b/tests/misc/projects/Issue10711/lib1/X.hx @@ -0,0 +1 @@ +class X {} \ No newline at end of file diff --git a/tests/misc/projects/Issue10711/lib2/X.hx b/tests/misc/projects/Issue10711/lib2/X.hx new file mode 100644 index 00000000..8ce55b99 --- /dev/null +++ b/tests/misc/projects/Issue10711/lib2/X.hx @@ -0,0 +1 @@ +class X {} \ No newline at end of file diff --git a/tests/misc/projects/Issue10729/Main.hx b/tests/misc/projects/Issue10729/Main.hx new file mode 100644 index 00000000..d1c6cd2c --- /dev/null +++ b/tests/misc/projects/Issue10729/Main.hx @@ -0,0 +1,15 @@ +interface IA { + public var a:String; +} + +abstract class A implements IA { + public function new() {} +} + +class B extends A { + public function new() { + super(); + } +} + +function main() {} diff --git a/tests/misc/projects/Issue10729/compile-fail.hxml b/tests/misc/projects/Issue10729/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10729/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10729/compile-fail.hxml.stderr b/tests/misc/projects/Issue10729/compile-fail.hxml.stderr new file mode 100644 index 00000000..030a8d28 --- /dev/null +++ b/tests/misc/projects/Issue10729/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:5: characters 16-17 : Field a needed by IA is missing \ No newline at end of file diff --git a/tests/misc/projects/Issue10739/Main.hx b/tests/misc/projects/Issue10739/Main.hx new file mode 100644 index 00000000..2c529352 --- /dev/null +++ b/tests/misc/projects/Issue10739/Main.hx @@ -0,0 +1,50 @@ +class Main { + static function main() { + getAnswer().next(v -> switch v { + case 42: 'Perfect!'; + case 40, 41, 43, 44: 'Close enough!'; + default: new NotError(); + }).handle(o -> trace(Std.string(o))); + } + + static function getAnswer():Promise + return 3 + 39; +} + +class NotError { + public function new() {} +} + +abstract Promise((handler:(result:Outcome) -> Void) -> Void) { + inline function new(f) + this = f; + + public function next(transform:Next):Promise + return new Promise(handler -> this(o -> switch o { + case Success(v): transform(v).handle(handler); + case Failure(e): handler(Failure(e)); + })); + + @:from static function ofOutcome(o:Outcome):Promise + return new Promise(h -> h(o)); + + @:from static function ofValue(v:T):Promise + return ofOutcome(Success(v)); + + @:from static function ofError(e:NotError):Promise + return ofOutcome(Failure(e)); + + public function handle(cb) + this(cb); +} + +@:callable +abstract Next(In->Promise) from In->Promise { + @:from(ignoredByInference) static function ofSync(f:In->Out):Next + return v -> (f(v) : Promise); +} + +enum Outcome { + Success(data:T); + Failure(error:E); +} diff --git a/tests/misc/projects/Issue10739/compile.hxml b/tests/misc/projects/Issue10739/compile.hxml new file mode 100644 index 00000000..a711e039 --- /dev/null +++ b/tests/misc/projects/Issue10739/compile.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp diff --git a/tests/misc/projects/Issue10739/compile.hxml.stdout b/tests/misc/projects/Issue10739/compile.hxml.stdout new file mode 100644 index 00000000..5d4c016d --- /dev/null +++ b/tests/misc/projects/Issue10739/compile.hxml.stdout @@ -0,0 +1 @@ +Main.hx:7: Success(Perfect!) \ No newline at end of file diff --git a/tests/misc/projects/Issue10793/Main.hx b/tests/misc/projects/Issue10793/Main.hx new file mode 100644 index 00000000..a511b30f --- /dev/null +++ b/tests/misc/projects/Issue10793/Main.hx @@ -0,0 +1,25 @@ +import haxe.macro.Context; +import haxe.macro.Expr; + +using haxe.macro.Tools; + +abstract Foo(Dynamic) from Dynamic to Dynamic { + @:op(A.B) function get(prop:String) { + return Reflect.field(this, prop); + } + + @:op(A.B) macro function set(obj:Expr, prop:Expr, val:Expr) { + var s = Context.getTypedExpr(Context.typeExpr(obj)).toString(); + Sys.stderr().writeString(s); + return macro $val; + } +} + +class Main { + static function main() { + var foo:Foo = { + n: 5 + }; + foo.n += 4; + } +} diff --git a/tests/misc/projects/Issue10793/compile.hxml b/tests/misc/projects/Issue10793/compile.hxml new file mode 100644 index 00000000..335f36ae --- /dev/null +++ b/tests/misc/projects/Issue10793/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--interp +-D warn-var-shadowing \ No newline at end of file diff --git a/tests/misc/projects/Issue10793/compile.hxml.stderr b/tests/misc/projects/Issue10793/compile.hxml.stderr new file mode 100644 index 00000000..19102815 --- /dev/null +++ b/tests/misc/projects/Issue10793/compile.hxml.stderr @@ -0,0 +1 @@ +foo \ No newline at end of file diff --git a/tests/misc/projects/Issue10804/Main.hx b/tests/misc/projects/Issue10804/Main.hx new file mode 100644 index 00000000..ab3f7eaa --- /dev/null +++ b/tests/misc/projects/Issue10804/Main.hx @@ -0,0 +1,3 @@ +function main() { + new haxe.ds.Vector(-1); +} diff --git a/tests/misc/projects/Issue10804/Main2.hx b/tests/misc/projects/Issue10804/Main2.hx new file mode 100644 index 00000000..f40fa946 --- /dev/null +++ b/tests/misc/projects/Issue10804/Main2.hx @@ -0,0 +1,7 @@ +function getMinusOne() { + return -1; +} + +function main() { + new haxe.ds.Vector(getMinusOne()); +} diff --git a/tests/misc/projects/Issue10804/compile-fail.hxml b/tests/misc/projects/Issue10804/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10804/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10804/compile-fail.hxml.stderr b/tests/misc/projects/Issue10804/compile-fail.hxml.stderr new file mode 100644 index 00000000..e9555b9c --- /dev/null +++ b/tests/misc/projects/Issue10804/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:2: characters 21-23 : Uncaught exception Vector size must be >= 0 \ No newline at end of file diff --git a/tests/misc/projects/Issue10804/compile2-fail.hxml b/tests/misc/projects/Issue10804/compile2-fail.hxml new file mode 100644 index 00000000..47f833e8 --- /dev/null +++ b/tests/misc/projects/Issue10804/compile2-fail.hxml @@ -0,0 +1,2 @@ +--main Main2 +--interp diff --git a/tests/misc/projects/Issue10804/compile2-fail.hxml.stderr b/tests/misc/projects/Issue10804/compile2-fail.hxml.stderr new file mode 100644 index 00000000..a00963e6 --- /dev/null +++ b/tests/misc/projects/Issue10804/compile2-fail.hxml.stderr @@ -0,0 +1 @@ +Main2.hx:6: characters 21-34 : Uncaught exception Vector size must be >= 0 \ No newline at end of file diff --git a/tests/misc/projects/Issue10819/Main.hx b/tests/misc/projects/Issue10819/Main.hx new file mode 100644 index 00000000..477e3f72 --- /dev/null +++ b/tests/misc/projects/Issue10819/Main.hx @@ -0,0 +1,10 @@ +class Main { + static var a:Array = [1]; + + static function main() { + switch a { // <--- Warning : (WVarShadow) This variable shadows a previously declared variable + case [1]: + case _: + } + } +} diff --git a/tests/misc/projects/Issue10819/compile.hxml b/tests/misc/projects/Issue10819/compile.hxml new file mode 100644 index 00000000..335f36ae --- /dev/null +++ b/tests/misc/projects/Issue10819/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--interp +-D warn-var-shadowing \ No newline at end of file diff --git a/tests/misc/projects/Issue10819/compile.hxml.stderr b/tests/misc/projects/Issue10819/compile.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10825/Main.hx b/tests/misc/projects/Issue10825/Main.hx new file mode 100644 index 00000000..f97b96af --- /dev/null +++ b/tests/misc/projects/Issue10825/Main.hx @@ -0,0 +1,14 @@ +class A { + public function new() {} + + public final function run() { + trace("running"); + } +} + +class Main { + static function main() { + final a:A = null; + a.run(); + } +} diff --git a/tests/misc/projects/Issue10825/compile-fail.hxml b/tests/misc/projects/Issue10825/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10825/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10825/compile-fail.hxml.stderr b/tests/misc/projects/Issue10825/compile-fail.hxml.stderr new file mode 100644 index 00000000..1dae708c --- /dev/null +++ b/tests/misc/projects/Issue10825/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:12: characters 3-4 : Uncaught exception Null Access \ No newline at end of file diff --git a/tests/misc/projects/Issue10832/Main.hx b/tests/misc/projects/Issue10832/Main.hx new file mode 100644 index 00000000..db5d9792 --- /dev/null +++ b/tests/misc/projects/Issue10832/Main.hx @@ -0,0 +1,21 @@ +import haxe.MainLoop; + +class Main { + static function main() { + trace('main'); + var event = null; + var done = false; + event = MainLoop.add( + () -> { + if(!done){ + done = true; + trace('ok'); + } + if(event !=null){ + trace("stop"); + event.stop(); + } + } + ); + } +} diff --git a/tests/misc/projects/Issue10832/compile.hxml b/tests/misc/projects/Issue10832/compile.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10832/compile.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10832/compile.hxml.stdout b/tests/misc/projects/Issue10832/compile.hxml.stdout new file mode 100644 index 00000000..84cf52d4 --- /dev/null +++ b/tests/misc/projects/Issue10832/compile.hxml.stdout @@ -0,0 +1,3 @@ +Main.hx:5: main +Main.hx:12: ok +Main.hx:15: stop diff --git a/tests/misc/projects/Issue10844/Fail.hx b/tests/misc/projects/Issue10844/Fail.hx new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10844/aaa-setup.hxml b/tests/misc/projects/Issue10844/aaa-setup.hxml new file mode 100644 index 00000000..a91615a9 --- /dev/null +++ b/tests/misc/projects/Issue10844/aaa-setup.hxml @@ -0,0 +1,2 @@ +--cmd haxelib dev dummy_doc_dep dummy_doc_dep +--cmd haxelib dev dummy_doc dummy_doc diff --git a/tests/misc/projects/Issue10844/base-fail.hxml b/tests/misc/projects/Issue10844/base-fail.hxml new file mode 100644 index 00000000..bb003708 --- /dev/null +++ b/tests/misc/projects/Issue10844/base-fail.hxml @@ -0,0 +1,2 @@ +-lib dummy_doc +--main Fail diff --git a/tests/misc/projects/Issue10844/custom-define-nofail.hxml b/tests/misc/projects/Issue10844/custom-define-nofail.hxml new file mode 100644 index 00000000..651032e0 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-define-nofail.hxml @@ -0,0 +1,2 @@ +base-fail.hxml +--help-user-defines diff --git a/tests/misc/projects/Issue10844/custom-define-nofail.hxml.stdout b/tests/misc/projects/Issue10844/custom-define-nofail.hxml.stdout new file mode 100644 index 00000000..d0a79c35 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-define-nofail.hxml.stdout @@ -0,0 +1,2 @@ + dummy : On a scale from 0 to 10 (from dummy_doc_dep) + no-bullshit: Only very important stuff should be compiled (from dummy_doc) diff --git a/tests/misc/projects/Issue10844/custom-define.hxml b/tests/misc/projects/Issue10844/custom-define.hxml new file mode 100644 index 00000000..6b93b164 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-define.hxml @@ -0,0 +1,2 @@ +-lib dummy_doc +--help-user-defines diff --git a/tests/misc/projects/Issue10844/custom-define.hxml.stdout b/tests/misc/projects/Issue10844/custom-define.hxml.stdout new file mode 100644 index 00000000..d0a79c35 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-define.hxml.stdout @@ -0,0 +1,2 @@ + dummy : On a scale from 0 to 10 (from dummy_doc_dep) + no-bullshit: Only very important stuff should be compiled (from dummy_doc) diff --git a/tests/misc/projects/Issue10844/custom-meta-nofail.hxml b/tests/misc/projects/Issue10844/custom-meta-nofail.hxml new file mode 100644 index 00000000..98f6a86e --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-meta-nofail.hxml @@ -0,0 +1,2 @@ +base-fail.hxml +--help-user-metas diff --git a/tests/misc/projects/Issue10844/custom-meta-nofail.hxml.stdout b/tests/misc/projects/Issue10844/custom-meta-nofail.hxml.stdout new file mode 100644 index 00000000..4e16e3d9 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-meta-nofail.hxml.stdout @@ -0,0 +1,5 @@ + @:bar : Some documentation for the TClass metadata '@:bar' (from dummy_doc) + @:baz : Some documentation for the @:baz metadata (from dummy_doc_dep) + @:foo : Some documentation for the @:foo metadata for cpp platform (cpp + only) (from dummy_doc) + @:foobar: Some documentation for @:foobar (cpp only) (from dummy_doc) diff --git a/tests/misc/projects/Issue10844/custom-meta.hxml b/tests/misc/projects/Issue10844/custom-meta.hxml new file mode 100644 index 00000000..40d28568 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-meta.hxml @@ -0,0 +1,2 @@ +-lib dummy_doc +--help-user-metas diff --git a/tests/misc/projects/Issue10844/custom-meta.hxml.stdout b/tests/misc/projects/Issue10844/custom-meta.hxml.stdout new file mode 100644 index 00000000..4e16e3d9 --- /dev/null +++ b/tests/misc/projects/Issue10844/custom-meta.hxml.stdout @@ -0,0 +1,5 @@ + @:bar : Some documentation for the TClass metadata '@:bar' (from dummy_doc) + @:baz : Some documentation for the @:baz metadata (from dummy_doc_dep) + @:foo : Some documentation for the @:foo metadata for cpp platform (cpp + only) (from dummy_doc) + @:foobar: Some documentation for @:foobar (cpp only) (from dummy_doc) diff --git a/tests/misc/projects/Issue10844/define.json b/tests/misc/projects/Issue10844/define.json new file mode 100644 index 00000000..8bec1b81 --- /dev/null +++ b/tests/misc/projects/Issue10844/define.json @@ -0,0 +1,6 @@ +[ + { + "define": "fromjson", + "doc": "Custom user define from json" + } +] diff --git a/tests/misc/projects/Issue10844/dummy_doc/doc/define.json b/tests/misc/projects/Issue10844/dummy_doc/doc/define.json new file mode 100644 index 00000000..247174db --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc/doc/define.json @@ -0,0 +1,6 @@ +[ + { + "define": "no-bullshit", + "doc": "Only very important stuff should be compiled" + } +] diff --git a/tests/misc/projects/Issue10844/dummy_doc/doc/meta.json b/tests/misc/projects/Issue10844/dummy_doc/doc/meta.json new file mode 100644 index 00000000..17ee9396 --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc/doc/meta.json @@ -0,0 +1,17 @@ +[ + { + "metadata": ":foo", + "doc": "Some documentation for the @:foo metadata for cpp platform", + "platforms": ["cpp"] + }, + { + "metadata": ":bar", + "doc": "Some documentation for the TClass metadata '@:bar'", + "targets": ["TClass"] + }, + { + "metadata": ":foobar", + "doc": "Some documentation for @:foobar", + "platforms": ["cpp"] + } +] diff --git a/tests/misc/projects/Issue10844/dummy_doc/haxelib.json b/tests/misc/projects/Issue10844/dummy_doc/haxelib.json new file mode 100644 index 00000000..8642f7eb --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc/haxelib.json @@ -0,0 +1,16 @@ +{ + "name": "dummy_doc", + "url" : "http://example.org", + "license": "GPL", + "description": "This is a dummy lib for test purposes", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json", + "defines": "doc/define.json" + }, + "contributors": ["haxe"], + "dependencies": { + "dummy_doc_dep": "*" + } +} diff --git a/tests/misc/projects/Issue10844/dummy_doc_dep/doc/define.json b/tests/misc/projects/Issue10844/dummy_doc_dep/doc/define.json new file mode 100644 index 00000000..0bc7f409 --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc_dep/doc/define.json @@ -0,0 +1,7 @@ +[ + { + "define": "dummy", + "doc": "On a scale from 0 to 10", + "params": ["level: 10 | 11"] + } +] diff --git a/tests/misc/projects/Issue10844/dummy_doc_dep/doc/meta.json b/tests/misc/projects/Issue10844/dummy_doc_dep/doc/meta.json new file mode 100644 index 00000000..1e2c38d6 --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc_dep/doc/meta.json @@ -0,0 +1,6 @@ +[ + { + "metadata": ":baz", + "doc": "Some documentation for the @:baz metadata" + } +] diff --git a/tests/misc/projects/Issue10844/dummy_doc_dep/haxelib.json b/tests/misc/projects/Issue10844/dummy_doc_dep/haxelib.json new file mode 100644 index 00000000..857cd676 --- /dev/null +++ b/tests/misc/projects/Issue10844/dummy_doc_dep/haxelib.json @@ -0,0 +1,13 @@ +{ + "name": "dummy_doc_dep", + "url" : "http://example.org", + "license": "GPL", + "description": "This is a dummy lib for test purposes", + "version": "1.0.0", + "releasenote": "Initial release, everything is working correctly", + "documentation": { + "metadata": "doc/meta.json", + "defines": "doc/define.json" + }, + "contributors": ["haxe"] +} diff --git a/tests/misc/projects/Issue10844/meta.json b/tests/misc/projects/Issue10844/meta.json new file mode 100644 index 00000000..ee35c7fd --- /dev/null +++ b/tests/misc/projects/Issue10844/meta.json @@ -0,0 +1,6 @@ +[ + { + "metadata": ":fromjson", + "doc": "Custom user metadata from json" + } +] diff --git a/tests/misc/projects/Issue10844/user-defined-define-fail.hxml b/tests/misc/projects/Issue10844/user-defined-define-fail.hxml new file mode 100644 index 00000000..80e628bd --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-define-fail.hxml @@ -0,0 +1,2 @@ +--macro haxe.macro.Compiler.registerCustomDefine({dfine: 'zzz', doc: 'something'}, 'myapp') +--help-user-defines diff --git a/tests/misc/projects/Issue10844/user-defined-define-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-define-fail.hxml.stderr new file mode 100644 index 00000000..82e29f77 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-define-fail.hxml.stderr @@ -0,0 +1,2 @@ +(unknown) : Object requires field define +(unknown) : ... For function argument 'define' diff --git a/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml b/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml new file mode 100644 index 00000000..f6dfdf2a --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml @@ -0,0 +1,2 @@ +--macro haxe.macro.Compiler.registerDefinesDescriptionFile('define.jsno', 'myapp') +--help-user-defines diff --git a/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr new file mode 100644 index 00000000..ece15c4e --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-define-json-fail.hxml.stderr @@ -0,0 +1,3 @@ +(unknown) : Uncaught exception Could not read file define.jsno +$$normPath(::std::)/haxe/macro/Compiler.hx:493: characters 11-39 : Called from here +(unknown) : Called from here diff --git a/tests/misc/projects/Issue10844/user-defined-defines.hxml b/tests/misc/projects/Issue10844/user-defined-defines.hxml new file mode 100644 index 00000000..cc85a5b5 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-defines.hxml @@ -0,0 +1,4 @@ +-lib dummy_doc +--macro haxe.macro.Compiler.registerDefinesDescriptionFile('define.json', 'myapp') +--macro haxe.macro.Compiler.registerCustomDefine({define: 'custom', doc: 'Some user define'}, 'myapp') +--help-user-defines diff --git a/tests/misc/projects/Issue10844/user-defined-defines.hxml.stdout b/tests/misc/projects/Issue10844/user-defined-defines.hxml.stdout new file mode 100644 index 00000000..0ae0b1de --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-defines.hxml.stdout @@ -0,0 +1,4 @@ + custom : Some user define (from myapp) + dummy : On a scale from 0 to 10 (from dummy_doc_dep) + fromjson : Custom user define from json (from myapp) + no-bullshit: Only very important stuff should be compiled (from dummy_doc) diff --git a/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml new file mode 100644 index 00000000..b65b3b2a --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml @@ -0,0 +1,2 @@ +--macro haxe.macro.Compiler.registerCustomMetadata({metdata: ':zzz', doc: 'something'}, 'myapp') +--help-user-metas diff --git a/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml.stderr new file mode 100644 index 00000000..eea038e4 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-fail.hxml.stderr @@ -0,0 +1,2 @@ +(unknown) : Object requires field metadata +(unknown) : ... For function argument 'meta' diff --git a/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml new file mode 100644 index 00000000..80f36755 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml @@ -0,0 +1,2 @@ +user-defined-meta-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr new file mode 100644 index 00000000..aba23ab1 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-indent-fail.hxml.stderr @@ -0,0 +1,2 @@ +(unknown) : Object requires field metadata + (unknown) : For function argument 'meta' diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml new file mode 100644 index 00000000..e3f78738 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml @@ -0,0 +1,2 @@ +--macro haxe.macro.Compiler.registerMetadataDescriptionFile('meta.jsno', 'myapp') +--help-user-metas diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr new file mode 100644 index 00000000..a5983384 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-fail.hxml.stderr @@ -0,0 +1,3 @@ +(unknown) : Uncaught exception Could not read file meta.jsno +$$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39 : Called from here +(unknown) : Called from here diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml new file mode 100644 index 00000000..2e3e74ec --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml @@ -0,0 +1,2 @@ +user-defined-meta-json-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr new file mode 100644 index 00000000..05432e4c --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-indent-fail.hxml.stderr @@ -0,0 +1,3 @@ +(unknown) : Uncaught exception Could not read file meta.jsno + $$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39 : Called from here + (unknown) : Called from here diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml new file mode 100644 index 00000000..05e35247 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml @@ -0,0 +1,4 @@ +user-defined-meta-json-fail.hxml +-D message-reporting=pretty +-D no-color + diff --git a/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr new file mode 100644 index 00000000..8646a409 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-json-pretty-fail.hxml.stderr @@ -0,0 +1,12 @@ +[ERROR] --macro haxe.macro.Compiler.registerMetadataDescriptionFile('meta.jsno', 'myapp') + + | Uncaught exception Could not read file meta.jsno + + -> $$normPath(::std::)/haxe/macro/Compiler.hx:483: characters 11-39 + + 483 | var f = sys.io.File.getContent(path); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Called from here + + | Called from here + diff --git a/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml b/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml new file mode 100644 index 00000000..1baed338 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml @@ -0,0 +1,4 @@ +user-defined-meta-fail.hxml +-D message-reporting=pretty +-D no-color + diff --git a/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr b/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr new file mode 100644 index 00000000..572f21e0 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-meta-pretty-fail.hxml.stderr @@ -0,0 +1,5 @@ +[ERROR] --macro haxe.macro.Compiler.registerCustomMetadata({metdata: ':zzz', doc: 'something'}, 'myapp') + + | Object requires field metadata + | For function argument 'meta' + diff --git a/tests/misc/projects/Issue10844/user-defined-metas.hxml b/tests/misc/projects/Issue10844/user-defined-metas.hxml new file mode 100644 index 00000000..856a444f --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-metas.hxml @@ -0,0 +1,4 @@ +-lib dummy_doc +--macro haxe.macro.Compiler.registerMetadataDescriptionFile('meta.json', 'myapp') +--macro haxe.macro.Compiler.registerCustomMetadata({metadata: ':custom', doc: 'Some user meta'}, 'myapp') +--help-user-metas diff --git a/tests/misc/projects/Issue10844/user-defined-metas.hxml.stdout b/tests/misc/projects/Issue10844/user-defined-metas.hxml.stdout new file mode 100644 index 00000000..c7e59453 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defined-metas.hxml.stdout @@ -0,0 +1,8 @@ + @:bar : Some documentation for the TClass metadata '@:bar' (from + dummy_doc) + @:baz : Some documentation for the @:baz metadata (from dummy_doc_dep) + @:custom : Some user meta (from myapp) + @:foo : Some documentation for the @:foo metadata for cpp platform (cpp + only) (from dummy_doc) + @:foobar : Some documentation for @:foobar (cpp only) (from dummy_doc) + @:fromjson: Custom user metadata from json (from myapp) diff --git a/tests/misc/projects/Issue10844/user-defines-did-something.hxml b/tests/misc/projects/Issue10844/user-defines-did-something.hxml new file mode 100644 index 00000000..0bc61cb2 --- /dev/null +++ b/tests/misc/projects/Issue10844/user-defines-did-something.hxml @@ -0,0 +1 @@ +--help-user-defines diff --git a/tests/misc/projects/Issue10844/user-defines-did-something.hxml.stdout b/tests/misc/projects/Issue10844/user-defines-did-something.hxml.stdout new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10844/user-metas-did-something.hxml b/tests/misc/projects/Issue10844/user-metas-did-something.hxml new file mode 100644 index 00000000..94f051cc --- /dev/null +++ b/tests/misc/projects/Issue10844/user-metas-did-something.hxml @@ -0,0 +1 @@ +--help-user-metas diff --git a/tests/misc/projects/Issue10844/user-metas-did-something.hxml.stdout b/tests/misc/projects/Issue10844/user-metas-did-something.hxml.stdout new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10845/Main.hx b/tests/misc/projects/Issue10845/Main.hx new file mode 100644 index 00000000..7e166129 --- /dev/null +++ b/tests/misc/projects/Issue10845/Main.hx @@ -0,0 +1,47 @@ +abstract MyInt(Int) from Int to Int { + @:op(A += B) + public inline function addEq(x:Int):MyInt { + return this += x; + } + + @:op(A++) + public inline function increment():MyInt { + return this++; + } +} + +class MyClass { + final field:MyInt = 1; + + function func():Void { + final local:MyInt = 1; + + // --- overriden operators --- + + field++; // Error: The field or identifier field is not accessible for writing + local++; + + field += 1; + local += 1; // Error: Cannot assign to final + + // --- raw operators --- + + field--; // Error: The field or identifier field is not accessible for writing + local--; // Error: Cannot assign to final + + field -= 1; // Error: Cannot access field or identifier field for writing + local -= 1; // Error: Cannot assign to final + + // --- method calls --- + + field.addEq(1); + local.addEq(1); + + field.increment(); + local.increment(); + } +} + +function main() { + +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10845/compile-fail.hxml b/tests/misc/projects/Issue10845/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10845/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10845/compile-fail.hxml.stderr b/tests/misc/projects/Issue10845/compile-fail.hxml.stderr new file mode 100644 index 00000000..410e4147 --- /dev/null +++ b/tests/misc/projects/Issue10845/compile-fail.hxml.stderr @@ -0,0 +1,12 @@ +Main.hx:21: characters 3-10 : Cannot modify abstract value of final field +Main.hx:22: characters 3-10 : Cannot modify abstract value of final local +Main.hx:24: characters 3-8 : Cannot modify abstract value of final field +Main.hx:25: characters 3-13 : Cannot modify abstract value of final local +Main.hx:29: characters 3-8 : This expression cannot be accessed for writing +Main.hx:30: characters 3-8 : Cannot assign to final +Main.hx:32: characters 3-8 : This expression cannot be accessed for writing +Main.hx:33: characters 3-8 : Cannot assign to final +Main.hx:37: characters 3-17 : Cannot modify abstract value of final field +Main.hx:38: characters 3-17 : Cannot modify abstract value of final local +Main.hx:40: characters 3-20 : Cannot modify abstract value of final field +Main.hx:41: characters 3-20 : Cannot modify abstract value of final local \ No newline at end of file diff --git a/tests/misc/projects/Issue10847/bad/Macro.hx b/tests/misc/projects/Issue10847/bad/Macro.hx new file mode 100644 index 00000000..569387e6 --- /dev/null +++ b/tests/misc/projects/Issue10847/bad/Macro.hx @@ -0,0 +1,9 @@ +import haxe.macro.Context; +import haxe.macro.Expr.Field; + +class Macro { + public static macro function build():Array { + Context.typeof(macro(null : String)); + return []; + } +} diff --git a/tests/misc/projects/Issue10847/bad/Main.hx b/tests/misc/projects/Issue10847/bad/Main.hx new file mode 100644 index 00000000..e6e295f0 --- /dev/null +++ b/tests/misc/projects/Issue10847/bad/Main.hx @@ -0,0 +1,10 @@ +interface Foo { + function foo():Void; +} + +@:build(Macro.build()) +class Main implements Foo { + public function foo():Void {} +} + +function main() {} diff --git a/tests/misc/projects/Issue10847/bad/compile-fail.hxml b/tests/misc/projects/Issue10847/bad/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10847/bad/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10847/bad/compile-fail.hxml.stderr b/tests/misc/projects/Issue10847/bad/compile-fail.hxml.stderr new file mode 100644 index 00000000..19c1af3f --- /dev/null +++ b/tests/misc/projects/Issue10847/bad/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:6: characters 7-11 : Field foo needed by Foo is missing \ No newline at end of file diff --git a/tests/misc/projects/Issue10847/good/Macro.hx b/tests/misc/projects/Issue10847/good/Macro.hx new file mode 100644 index 00000000..38ddce52 --- /dev/null +++ b/tests/misc/projects/Issue10847/good/Macro.hx @@ -0,0 +1,9 @@ +import haxe.macro.Context; +import haxe.macro.Expr.Field; + +class Macro { + public static macro function build():Array { + Context.typeof(macro(null : String)); + return null; + } +} diff --git a/tests/misc/projects/Issue10847/good/Main.hx b/tests/misc/projects/Issue10847/good/Main.hx new file mode 100644 index 00000000..e6e295f0 --- /dev/null +++ b/tests/misc/projects/Issue10847/good/Main.hx @@ -0,0 +1,10 @@ +interface Foo { + function foo():Void; +} + +@:build(Macro.build()) +class Main implements Foo { + public function foo():Void {} +} + +function main() {} diff --git a/tests/misc/projects/Issue10847/good/compile.hxml b/tests/misc/projects/Issue10847/good/compile.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10847/good/compile.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10852/Main.hx b/tests/misc/projects/Issue10852/Main.hx new file mode 100644 index 00000000..a2367de1 --- /dev/null +++ b/tests/misc/projects/Issue10852/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + foo((o) ->); + } + static function foo(?f:Dynamic->Bool) {} +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10852/compile-fail.hxml b/tests/misc/projects/Issue10852/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10852/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10852/compile-fail.hxml.stderr b/tests/misc/projects/Issue10852/compile-fail.hxml.stderr new file mode 100644 index 00000000..f36d0e52 --- /dev/null +++ b/tests/misc/projects/Issue10852/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:3: characters 19-20 : Expected expression \ No newline at end of file diff --git a/tests/misc/projects/Issue10863/Main.js.hx b/tests/misc/projects/Issue10863/Main.js.hx new file mode 100644 index 00000000..b7ad97b1 --- /dev/null +++ b/tests/misc/projects/Issue10863/Main.js.hx @@ -0,0 +1,5 @@ +function main() { + foo(); +} + +macro function foo():Expr; diff --git a/tests/misc/projects/Issue10863/Main.macro.hx b/tests/misc/projects/Issue10863/Main.macro.hx new file mode 100644 index 00000000..cada5aaa --- /dev/null +++ b/tests/misc/projects/Issue10863/Main.macro.hx @@ -0,0 +1,8 @@ +import haxe.macro.Context; +import haxe.macro.Expr; + +macro function foo():Expr { + Context.warning("from macro", (macro 0).pos); + Context.warning("from non macro", Context.currentPos()); + return macro null; +} diff --git a/tests/misc/projects/Issue10863/compile.hxml b/tests/misc/projects/Issue10863/compile.hxml new file mode 100644 index 00000000..def182fa --- /dev/null +++ b/tests/misc/projects/Issue10863/compile.hxml @@ -0,0 +1,5 @@ +-main Main +-js js.js +-D message-reporting=pretty +-D no-color +--no-output diff --git a/tests/misc/projects/Issue10863/compile.hxml.stderr b/tests/misc/projects/Issue10863/compile.hxml.stderr new file mode 100644 index 00000000..2ec0808d --- /dev/null +++ b/tests/misc/projects/Issue10863/compile.hxml.stderr @@ -0,0 +1,12 @@ +[WARNING] Main.macro.hx:5: characters 39-40 + + 5 | Context.warning("from macro", (macro 0).pos); + | ^ + | from macro + +[WARNING] Main.js.hx:2: characters 2-7 + + 2 | foo(); + | ^^^^^ + | from non macro + diff --git a/tests/misc/projects/Issue10863/logmode-fail.hxml b/tests/misc/projects/Issue10863/logmode-fail.hxml new file mode 100644 index 00000000..feee9acb --- /dev/null +++ b/tests/misc/projects/Issue10863/logmode-fail.hxml @@ -0,0 +1,3 @@ +Any +-D messages-log-file=error.log +-D messages-log-format=awesome diff --git a/tests/misc/projects/Issue10863/logmode-fail.hxml.stderr b/tests/misc/projects/Issue10863/logmode-fail.hxml.stderr new file mode 100644 index 00000000..1ac77f02 --- /dev/null +++ b/tests/misc/projects/Issue10863/logmode-fail.hxml.stderr @@ -0,0 +1 @@ +Invalid message reporting mode: "awesome", expected classic | pretty | indent (for -D messages_log_format). diff --git a/tests/misc/projects/Issue10863/reporting-fail.hxml b/tests/misc/projects/Issue10863/reporting-fail.hxml new file mode 100644 index 00000000..d4d64597 --- /dev/null +++ b/tests/misc/projects/Issue10863/reporting-fail.hxml @@ -0,0 +1,2 @@ +Any +-D message-reporting=awesome diff --git a/tests/misc/projects/Issue10863/reporting-fail.hxml.stderr b/tests/misc/projects/Issue10863/reporting-fail.hxml.stderr new file mode 100644 index 00000000..0d504ddc --- /dev/null +++ b/tests/misc/projects/Issue10863/reporting-fail.hxml.stderr @@ -0,0 +1 @@ +Invalid message reporting mode: "awesome", expected classic | pretty | indent (for -D message_reporting). diff --git a/tests/misc/projects/Issue10865/Main.hx b/tests/misc/projects/Issue10865/Main.hx new file mode 100644 index 00000000..6e3fc33f --- /dev/null +++ b/tests/misc/projects/Issue10865/Main.hx @@ -0,0 +1,9 @@ +class Main { + public static function main():Void + trace(foo()); + + public static function foo():Int { + true || throw 0; + 0; + } +} diff --git a/tests/misc/projects/Issue10865/compile-fail.hxml b/tests/misc/projects/Issue10865/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10865/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10865/compile-fail.hxml.stderr b/tests/misc/projects/Issue10865/compile-fail.hxml.stderr new file mode 100644 index 00000000..e0fddf72 --- /dev/null +++ b/tests/misc/projects/Issue10865/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:7: characters 3-4 : Missing return: Int \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/Main.hx b/tests/misc/projects/Issue10871/Compiler/Main.hx new file mode 100644 index 00000000..a74cd5cb --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/Main.hx @@ -0,0 +1,32 @@ +package; + +#if macro +import haxe.macro.Compiler; +import haxe.macro.Compiler.PackageRule; +#end + +function main() { +} + +#if macro +class MacroClass { + public static function start() { + final config = Compiler.getConfiguration(); + + trace(config.args); + trace(config.debug); + trace(config.verbose); + trace(config.foptimize); + trace(config.platform); + trace(config.mainClass.pack); + trace(config.mainClass.name); + + for(packageName => packageRule in config.packageRules) { + switch(packageRule) { + case Forbidden: trace(packageName + " is forbidden"); + case _: + } + } + } +} +#end diff --git a/tests/misc/projects/Issue10871/Compiler/MainFail.hx b/tests/misc/projects/Issue10871/Compiler/MainFail.hx new file mode 100644 index 00000000..06dd7d94 --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/MainFail.hx @@ -0,0 +1,5 @@ +package; + +function main() { + haxe.macro.Compiler.getConfiguration(); +} diff --git a/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml b/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml new file mode 100644 index 00000000..a489c77e --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml @@ -0,0 +1,2 @@ +--main MainFail +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml.stderr b/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml.stderr new file mode 100644 index 00000000..a897fbd2 --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile-fail.hxml.stderr @@ -0,0 +1 @@ +MainFail.hx:4: characters 22-38 : Class has no field getConfiguration \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/compile1.hxml b/tests/misc/projects/Issue10871/Compiler/compile1.hxml new file mode 100644 index 00000000..be1dafa7 --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile1.hxml @@ -0,0 +1,3 @@ +--main Main +--macro Main.MacroClass.start() +--interp diff --git a/tests/misc/projects/Issue10871/Compiler/compile1.hxml.stdout b/tests/misc/projects/Issue10871/Compiler/compile1.hxml.stdout new file mode 100644 index 00000000..af10cb1c --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile1.hxml.stdout @@ -0,0 +1,18 @@ +Main.hx:16: [--main,Main,--macro,Main.MacroClass.start(),--interp] +Main.hx:17: false +Main.hx:18: false +Main.hx:19: true +Main.hx:20: eval +Main.hx:21: [] +Main.hx:22: Main +Main.hx:26: python is forbidden +Main.hx:26: php is forbidden +Main.hx:26: neko is forbidden +Main.hx:26: lua is forbidden +Main.hx:26: jvm is forbidden +Main.hx:26: js is forbidden +Main.hx:26: java is forbidden +Main.hx:26: hl is forbidden +Main.hx:26: flash is forbidden +Main.hx:26: cs is forbidden +Main.hx:26: cpp is forbidden \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/compile2.hxml b/tests/misc/projects/Issue10871/Compiler/compile2.hxml new file mode 100644 index 00000000..7d365e9f --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile2.hxml @@ -0,0 +1,5 @@ +--main Main +--macro Main.MacroClass.start() +--js Output.js +--no-output +--debug \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/compile2.hxml.stdout b/tests/misc/projects/Issue10871/Compiler/compile2.hxml.stdout new file mode 100644 index 00000000..3b471bd6 --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile2.hxml.stdout @@ -0,0 +1,19 @@ +Main.hx:16: [--main,Main,--macro,Main.MacroClass.start(),--js,Output.js,--no-output,--debug] +Main.hx:17: true +Main.hx:18: false +Main.hx:19: true +Main.hx:20: js +Main.hx:21: [] +Main.hx:22: Main +Main.hx:26: sys is forbidden +Main.hx:26: python is forbidden +Main.hx:26: php is forbidden +Main.hx:26: neko is forbidden +Main.hx:26: lua is forbidden +Main.hx:26: jvm is forbidden +Main.hx:26: java is forbidden +Main.hx:26: hl is forbidden +Main.hx:26: flash is forbidden +Main.hx:26: eval is forbidden +Main.hx:26: cs is forbidden +Main.hx:26: cpp is forbidden \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Compiler/compile3.hxml b/tests/misc/projects/Issue10871/Compiler/compile3.hxml new file mode 100644 index 00000000..ce7b5fd8 --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile3.hxml @@ -0,0 +1,5 @@ +--main Main +--macro Main.MacroClass.start() +--cpp bin +--no-output +--no-opt diff --git a/tests/misc/projects/Issue10871/Compiler/compile3.hxml.stdout b/tests/misc/projects/Issue10871/Compiler/compile3.hxml.stdout new file mode 100644 index 00000000..ac231cba --- /dev/null +++ b/tests/misc/projects/Issue10871/Compiler/compile3.hxml.stdout @@ -0,0 +1,18 @@ +Main.hx:16: [--main,Main,--macro,Main.MacroClass.start(),--cpp,bin,--no-output,--no-opt] +Main.hx:17: false +Main.hx:18: false +Main.hx:19: false +Main.hx:20: cpp +Main.hx:21: [] +Main.hx:22: Main +Main.hx:26: python is forbidden +Main.hx:26: php is forbidden +Main.hx:26: neko is forbidden +Main.hx:26: lua is forbidden +Main.hx:26: jvm is forbidden +Main.hx:26: js is forbidden +Main.hx:26: java is forbidden +Main.hx:26: hl is forbidden +Main.hx:26: flash is forbidden +Main.hx:26: eval is forbidden +Main.hx:26: cs is forbidden \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/Context/Main.hx b/tests/misc/projects/Issue10871/Context/Main.hx new file mode 100644 index 00000000..a814092e --- /dev/null +++ b/tests/misc/projects/Issue10871/Context/Main.hx @@ -0,0 +1,43 @@ +package; + +#if macro +import haxe.macro.Context; +import haxe.macro.Type; +#end + +function main() { +} + +#if macro +class MacroClass { + public static function start() { + printTypedExpr(Context.getMainExpr()); + + Context.onAfterTyping(function(modules: Array) { + printTypedExpr(Context.getMainExpr()); + }); + + Context.onGenerate(function(types: Array) { + printTypedExpr(Context.getMainExpr()); + + if(Context.getAllModuleTypes().length == types.length) { + trace("Module type list length == types.length"); + } else { + trace("Module types length != types.length"); + } + }); + + Context.onAfterGenerate(function() { + printTypedExpr(Context.getMainExpr()); + }); + } + + static function printTypedExpr(te: Null, ?pos:haxe.PosInfos) { + if(te == null) { + haxe.Log.trace("null", pos); + } else { + haxe.Log.trace(haxe.macro.TypedExprTools.toString(te, true), pos); + } + } +} +#end diff --git a/tests/misc/projects/Issue10871/Context/compile.hxml b/tests/misc/projects/Issue10871/Context/compile.hxml new file mode 100644 index 00000000..be1dafa7 --- /dev/null +++ b/tests/misc/projects/Issue10871/Context/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--macro Main.MacroClass.start() +--interp diff --git a/tests/misc/projects/Issue10871/Context/compile.hxml.stdout b/tests/misc/projects/Issue10871/Context/compile.hxml.stdout new file mode 100644 index 00000000..a67f9dc8 --- /dev/null +++ b/tests/misc/projects/Issue10871/Context/compile.hxml.stdout @@ -0,0 +1,5 @@ +Main.hx:14: null +Main.hx:17: null +Main.hx:21: _Main.Main_Fields_.main() +Main.hx:24: Module type list length == types.length +Main.hx:31: _Main.Main_Fields_.main() \ No newline at end of file diff --git a/tests/misc/projects/Issue10871/TypeTools/Main.hx b/tests/misc/projects/Issue10871/TypeTools/Main.hx new file mode 100644 index 00000000..03bd67dc --- /dev/null +++ b/tests/misc/projects/Issue10871/TypeTools/Main.hx @@ -0,0 +1,118 @@ +package; + +#if macro +import haxe.macro.Context; +import haxe.macro.TypeTools; +import haxe.macro.Type; +#end + +function main() { +} + +#if macro +class MacroClass { + public static function start() { + Context.onGenerate(function(types: Array) { + for(t in types) { + testTypeConversion(t); + } + }); + } + + static function testTypeConversion(t: Type) { + final moduleType = TypeTools.toModuleType(t); + + switch(t) { + case TInst(t, params): { + switch(moduleType) { + case TClassDecl(t2): { + if(!compareClassTypes(t.get(), t2.get())) { + trace("Class types not equal"); + } + } + case _: trace("Module wasn't class type"); + } + } + case TEnum(e, params): { + switch(moduleType) { + case TEnumDecl(e2): { + if(!compareEnumTypes(e.get(), e2.get())) { + trace("Enum types not equal"); + } + } + case _: trace("Module wasn't enum type"); + } + } + case TType(d, params): { + switch(moduleType) { + case TTypeDecl(d2): { + if(!compareDefTypes(d.get(), d2.get())) { + trace("Def types not equal"); + } + } + case _: trace("Module wasn't def type"); + } + } + case TAbstract(a, params): { + switch(moduleType) { + case TAbstract(a2): { + if(!compareDefTypes(a.get(), a2.get())) { + trace("Abstract types not equal"); + } + } + case _: trace("Module wasn't abstract type"); + } + } + case _: + } + } + + static function compareClassTypes(c1: ClassType, c2: ClassType): Bool { + return ( + c1.name == c2.name && + c1.module == c2.module && + c1.pack.toString() == c2.pack.toString() && + Std.string(c1.pos) == Std.string(c2.pos) && + c1.isPrivate == c2.isPrivate && + c1.isInterface == c2.isInterface && + c1.isFinal == c2.isFinal && + c1.isExtern == c2.isExtern && + c1.isAbstract == c2.isAbstract + ); + } + + static function compareEnumTypes(e1: EnumType, e2: EnumType): Bool { + return ( + e1.name == e2.name && + e1.module == e2.module && + e1.pack.toString() == e2.pack.toString() && + e1.names.toString() == e2.names.toString() && + Std.string(e1.pos) == Std.string(e2.pos) && + e1.isPrivate == e2.isPrivate && + e1.isExtern == e2.isExtern + ); + } + + static function compareDefTypes(d1: DefType, d2: DefType): Bool { + return ( + d1.name == d2.name && + d1.module == d2.module && + d1.pack.toString() == d2.pack.toString() && + Std.string(d1.pos) == Std.string(d2.pos) && + d1.isPrivate == d2.isPrivate && + d1.isExtern == d2.isExtern + ); + } + + static function compareAbstractTypes(a1: AbstractType, a2: AbstractType): Bool { + return ( + a1.name == a2.name && + a1.module == a2.module && + a1.pack.toString() == a2.pack.toString() && + Std.string(a1.pos) == Std.string(a2.pos) && + a1.isPrivate == a2.isPrivate && + a1.isExtern == a2.isExtern + ); + } +} +#end diff --git a/tests/misc/projects/Issue10871/TypeTools/compile.hxml b/tests/misc/projects/Issue10871/TypeTools/compile.hxml new file mode 100644 index 00000000..be1dafa7 --- /dev/null +++ b/tests/misc/projects/Issue10871/TypeTools/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--macro Main.MacroClass.start() +--interp diff --git a/tests/misc/projects/Issue10871/TypeTools/compile.hxml.stdout b/tests/misc/projects/Issue10871/TypeTools/compile.hxml.stdout new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue10880/Main.hx b/tests/misc/projects/Issue10880/Main.hx new file mode 100644 index 00000000..0ba37e48 --- /dev/null +++ b/tests/misc/projects/Issue10880/Main.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + trace(null()); + } +} diff --git a/tests/misc/projects/Issue10880/compile-fail.hxml b/tests/misc/projects/Issue10880/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10880/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10880/compile-fail.hxml.stderr b/tests/misc/projects/Issue10880/compile-fail.hxml.stderr new file mode 100644 index 00000000..356c7f72 --- /dev/null +++ b/tests/misc/projects/Issue10880/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:3: characters 9-13 : This expression cannot be called +Main.hx:3: characters 9-13 : ... For function argument 'v' \ No newline at end of file diff --git a/tests/misc/projects/Issue10881/Main.hx b/tests/misc/projects/Issue10881/Main.hx new file mode 100644 index 00000000..7afc7c5c --- /dev/null +++ b/tests/misc/projects/Issue10881/Main.hx @@ -0,0 +1,7 @@ +abstract SerArray(Array) { + public dynamic function onChange():Void {}; +} + +class Main { + static function main() {} +} diff --git a/tests/misc/projects/Issue10881/compile-fail.hxml b/tests/misc/projects/Issue10881/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10881/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10881/compile-fail.hxml.stderr b/tests/misc/projects/Issue10881/compile-fail.hxml.stderr new file mode 100644 index 00000000..a3258179 --- /dev/null +++ b/tests/misc/projects/Issue10881/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:2: characters 2-44 : Invalid modifier: dynamic on method of abstract \ No newline at end of file diff --git a/tests/misc/projects/Issue10883/Main.hx b/tests/misc/projects/Issue10883/Main.hx new file mode 100644 index 00000000..5c898307 --- /dev/null +++ b/tests/misc/projects/Issue10883/Main.hx @@ -0,0 +1,15 @@ +class Main { + static function main() { + var foo = {"a": 1, "b": 1, "c": 1}; + test(); + trace($i{"wtf"}); + } + + static macro function test() { + var options:Array = [ + for (s in ["a", "b", "c"]) + macro @:pos(pos) $p{["foo", s]} + ]; + return macro $b{options}; + } +} diff --git a/tests/misc/projects/Issue10883/compile-fail.hxml b/tests/misc/projects/Issue10883/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10883/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10883/compile-fail.hxml.stderr b/tests/misc/projects/Issue10883/compile-fail.hxml.stderr new file mode 100644 index 00000000..69d893e0 --- /dev/null +++ b/tests/misc/projects/Issue10883/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:5: characters 9-11 : Reification $i is not allowed outside of `macro` expression +Main.hx:5: characters 9-11 : Reification $i is not allowed outside of `macro` expression diff --git a/tests/misc/projects/Issue10890/Main.hx b/tests/misc/projects/Issue10890/Main.hx new file mode 100644 index 00000000..2f26d140 --- /dev/null +++ b/tests/misc/projects/Issue10890/Main.hx @@ -0,0 +1,5 @@ +extern class Main { + overload function test(b:Bool):Void; + @:require(false) overload function test(i:Int):Void; + @:require(false) overload function test(s:String):Void; +} diff --git a/tests/misc/projects/Issue10890/build.hxml b/tests/misc/projects/Issue10890/build.hxml new file mode 100644 index 00000000..c098216e --- /dev/null +++ b/tests/misc/projects/Issue10890/build.hxml @@ -0,0 +1 @@ +Main diff --git a/tests/misc/projects/Issue10897/AbstractFields.hx b/tests/misc/projects/Issue10897/AbstractFields.hx new file mode 100644 index 00000000..76923579 --- /dev/null +++ b/tests/misc/projects/Issue10897/AbstractFields.hx @@ -0,0 +1,3 @@ +abstract AbstractFields(Dynamic) { + static function bar() {} +} diff --git a/tests/misc/projects/Issue10897/ClassFields.hx b/tests/misc/projects/Issue10897/ClassFields.hx new file mode 100644 index 00000000..92a4f806 --- /dev/null +++ b/tests/misc/projects/Issue10897/ClassFields.hx @@ -0,0 +1,3 @@ +class ClassFields { + static function bar() {} +} diff --git a/tests/misc/projects/Issue10897/EnumFields.hx b/tests/misc/projects/Issue10897/EnumFields.hx new file mode 100644 index 00000000..4cf631be --- /dev/null +++ b/tests/misc/projects/Issue10897/EnumFields.hx @@ -0,0 +1,3 @@ +enum EnumFields { + bar; +} diff --git a/tests/misc/projects/Issue10897/ImportAbstractField.hx b/tests/misc/projects/Issue10897/ImportAbstractField.hx new file mode 100644 index 00000000..7acd636e --- /dev/null +++ b/tests/misc/projects/Issue10897/ImportAbstractField.hx @@ -0,0 +1,3 @@ +import AbstractFields.baz; + +function main() {} diff --git a/tests/misc/projects/Issue10897/ImportClassField.hx b/tests/misc/projects/Issue10897/ImportClassField.hx new file mode 100644 index 00000000..5d194b12 --- /dev/null +++ b/tests/misc/projects/Issue10897/ImportClassField.hx @@ -0,0 +1,3 @@ +import ClassFields.baz; + +function main() {} diff --git a/tests/misc/projects/Issue10897/ImportEnumField.hx b/tests/misc/projects/Issue10897/ImportEnumField.hx new file mode 100644 index 00000000..ba0a91b0 --- /dev/null +++ b/tests/misc/projects/Issue10897/ImportEnumField.hx @@ -0,0 +1,3 @@ +import EnumFields.baz; + +function main() {} diff --git a/tests/misc/projects/Issue10897/ImportModuleField.hx b/tests/misc/projects/Issue10897/ImportModuleField.hx new file mode 100644 index 00000000..69d5e96f --- /dev/null +++ b/tests/misc/projects/Issue10897/ImportModuleField.hx @@ -0,0 +1,3 @@ +import ModuleFields.baz; + +function main() {} diff --git a/tests/misc/projects/Issue10897/ImportSubtype.hx b/tests/misc/projects/Issue10897/ImportSubtype.hx new file mode 100644 index 00000000..46942daf --- /dev/null +++ b/tests/misc/projects/Issue10897/ImportSubtype.hx @@ -0,0 +1,3 @@ +import Types.Baz; + +function main() {} diff --git a/tests/misc/projects/Issue10897/ModuleFields.hx b/tests/misc/projects/Issue10897/ModuleFields.hx new file mode 100644 index 00000000..585b645d --- /dev/null +++ b/tests/misc/projects/Issue10897/ModuleFields.hx @@ -0,0 +1 @@ +function bar() {} diff --git a/tests/misc/projects/Issue10897/Types.hx b/tests/misc/projects/Issue10897/Types.hx new file mode 100644 index 00000000..e431de40 --- /dev/null +++ b/tests/misc/projects/Issue10897/Types.hx @@ -0,0 +1 @@ +enum Bar {} \ No newline at end of file diff --git a/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml b/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml new file mode 100644 index 00000000..bf990903 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml @@ -0,0 +1,2 @@ +--main ImportAbstractField +--interp diff --git a/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml.stderr b/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml.stderr new file mode 100644 index 00000000..aa815ac5 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-abstract-field-fail.hxml.stderr @@ -0,0 +1 @@ +ImportAbstractField.hx:1: characters 1-27 : Abstract has no field baz (Suggestion: bar) diff --git a/tests/misc/projects/Issue10897/import-class-field-fail.hxml b/tests/misc/projects/Issue10897/import-class-field-fail.hxml new file mode 100644 index 00000000..5921968c --- /dev/null +++ b/tests/misc/projects/Issue10897/import-class-field-fail.hxml @@ -0,0 +1,2 @@ +--main ImportClassField +--interp diff --git a/tests/misc/projects/Issue10897/import-class-field-fail.hxml.stderr b/tests/misc/projects/Issue10897/import-class-field-fail.hxml.stderr new file mode 100644 index 00000000..050ff308 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-class-field-fail.hxml.stderr @@ -0,0 +1 @@ +ImportClassField.hx:1: characters 1-24 : Class has no field baz (Suggestion: bar) diff --git a/tests/misc/projects/Issue10897/import-enum-field-fail.hxml b/tests/misc/projects/Issue10897/import-enum-field-fail.hxml new file mode 100644 index 00000000..7ce58fcc --- /dev/null +++ b/tests/misc/projects/Issue10897/import-enum-field-fail.hxml @@ -0,0 +1,2 @@ +--main ImportEnumField +--interp diff --git a/tests/misc/projects/Issue10897/import-enum-field-fail.hxml.stderr b/tests/misc/projects/Issue10897/import-enum-field-fail.hxml.stderr new file mode 100644 index 00000000..7791db74 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-enum-field-fail.hxml.stderr @@ -0,0 +1 @@ +ImportEnumField.hx:1: characters 1-23 : Enum has no field baz (Suggestion: bar) diff --git a/tests/misc/projects/Issue10897/import-module-field-fail.hxml b/tests/misc/projects/Issue10897/import-module-field-fail.hxml new file mode 100644 index 00000000..24735699 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-module-field-fail.hxml @@ -0,0 +1,2 @@ +--main ImportModuleField +--interp diff --git a/tests/misc/projects/Issue10897/import-module-field-fail.hxml.stderr b/tests/misc/projects/Issue10897/import-module-field-fail.hxml.stderr new file mode 100644 index 00000000..7b621c3c --- /dev/null +++ b/tests/misc/projects/Issue10897/import-module-field-fail.hxml.stderr @@ -0,0 +1 @@ +ImportModuleField.hx:1: characters 1-25 : Module ModuleFields does not define field baz (Suggestion: bar) diff --git a/tests/misc/projects/Issue10897/import-subtype-fail.hxml b/tests/misc/projects/Issue10897/import-subtype-fail.hxml new file mode 100644 index 00000000..c14b63e6 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-subtype-fail.hxml @@ -0,0 +1,2 @@ +--main ImportSubtype +--interp diff --git a/tests/misc/projects/Issue10897/import-subtype-fail.hxml.stderr b/tests/misc/projects/Issue10897/import-subtype-fail.hxml.stderr new file mode 100644 index 00000000..d06b80b5 --- /dev/null +++ b/tests/misc/projects/Issue10897/import-subtype-fail.hxml.stderr @@ -0,0 +1 @@ +ImportSubtype.hx:1: characters 1-18 : Module Types does not define type Baz (Suggestion: Bar) diff --git a/tests/misc/projects/Issue10935/Main.hx b/tests/misc/projects/Issue10935/Main.hx new file mode 100644 index 00000000..cacff875 --- /dev/null +++ b/tests/misc/projects/Issue10935/Main.hx @@ -0,0 +1,6 @@ +class Main { + public var test:Int; + public function set_test(i:String) { + test = 42; + } +} diff --git a/tests/misc/projects/Issue10935/build.hxml b/tests/misc/projects/Issue10935/build.hxml new file mode 100644 index 00000000..c098216e --- /dev/null +++ b/tests/misc/projects/Issue10935/build.hxml @@ -0,0 +1 @@ +Main diff --git a/tests/misc/projects/Issue10961/Main.hx b/tests/misc/projects/Issue10961/Main.hx new file mode 100644 index 00000000..dbc1568a --- /dev/null +++ b/tests/misc/projects/Issue10961/Main.hx @@ -0,0 +1,37 @@ +class Main { + static function main() { + final foo:Foo = { + a: 0, + b: 0, + c: 0, + a: 0, + } + final foo = { + a: 0, + b: 0, + c: 0, + a: 0, + } + final foo:CFoo = { + a: 0, + b: 0, + a: 0, + } + } +} + +typedef Foo = { + a:Int, + b:Int, + c:Int, +} + +typedef FooAdd = { + d:Int, +} + +@:structInit +class CFoo { + var a:Int; + var b:Int; +} diff --git a/tests/misc/projects/Issue10961/compile-fail.hxml b/tests/misc/projects/Issue10961/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue10961/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue10961/compile-fail.hxml.stderr b/tests/misc/projects/Issue10961/compile-fail.hxml.stderr new file mode 100644 index 00000000..33e9e4de --- /dev/null +++ b/tests/misc/projects/Issue10961/compile-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main.hx:7: characters 4-5 : Duplicate field in object declaration : a +Main.hx:13: characters 4-5 : Duplicate field in object declaration : a +Main.hx:18: characters 4-5 : Duplicate field in object declaration : a diff --git a/tests/misc/projects/Issue10980/Main.hx b/tests/misc/projects/Issue10980/Main.hx new file mode 100644 index 00000000..4c2460bd --- /dev/null +++ b/tests/misc/projects/Issue10980/Main.hx @@ -0,0 +1,18 @@ +import haxe.extern.EitherType as Or; + +typedef Callback = Or< + Or< + () -> Void, + (a:Int) -> Void + >, + (a:Int, b:Int) -> Void + >; + +class Main { + static function main() { + final foo:Callback = a -> { + $type(a); // Unknown<0> instead of Int + return; + } + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue10980/compile.hxml b/tests/misc/projects/Issue10980/compile.hxml new file mode 100644 index 00000000..a711e039 --- /dev/null +++ b/tests/misc/projects/Issue10980/compile.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp diff --git a/tests/misc/projects/Issue10980/compile.hxml.stderr b/tests/misc/projects/Issue10980/compile.hxml.stderr new file mode 100644 index 00000000..abb3206c --- /dev/null +++ b/tests/misc/projects/Issue10980/compile.hxml.stderr @@ -0,0 +1 @@ +Main.hx:14: characters 10-11 : Warning : Int \ No newline at end of file diff --git a/tests/misc/projects/Issue11004/Bar.hx b/tests/misc/projects/Issue11004/Bar.hx new file mode 100644 index 00000000..a1813816 --- /dev/null +++ b/tests/misc/projects/Issue11004/Bar.hx @@ -0,0 +1,49 @@ +package; + +#if macro +import haxe.macro.Context; +import haxe.macro.Expr; +import haxe.macro.Type; + +using haxe.macro.ExprTools; +using haxe.macro.TypeTools; +#end + +class Bar +{ + public function new() + { + trace('New Bar'); + } + + public function nonmacro_func(val:String) + { + trace('Hello runtime: $val'); + } + + public macro function macro_func(this_expr:Expr) // :this (should refer to the Bar instance on the Foo) + { + var this_ident:String = get_this_ident(this_expr); + trace('${ this_expr.toString() } computed this_ident as: ${ this_ident }'); + + var code = '${ this_ident }.nonmacro_func("${ this_ident }")'; + return Context.parse(code, Context.currentPos()); + } + + #if macro + static function get_this_ident(this_expr:Expr):String + { + // Read the ident from the source code + return switch (this_expr.expr) { + case EMeta(_, e): + var info = Context.getPosInfos(this_expr.pos); + var bytes = sys.io.File.getBytes(info.file); + bytes.getString(info.min, info.max-info.min); + default: + // Not in the above form? Hmm... + trace('Unexpected this resolution: ${ this_expr.toString() }'); + this_expr.toString(); + } + } + #end +} diff --git a/tests/misc/projects/Issue11004/Foo.hx b/tests/misc/projects/Issue11004/Foo.hx new file mode 100644 index 00000000..502734f8 --- /dev/null +++ b/tests/misc/projects/Issue11004/Foo.hx @@ -0,0 +1,13 @@ +class Foo +{ + var _bar_private:Bar; + + public function new() + { + trace('New foo'); + _bar_private = new Bar(); + } + + public var bar(get,null):Bar; + public function get_bar():Bar return _bar_private; +} diff --git a/tests/misc/projects/Issue11004/Main.hx b/tests/misc/projects/Issue11004/Main.hx new file mode 100644 index 00000000..fd968402 --- /dev/null +++ b/tests/misc/projects/Issue11004/Main.hx @@ -0,0 +1,8 @@ +class Main +{ + public static function main() + { + var foo = new Foo(); + foo.bar.macro_func(); + } +} diff --git a/tests/misc/projects/Issue11004/build.hxml b/tests/misc/projects/Issue11004/build.hxml new file mode 100644 index 00000000..c098216e --- /dev/null +++ b/tests/misc/projects/Issue11004/build.hxml @@ -0,0 +1 @@ +Main diff --git a/tests/misc/projects/Issue11004/build.hxml.stdout b/tests/misc/projects/Issue11004/build.hxml.stdout new file mode 100644 index 00000000..800b6c23 --- /dev/null +++ b/tests/misc/projects/Issue11004/build.hxml.stdout @@ -0,0 +1 @@ +Bar.hx:27: @:storedTypedExpr 3 computed this_ident as: foo.bar \ No newline at end of file diff --git a/tests/misc/projects/Issue11020/Main.hx b/tests/misc/projects/Issue11020/Main.hx new file mode 100644 index 00000000..a660d173 --- /dev/null +++ b/tests/misc/projects/Issue11020/Main.hx @@ -0,0 +1,17 @@ +#if !macro @:build(Main.error()) #end +class Main { + var field:Int = 0; + static function main() {} +} + +#if macro +function error() { + var fields = haxe.macro.Context.getBuildFields(); + haxe.macro.Context.info("Context.info", fields[0].pos); + haxe.macro.Context.warning("Context.warning", fields[0].pos); + haxe.macro.Context.error("Context.error", fields[0].pos); + + return null; +} +#end + diff --git a/tests/misc/projects/Issue11020/compile.hxml b/tests/misc/projects/Issue11020/compile.hxml new file mode 100644 index 00000000..e4dacfcd --- /dev/null +++ b/tests/misc/projects/Issue11020/compile.hxml @@ -0,0 +1,4 @@ +-cp src +-main Main +--interp +--display "Main.hx@0@diagnostics" diff --git a/tests/misc/projects/Issue11020/compile.hxml.stderr b/tests/misc/projects/Issue11020/compile.hxml.stderr new file mode 100644 index 00000000..906a6c34 --- /dev/null +++ b/tests/misc/projects/Issue11020/compile.hxml.stderr @@ -0,0 +1 @@ +[{"file":"$$normPath(::cwd::/,true)Main.hx","diagnostics":[{"kind":2,"severity":3,"range":{"start":{"line":2,"character":1},"end":{"line":2,"character":19}},"args":"Context.info","relatedInformation":[]},{"kind":2,"severity":2,"range":{"start":{"line":2,"character":1},"end":{"line":2,"character":19}},"args":"Context.warning","relatedInformation":[]},{"kind":2,"severity":1,"range":{"start":{"line":2,"character":1},"end":{"line":2,"character":19}},"args":"Context.error","relatedInformation":[]},{"kind":2,"severity":1,"range":{"start":{"line":0,"character":11},"end":{"line":0,"character":18}},"args":"Build failure","relatedInformation":[]}]}] diff --git a/tests/misc/projects/Issue11055/Main.hx b/tests/misc/projects/Issue11055/Main.hx new file mode 100644 index 00000000..586cec74 --- /dev/null +++ b/tests/misc/projects/Issue11055/Main.hx @@ -0,0 +1,14 @@ +macro function test() { + foo(); + return macro null; +} + +#if macro +function foo() { + throw "up"; +} +#end + +function main() { + test(); +} diff --git a/tests/misc/projects/Issue11055/compile-fail.hxml b/tests/misc/projects/Issue11055/compile-fail.hxml new file mode 100644 index 00000000..20afe58f --- /dev/null +++ b/tests/misc/projects/Issue11055/compile-fail.hxml @@ -0,0 +1 @@ +--main Main diff --git a/tests/misc/projects/Issue11055/compile-fail.hxml.stderr b/tests/misc/projects/Issue11055/compile-fail.hxml.stderr new file mode 100644 index 00000000..a17d5acd --- /dev/null +++ b/tests/misc/projects/Issue11055/compile-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main.hx:8: characters 2-7 : Uncaught exception up +Main.hx:2: characters 2-7 : Called from here +Main.hx:13: characters 2-8 : Called from here diff --git a/tests/misc/projects/Issue11055/compile-pretty-fail.hxml b/tests/misc/projects/Issue11055/compile-pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue11055/compile-pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue11055/compile-pretty-fail.hxml.stderr b/tests/misc/projects/Issue11055/compile-pretty-fail.hxml.stderr new file mode 100644 index 00000000..34dc6c96 --- /dev/null +++ b/tests/misc/projects/Issue11055/compile-pretty-fail.hxml.stderr @@ -0,0 +1,14 @@ +[ERROR] Main.hx:8: characters 2-7 + + 8 | throw "up"; + | ^^^^^ + | Uncaught exception up + + 2 | foo(); + | ^^^^^ + | Called from here + + 13 | test(); + | ^^^^^^ + | Called from here + diff --git a/tests/misc/projects/Issue2508/compile.hxml.stderr b/tests/misc/projects/Issue2508/compile.hxml.stderr index 038fd821..bd127348 100644 --- a/tests/misc/projects/Issue2508/compile.hxml.stderr +++ b/tests/misc/projects/Issue2508/compile.hxml.stderr @@ -1,10 +1,10 @@ -Main.hx:15: characters 4-15 : Warning : This case is unused -Main.hx:20: characters 4-14 : Warning : This case is unused -Main.hx:21: characters 4-15 : Warning : This case is unused -Main.hx:26: characters 6-11 : Warning : This pattern is unused -Main.hx:32: characters 7-18 : Warning : This pattern is unused -Main.hx:44: characters 4-32 : Warning : This case is unused -Main.hx:50: characters 4-19 : Warning : This case is unused -Main.hx:55: characters 4-15 : Warning : This case is unused -Main.hx:60: characters 4-16 : Warning : This case is unused -Main.hx:66: characters 9-15 : Warning : This pattern is unused \ No newline at end of file +Main.hx:15: characters 4-15 : Warning : (WUnusedPattern) This case is unused +Main.hx:20: characters 4-14 : Warning : (WUnusedPattern) This case is unused +Main.hx:21: characters 4-15 : Warning : (WUnusedPattern) This case is unused +Main.hx:26: characters 6-11 : Warning : (WUnusedPattern) This pattern is unused +Main.hx:32: characters 7-18 : Warning : (WUnusedPattern) This pattern is unused +Main.hx:44: characters 4-32 : Warning : (WUnusedPattern) This case is unused +Main.hx:50: characters 4-19 : Warning : (WUnusedPattern) This case is unused +Main.hx:55: characters 4-15 : Warning : (WUnusedPattern) This case is unused +Main.hx:60: characters 4-16 : Warning : (WUnusedPattern) This case is unused +Main.hx:66: characters 9-15 : Warning : (WUnusedPattern) This pattern is unused \ No newline at end of file diff --git a/tests/misc/projects/Issue2996/A.hx b/tests/misc/projects/Issue2996/A.hx deleted file mode 100644 index ec7e2d3e..00000000 --- a/tests/misc/projects/Issue2996/A.hx +++ /dev/null @@ -1 +0,0 @@ -abstract A(String) {} diff --git a/tests/misc/projects/Issue2996/Main.hx b/tests/misc/projects/Issue2996/Main.hx deleted file mode 100644 index 8a17b7a7..00000000 --- a/tests/misc/projects/Issue2996/Main.hx +++ /dev/null @@ -1,5 +0,0 @@ -import pack.B; - -class Main { - -} \ No newline at end of file diff --git a/tests/misc/projects/Issue2996/compile1.hxml b/tests/misc/projects/Issue2996/compile1.hxml deleted file mode 100644 index 433fea7e..00000000 --- a/tests/misc/projects/Issue2996/compile1.hxml +++ /dev/null @@ -1 +0,0 @@ ---display Main.hx@0@resolve@A \ No newline at end of file diff --git a/tests/misc/projects/Issue2996/compile1.hxml.stderr b/tests/misc/projects/Issue2996/compile1.hxml.stderr deleted file mode 100644 index 50e88f17..00000000 --- a/tests/misc/projects/Issue2996/compile1.hxml.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -$$normPath(::cwd::/A.hx):1: characters 1-22 - \ No newline at end of file diff --git a/tests/misc/projects/Issue2996/compile2.hxml b/tests/misc/projects/Issue2996/compile2.hxml deleted file mode 100644 index e663cfee..00000000 --- a/tests/misc/projects/Issue2996/compile2.hxml +++ /dev/null @@ -1 +0,0 @@ ---display Main.hx@0@resolve@C \ No newline at end of file diff --git a/tests/misc/projects/Issue2996/compile2.hxml.stderr b/tests/misc/projects/Issue2996/compile2.hxml.stderr deleted file mode 100644 index 861c6132..00000000 --- a/tests/misc/projects/Issue2996/compile2.hxml.stderr +++ /dev/null @@ -1,3 +0,0 @@ - -$$normPath(::cwd::/pack/B.hx):3: characters 1-11 - diff --git a/tests/misc/projects/Issue2996/pack/B.hx b/tests/misc/projects/Issue2996/pack/B.hx deleted file mode 100644 index 4a0eb451..00000000 --- a/tests/misc/projects/Issue2996/pack/B.hx +++ /dev/null @@ -1,3 +0,0 @@ -package pack; - -class C {} diff --git a/tests/misc/projects/Issue4720/compile.hxml.stderr b/tests/misc/projects/Issue4720/compile.hxml.stderr index 215e2f55..aaddd2a0 100644 --- a/tests/misc/projects/Issue4720/compile.hxml.stderr +++ b/tests/misc/projects/Issue4720/compile.hxml.stderr @@ -1,24 +1,24 @@ -Main.hx:8: characters 9-15 : Warning : Usage of this typedef is deprecated -Main.hx:9: characters 9-19 : Warning : Usage of this typedef is deprecated -Main.hx:10: characters 9-14 : Warning : Usage of this typedef is deprecated -Main.hx:18: characters 13-19 : Warning : This typedef is deprecated in favor of MyClass -Main.hx:19: characters 9-14 : Warning : Usage of this typedef is deprecated -Main.hx:20: characters 13-22 : Warning : This typedef is deprecated in favor of MyAbstract -Main.hx:32: characters 9-13 : Warning : Usage of this enum is deprecated -Main.hx:36: characters 9-14 : Warning : Usage of this enum field is deprecated -Main.hx:4: characters 9-16 : Warning : Usage of this class is deprecated -Main.hx:5: characters 9-20 : Warning : Usage of this class is deprecated -Main.hx:6: characters 9-15 : Warning : Usage of this enum is deprecated -Main.hx:15: characters 9-22 : Warning : Usage of this class is deprecated -Main.hx:16: characters 9-15 : Warning : Usage of this enum is deprecated -Main.hx:17: characters 9-29 : Warning : Usage of this class is deprecated -Main.hx:18: characters 9-21 : Warning : Usage of this class is deprecated -Main.hx:20: characters 9-28 : Warning : Usage of this class is deprecated -Main.hx:23: characters 9-24 : Warning : Usage of this field is deprecated -Main.hx:24: characters 9-23 : Warning : Usage of this field is deprecated -Main.hx:25: characters 9-27 : Warning : Usage of this field is deprecated -Main.hx:26: characters 17-35 : Warning : Usage of this field is deprecated -Main.hx:27: characters 9-25 : Warning : Usage of this field is deprecated -Main.hx:28: characters 17-33 : Warning : Usage of this field is deprecated -Main.hx:31: characters 11-15 : Warning : Usage of this enum is deprecated -Main.hx:35: characters 11-16 : Warning : Usage of this enum field is deprecated \ No newline at end of file +Main.hx:8: characters 9-15 : Warning : (WDeprecated) Usage of this typedef is deprecated +Main.hx:9: characters 9-19 : Warning : (WDeprecated) Usage of this typedef is deprecated +Main.hx:10: characters 9-14 : Warning : (WDeprecated) Usage of this typedef is deprecated +Main.hx:18: characters 13-19 : Warning : (WDeprecated) This typedef is deprecated in favor of MyClass +Main.hx:19: characters 9-14 : Warning : (WDeprecated) Usage of this typedef is deprecated +Main.hx:20: characters 13-22 : Warning : (WDeprecated) This typedef is deprecated in favor of MyAbstract +Main.hx:32: characters 9-13 : Warning : (WDeprecated) Usage of this enum is deprecated +Main.hx:36: characters 9-14 : Warning : (WDeprecated) Usage of this enum field is deprecated +Main.hx:4: characters 9-16 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:5: characters 9-20 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:6: characters 9-15 : Warning : (WDeprecated) Usage of this enum is deprecated +Main.hx:15: characters 9-22 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:16: characters 9-15 : Warning : (WDeprecated) Usage of this enum is deprecated +Main.hx:17: characters 9-29 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:18: characters 9-21 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:20: characters 9-28 : Warning : (WDeprecated) Usage of this class is deprecated +Main.hx:23: characters 9-24 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:24: characters 9-23 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:25: characters 9-27 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:26: characters 17-35 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:27: characters 9-25 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:28: characters 17-33 : Warning : (WDeprecated) Usage of this field is deprecated +Main.hx:31: characters 11-15 : Warning : (WDeprecated) Usage of this enum is deprecated +Main.hx:35: characters 11-16 : Warning : (WDeprecated) Usage of this enum field is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue4803/Main2.hx b/tests/misc/projects/Issue4803/Main2.hx new file mode 100644 index 00000000..7d772602 --- /dev/null +++ b/tests/misc/projects/Issue4803/Main2.hx @@ -0,0 +1,8 @@ +extern inline overload function test(a:String) {} +extern inline overload function test(a:Int) {} + +function main() { + test(function() { + var x:String = 1; + }); +} diff --git a/tests/misc/projects/Issue4803/compile2-fail.hxml b/tests/misc/projects/Issue4803/compile2-fail.hxml new file mode 100644 index 00000000..628320e6 --- /dev/null +++ b/tests/misc/projects/Issue4803/compile2-fail.hxml @@ -0,0 +1,3 @@ +--main Main2 +-js js.js +--no-output diff --git a/tests/misc/projects/Issue4803/compile2-fail.hxml.stderr b/tests/misc/projects/Issue4803/compile2-fail.hxml.stderr new file mode 100644 index 00000000..80a3130e --- /dev/null +++ b/tests/misc/projects/Issue4803/compile2-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main2.hx:6: characters 3-20 : Int should be String +Main2.hx:6: characters 3-20 : ... For function argument 'a' diff --git a/tests/misc/projects/Issue4907/compile-fail.hxml.stderr b/tests/misc/projects/Issue4907/compile-fail.hxml.stderr index c82ffafb..30e41bfa 100644 --- a/tests/misc/projects/Issue4907/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue4907/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:7: characters 18-23 : Pattern variables must be lower-case \ No newline at end of file +Main.hx:7: characters 18-23 : Unknown identifier : VALUE, pattern variables must be lower-case or with `var ` prefix diff --git a/tests/misc/projects/Issue5644/compile-fail.hxml.stderr b/tests/misc/projects/Issue5644/compile-fail.hxml.stderr index 460c2f18..ea73b985 100644 --- a/tests/misc/projects/Issue5644/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue5644/compile-fail.hxml.stderr @@ -1,2 +1 @@ -User.hx:1: characters 20-47 : Type not found : ThisObviouslyDoesntExist -Main.hx:1: lines 1-5 : ... Defined in this class \ No newline at end of file +User.hx:1: characters 20-47 : Type not found : ThisObviouslyDoesntExist \ No newline at end of file diff --git a/tests/misc/projects/Issue5644/pretty-fail.hxml b/tests/misc/projects/Issue5644/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue5644/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue5644/pretty-fail.hxml.stderr b/tests/misc/projects/Issue5644/pretty-fail.hxml.stderr new file mode 100644 index 00000000..88c0ed82 --- /dev/null +++ b/tests/misc/projects/Issue5644/pretty-fail.hxml.stderr @@ -0,0 +1,6 @@ +[ERROR] User.hx:1: characters 20-47 + + 1 | class User extends ThisObviouslyDoesntExist {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Type not found : ThisObviouslyDoesntExist + diff --git a/tests/misc/projects/Issue5854/NamedLocal.hx b/tests/misc/projects/Issue5854/NamedLocal.hx new file mode 100644 index 00000000..c376d8f8 --- /dev/null +++ b/tests/misc/projects/Issue5854/NamedLocal.hx @@ -0,0 +1,10 @@ +class NamedLocal { + static function main() { + // shouldn't be parsed as a call + function test(str) { + trace(str); + throw "This function shouldn't have been called!!"; + } + ("hello"); + } +} diff --git a/tests/misc/projects/Issue5854/UnnamedLocal.hx b/tests/misc/projects/Issue5854/UnnamedLocal.hx new file mode 100644 index 00000000..1d9768c3 --- /dev/null +++ b/tests/misc/projects/Issue5854/UnnamedLocal.hx @@ -0,0 +1,8 @@ +class UnnamedLocal { + static function main() { + function(str) { + trace(str); + } + ("hello"); + } +} diff --git a/tests/misc/projects/Issue5854/compile-fail.hxml b/tests/misc/projects/Issue5854/compile-fail.hxml new file mode 100644 index 00000000..8807175f --- /dev/null +++ b/tests/misc/projects/Issue5854/compile-fail.hxml @@ -0,0 +1 @@ +--main UnnamedLocal diff --git a/tests/misc/projects/Issue5854/compile-fail.hxml.stderr b/tests/misc/projects/Issue5854/compile-fail.hxml.stderr new file mode 100644 index 00000000..d77e9107 --- /dev/null +++ b/tests/misc/projects/Issue5854/compile-fail.hxml.stderr @@ -0,0 +1 @@ +UnnamedLocal.hx:3: lines 3-5 : Unnamed lvalue functions are not supported diff --git a/tests/misc/projects/Issue5854/compile.hxml b/tests/misc/projects/Issue5854/compile.hxml new file mode 100644 index 00000000..8707245b --- /dev/null +++ b/tests/misc/projects/Issue5854/compile.hxml @@ -0,0 +1 @@ +--run NamedLocal diff --git a/tests/misc/projects/Issue5854/compile.hxml.stderr b/tests/misc/projects/Issue5854/compile.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/Issue5880/compile5-fail.hxml.stderr b/tests/misc/projects/Issue5880/compile5-fail.hxml.stderr index 3404bef0..b541136a 100644 --- a/tests/misc/projects/Issue5880/compile5-fail.hxml.stderr +++ b/tests/misc/projects/Issue5880/compile5-fail.hxml.stderr @@ -1,2 +1 @@ -Main5.hx:4: characters 10-13 : Array<{ v : fromV.T }> should be fromV.T -Main5.hx:4: characters 3-13 : Recursive implicit cast \ No newline at end of file +Main5.hx:4: characters 3-13 : Array<{ v : fromV.T }> should be Ref \ No newline at end of file diff --git a/tests/misc/projects/Issue5949/indent-fail.hxml b/tests/misc/projects/Issue5949/indent-fail.hxml new file mode 100644 index 00000000..1a2ff3dc --- /dev/null +++ b/tests/misc/projects/Issue5949/indent-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue5949/indent-fail.hxml.stderr b/tests/misc/projects/Issue5949/indent-fail.hxml.stderr new file mode 100644 index 00000000..b60840c6 --- /dev/null +++ b/tests/misc/projects/Issue5949/indent-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:11: characters 11-14 : Field a has different @:native value than in superclass + Main.hx:6: characters 18-19 : Base field is defined here diff --git a/tests/misc/projects/Issue5949/pretty-fail.hxml b/tests/misc/projects/Issue5949/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue5949/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue5949/pretty-fail.hxml.stderr b/tests/misc/projects/Issue5949/pretty-fail.hxml.stderr new file mode 100644 index 00000000..b553bf6a --- /dev/null +++ b/tests/misc/projects/Issue5949/pretty-fail.hxml.stderr @@ -0,0 +1,10 @@ +[ERROR] Main.hx:11: characters 11-14 + + 11 | @:native("z") override public function a() {} + | ^^^ + | Field a has different @:native value than in superclass + + 6 | public function a() {} + | ^ + | Base field is defined here + diff --git a/tests/misc/projects/Issue6065/indent-fail.hxml b/tests/misc/projects/Issue6065/indent-fail.hxml new file mode 100644 index 00000000..1a2ff3dc --- /dev/null +++ b/tests/misc/projects/Issue6065/indent-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue6065/indent-fail.hxml.stderr b/tests/misc/projects/Issue6065/indent-fail.hxml.stderr new file mode 100644 index 00000000..a9222d5e --- /dev/null +++ b/tests/misc/projects/Issue6065/indent-fail.hxml.stderr @@ -0,0 +1,14 @@ +Main.hx:8: characters 9-18 : Could not find a suitable overload, reasons follow + Main.hx:8: characters 9-18 : Overload resolution failed for () -> Void + Main.hx:8: characters 13-17 : Too many arguments + Main.hx:8: characters 9-18 : Overload resolution failed for (t : f.T) -> Void + Main.hx:8: characters 13-17 : Constraint check failure for f.T + Main.hx:8: characters 13-17 : String should be Int + Main.hx:8: characters 13-17 : For function argument 't' +Main.hx:9: characters 9-16 : Could not find a suitable overload, reasons follow + Main.hx:9: characters 9-16 : Overload resolution failed for () -> Void + Main.hx:9: characters 13-15 : Too many arguments + Main.hx:9: characters 9-16 : Overload resolution failed for (t : f.T) -> Void + Main.hx:9: characters 13-15 : Constraint check failure for f.T + Main.hx:9: characters 13-15 : { } should be Int + Main.hx:9: characters 13-15 : For function argument 't' diff --git a/tests/misc/projects/Issue6065/pretty-fail.hxml b/tests/misc/projects/Issue6065/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue6065/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue6065/pretty-fail.hxml.stderr b/tests/misc/projects/Issue6065/pretty-fail.hxml.stderr new file mode 100644 index 00000000..0cec7a59 --- /dev/null +++ b/tests/misc/projects/Issue6065/pretty-fail.hxml.stderr @@ -0,0 +1,40 @@ +[ERROR] Main.hx:8: characters 9-18 + + 8 | C.f("hi"); + | ^^^^^^^^^ + | Could not find a suitable overload, reasons follow + + | Overload resolution failed for () -> Void + + 8 | C.f("hi"); + | ^^^^ + | Too many arguments + + | Overload resolution failed for (t : f.T) -> Void + + 8 | C.f("hi"); + | ^^^^ + | Constraint check failure for f.T + | String should be Int + | For function argument 't' + +[ERROR] Main.hx:9: characters 9-16 + + 9 | C.f({}); + | ^^^^^^^ + | Could not find a suitable overload, reasons follow + + | Overload resolution failed for () -> Void + + 9 | C.f({}); + | ^^ + | Too many arguments + + | Overload resolution failed for (t : f.T) -> Void + + 9 | C.f({}); + | ^^ + | Constraint check failure for f.T + | { } should be Int + | For function argument 't' + diff --git a/tests/misc/projects/Issue6226/compile.hxml.stderr b/tests/misc/projects/Issue6226/compile.hxml.stderr index 534c9404..afad79f7 100644 --- a/tests/misc/projects/Issue6226/compile.hxml.stderr +++ b/tests/misc/projects/Issue6226/compile.hxml.stderr @@ -1 +1 @@ -Main.hx:7: characters 3-4 : Warning : Usage of this field is deprecated \ No newline at end of file +Main.hx:7: characters 3-4 : Warning : (WDeprecated) Usage of this field is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue6584/compile1-fail.hxml.stderr b/tests/misc/projects/Issue6584/compile1-fail.hxml.stderr index e515bda1..870b2c4d 100644 --- a/tests/misc/projects/Issue6584/compile1-fail.hxml.stderr +++ b/tests/misc/projects/Issue6584/compile1-fail.hxml.stderr @@ -1 +1 @@ -Main1.hx:2: characters 2-17 : v: Static final variable must be initialized \ No newline at end of file +Main1.hx:2: characters 2-17 : Static final variable must be initialized \ No newline at end of file diff --git a/tests/misc/projects/Issue6584/compile2-fail.hxml.stderr b/tests/misc/projects/Issue6584/compile2-fail.hxml.stderr index 72019de5..752bd5a0 100644 --- a/tests/misc/projects/Issue6584/compile2-fail.hxml.stderr +++ b/tests/misc/projects/Issue6584/compile2-fail.hxml.stderr @@ -1 +1 @@ -Main2.hx:5: characters 3-8 : Cannot access field or identifier v for writing \ No newline at end of file +Main2.hx:5: characters 3-4 : This expression cannot be accessed for writing \ No newline at end of file diff --git a/tests/misc/projects/Issue6584/compile3-fail.hxml.stderr b/tests/misc/projects/Issue6584/compile3-fail.hxml.stderr index 68360d33..db932d61 100644 --- a/tests/misc/projects/Issue6584/compile3-fail.hxml.stderr +++ b/tests/misc/projects/Issue6584/compile3-fail.hxml.stderr @@ -1 +1 @@ -Main3.hx:9: characters 3-8 : Cannot access field or identifier v for writing \ No newline at end of file +Main3.hx:9: characters 3-4 : This expression cannot be accessed for writing \ No newline at end of file diff --git a/tests/misc/projects/Issue6584/compile4-fail.hxml.stderr b/tests/misc/projects/Issue6584/compile4-fail.hxml.stderr index 6ecdd03b..5dbf36b5 100644 --- a/tests/misc/projects/Issue6584/compile4-fail.hxml.stderr +++ b/tests/misc/projects/Issue6584/compile4-fail.hxml.stderr @@ -1 +1,2 @@ -Main4.hx:2: characters 2-14 : final field y must be initialized immediately or in the constructor \ No newline at end of file +Main4.hx:1: characters 7-11 : Some final fields are uninitialized in this class +Main4.hx:2: characters 8-9 : Uninitialized field \ No newline at end of file diff --git a/tests/misc/projects/Issue6584/pretty-fail.hxml b/tests/misc/projects/Issue6584/pretty-fail.hxml new file mode 100644 index 00000000..bfe824aa --- /dev/null +++ b/tests/misc/projects/Issue6584/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile5-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue6584/pretty-fail.hxml.stderr b/tests/misc/projects/Issue6584/pretty-fail.hxml.stderr new file mode 100644 index 00000000..95320545 --- /dev/null +++ b/tests/misc/projects/Issue6584/pretty-fail.hxml.stderr @@ -0,0 +1,14 @@ +[ERROR] Main5.hx:1: lines 1-6 + + 1 | class Main { + - | [...] + 6 | } + | + | This class has uninitialized final vars, which requires a constructor + +[ERROR] Main5.hx:2: characters 8-9 + + 2 | final v:Int; + | ^ + | Example of an uninitialized final var + diff --git a/tests/misc/projects/Issue6790/pretty-fail.hxml b/tests/misc/projects/Issue6790/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue6790/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue6790/pretty-fail.hxml.stderr b/tests/misc/projects/Issue6790/pretty-fail.hxml.stderr new file mode 100644 index 00000000..8b523e36 --- /dev/null +++ b/tests/misc/projects/Issue6790/pretty-fail.hxml.stderr @@ -0,0 +1,7 @@ +[ERROR] Mismatch.hx:6: characters 19-26 + + 6 | p.then(x -> 10, e -> ""); + | ^^^^^^^ + | (e : Unknown<0>) -> String should be Null> + | For optional function argument 'onRejected' + diff --git a/tests/misc/projects/Issue6794/Main.hx b/tests/misc/projects/Issue6794/Main.hx new file mode 100644 index 00000000..59325374 --- /dev/null +++ b/tests/misc/projects/Issue6794/Main.hx @@ -0,0 +1,17 @@ +#if macro +import haxe.macro.Expr; +import haxe.macro.Context; +#end + +class Main { + #if !macro + static function main() + test(); + #end + + static macro function test() { + try Context.typeof(macro foo) + catch (e) Context.warning("foo", (macro 0).pos); + return macro {}; + } +} diff --git a/tests/misc/projects/Issue6794/compile.hxml b/tests/misc/projects/Issue6794/compile.hxml new file mode 100644 index 00000000..26d8f1e5 --- /dev/null +++ b/tests/misc/projects/Issue6794/compile.hxml @@ -0,0 +1,2 @@ +--main Main +--display "Main.hx@0@diagnostics" diff --git a/tests/misc/projects/Issue6794/compile.hxml.stderr b/tests/misc/projects/Issue6794/compile.hxml.stderr new file mode 100644 index 00000000..fdbde424 --- /dev/null +++ b/tests/misc/projects/Issue6794/compile.hxml.stderr @@ -0,0 +1 @@ +[{"file":"$$normPath(::cwd::/,true)Main.hx","diagnostics":[{"kind":2,"severity":2,"range":{"start":{"line":13,"character":42},"end":{"line":13,"character":43}},"args":"foo","relatedInformation":[]}]}] diff --git a/tests/misc/projects/Issue6796/pretty-fail.hxml b/tests/misc/projects/Issue6796/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue6796/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue6796/pretty-fail.hxml.stderr b/tests/misc/projects/Issue6796/pretty-fail.hxml.stderr new file mode 100644 index 00000000..2b8127e3 --- /dev/null +++ b/tests/misc/projects/Issue6796/pretty-fail.hxml.stderr @@ -0,0 +1,7 @@ +[ERROR] Main.hx:3: characters 21-25 + + 3 | Sys.println(main["foo"]); + | ^^^^ + | Array access is not allowed on () -> Unknown<0> + | For function argument 'v' + diff --git a/tests/misc/projects/Issue6810/compile-fail.hxml.stderr b/tests/misc/projects/Issue6810/compile-fail.hxml.stderr index 999b38b7..9b8975b0 100644 --- a/tests/misc/projects/Issue6810/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue6810/compile-fail.hxml.stderr @@ -1,8 +1,6 @@ Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid Fail.hx:7: characters 8-12 : ... have: (...) -> Void Fail.hx:7: characters 8-12 : ... want: (...) -> haxe.NotVoid -Fail.hx:7: characters 8-12 : ... For function argument 'f' Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid Fail.hx:8: characters 8-16 : ... have: (...) -> FakeVoid Fail.hx:8: characters 8-16 : ... want: (...) -> haxe.NotVoid -Fail.hx:8: characters 8-16 : ... For function argument 'f' \ No newline at end of file diff --git a/tests/misc/projects/Issue6810/indent-fail.hxml b/tests/misc/projects/Issue6810/indent-fail.hxml new file mode 100644 index 00000000..1a2ff3dc --- /dev/null +++ b/tests/misc/projects/Issue6810/indent-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue6810/indent-fail.hxml.stderr b/tests/misc/projects/Issue6810/indent-fail.hxml.stderr new file mode 100644 index 00000000..933eedd4 --- /dev/null +++ b/tests/misc/projects/Issue6810/indent-fail.hxml.stderr @@ -0,0 +1,6 @@ +Fail.hx:7: characters 8-12 : error: Void should be haxe.NotVoid + Fail.hx:7: characters 8-12 : have: (...) -> Void + Fail.hx:7: characters 8-12 : want: (...) -> haxe.NotVoid +Fail.hx:8: characters 8-16 : error: FakeVoid should be haxe.NotVoid + Fail.hx:8: characters 8-16 : have: (...) -> FakeVoid + Fail.hx:8: characters 8-16 : want: (...) -> haxe.NotVoid diff --git a/tests/misc/projects/Issue6810/logfile-01-fail.hxml b/tests/misc/projects/Issue6810/logfile-01-fail.hxml new file mode 100644 index 00000000..3f92af70 --- /dev/null +++ b/tests/misc/projects/Issue6810/logfile-01-fail.hxml @@ -0,0 +1,2 @@ +-D messages-log-file=logfile-02-fail.hxml.stderr +compile-fail.hxml diff --git a/tests/misc/projects/Issue6810/logfile-02-fail.hxml b/tests/misc/projects/Issue6810/logfile-02-fail.hxml new file mode 100644 index 00000000..6da45668 --- /dev/null +++ b/tests/misc/projects/Issue6810/logfile-02-fail.hxml @@ -0,0 +1,4 @@ +-D messages-log-file=logfile-03-fail.hxml.stderr +-D messages-log-format=pretty +-D message-reporting=indent +compile-fail.hxml diff --git a/tests/misc/projects/Issue6810/logfile-03-fail.hxml b/tests/misc/projects/Issue6810/logfile-03-fail.hxml new file mode 100644 index 00000000..da03d44b --- /dev/null +++ b/tests/misc/projects/Issue6810/logfile-03-fail.hxml @@ -0,0 +1,4 @@ +-D messages-log-file=logfile-04-fail.hxml.stderr +-D messages-log-format=classic +-D message-reporting=pretty +compile-fail.hxml diff --git a/tests/misc/projects/Issue6810/logfile-04-fail.hxml b/tests/misc/projects/Issue6810/logfile-04-fail.hxml new file mode 100644 index 00000000..cd2549fd --- /dev/null +++ b/tests/misc/projects/Issue6810/logfile-04-fail.hxml @@ -0,0 +1 @@ +compile-fail.hxml diff --git a/tests/misc/projects/Issue6810/pretty-fail.hxml b/tests/misc/projects/Issue6810/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue6810/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue6810/pretty-fail.hxml.stderr b/tests/misc/projects/Issue6810/pretty-fail.hxml.stderr new file mode 100644 index 00000000..f67eb449 --- /dev/null +++ b/tests/misc/projects/Issue6810/pretty-fail.hxml.stderr @@ -0,0 +1,16 @@ +[ERROR] Fail.hx:7: characters 8-12 + + 7 | test(void); + | ^^^^ + | error: Void should be haxe.NotVoid + | have: (...) -> Void + | want: (...) -> haxe.NotVoid + +[ERROR] Fail.hx:8: characters 8-16 + + 8 | test(fakeVoid); + | ^^^^^^^^ + | error: FakeVoid should be haxe.NotVoid + | have: (...) -> FakeVoid + | want: (...) -> haxe.NotVoid + diff --git a/tests/misc/projects/Issue6903/compile-fail.hxml.stderr b/tests/misc/projects/Issue6903/compile-fail.hxml.stderr index 8a7b92fd..d261b7d0 100644 --- a/tests/misc/projects/Issue6903/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue6903/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:6: lines 6-8 : opAssign: Assignment overloading is not supported \ No newline at end of file +Main.hx:6: lines 6-8 : Assignment overloading is not supported \ No newline at end of file diff --git a/tests/misc/projects/Issue6938/Main1.hx b/tests/misc/projects/Issue6938/Main1.hx new file mode 100644 index 00000000..74869575 --- /dev/null +++ b/tests/misc/projects/Issue6938/Main1.hx @@ -0,0 +1,25 @@ +enum ScreenMode { + AAA; + AAB; +} +enum abstract ScreenRatio(Int) { + var ZZA; + var ZZB; +} + +class Main { + static final AAF = 0; + static final ZZF = 0; + static function main() { + var mode = ScreenMode.AAA; + switch(mode) { + case ScreenMoze.AAZ: + case _: + } + var mode2 = ScreenRatio.ZZA; + switch(mode2) { + case ScreenRazio.ZZZ: + case _: + } + } +} diff --git a/tests/misc/projects/Issue6938/compile1-fail.hxml b/tests/misc/projects/Issue6938/compile1-fail.hxml new file mode 100644 index 00000000..418317cd --- /dev/null +++ b/tests/misc/projects/Issue6938/compile1-fail.hxml @@ -0,0 +1,2 @@ +--main Main1 +--interp diff --git a/tests/misc/projects/Issue6938/compile1-fail.hxml.stderr b/tests/misc/projects/Issue6938/compile1-fail.hxml.stderr new file mode 100644 index 00000000..c928d4cd --- /dev/null +++ b/tests/misc/projects/Issue6938/compile1-fail.hxml.stderr @@ -0,0 +1,4 @@ +Main1.hx:16: characters 9-23 : Warning : Potential typo detected (expected similar values are ScreenMode.AAA, ScreenMode.AAB) +Main1.hx:16: characters 9-23 : Only inline or read-only (default, never) fields can be used as a pattern +Main1.hx:21: characters 9-24 : Warning : Potential typo detected (expected similar values are ScreenRatio.ZZA, ScreenRatio.ZZB) +Main1.hx:21: characters 9-24 : Only inline or read-only (default, never) fields can be used as a pattern diff --git a/tests/misc/projects/Issue7096/compile.hxml.stderr b/tests/misc/projects/Issue7096/compile.hxml.stderr index 4e435f31..9e2a163c 100644 --- a/tests/misc/projects/Issue7096/compile.hxml.stderr +++ b/tests/misc/projects/Issue7096/compile.hxml.stderr @@ -1 +1 @@ -Main.hx:5: characters 9-10 : Warning : Usage of this field is deprecated \ No newline at end of file +Main.hx:5: characters 9-10 : Warning : (WDeprecated) Usage of this field is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue7447/Main.hx b/tests/misc/projects/Issue7447/Main.hx index bc8869dd..99ff8cfe 100644 --- a/tests/misc/projects/Issue7447/Main.hx +++ b/tests/misc/projects/Issue7447/Main.hx @@ -8,5 +8,6 @@ class Main { abstract Abstr(Int) { public inline function new() { trace(() -> this); + this = 0; } } \ No newline at end of file diff --git a/tests/misc/projects/Issue7447/compile.hxml.stderr b/tests/misc/projects/Issue7447/compile.hxml.stderr index 7c5d4eb3..f10ea255 100644 --- a/tests/misc/projects/Issue7447/compile.hxml.stderr +++ b/tests/misc/projects/Issue7447/compile.hxml.stderr @@ -1,2 +1,2 @@ -Main.hx:4: characters 15-16 : Warning : Local variable v might be used before being initialized -Main.hx:10: characters 15-19 : Warning : this might be used before assigning a value to it \ No newline at end of file +Main.hx:4: characters 15-16 : Warning : (WVarInit) Local variable v might be used before being initialized +Main.hx:10: characters 15-19 : Warning : (WVarInit) this might be used before assigning a value to it \ No newline at end of file diff --git a/tests/misc/projects/Issue7614/compile-fail.hxml.stderr b/tests/misc/projects/Issue7614/compile-fail.hxml.stderr index 412cae3d..948c2d9f 100644 --- a/tests/misc/projects/Issue7614/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue7614/compile-fail.hxml.stderr @@ -1,3 +1,2 @@ Main.hx:13: characters 1-24 : Abstract chain detected: Test2 -> Test -> Test2 -Main.hx:6: lines 6-10 : ... Defined in this class Main.hx:6: lines 6-10 : Abstract underlying type cannot be recursive \ No newline at end of file diff --git a/tests/misc/projects/Issue7638/compile-fail.hxml.stderr b/tests/misc/projects/Issue7638/compile-fail.hxml.stderr index 6d437cf5..53c2e1b0 100644 --- a/tests/misc/projects/Issue7638/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue7638/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:13: characters 3-18 : Cannot call super.dynMethod since it's a dynamic method \ No newline at end of file +Main.hx:13: characters 3-20 : Cannot call super.dynMethod since it's a dynamic method \ No newline at end of file diff --git a/tests/misc/projects/Issue7669/Main.hx b/tests/misc/projects/Issue7669/Main.hx new file mode 100644 index 00000000..267a6a62 --- /dev/null +++ b/tests/misc/projects/Issue7669/Main.hx @@ -0,0 +1,12 @@ +class Main { + static function main() { + switch Std.random(0) { + case NumpadSubtract: + case NumpadDecimal: + } + switch Std.random(0) { + case 1, 2, NumpadSubtract: + case _: + } + } +} diff --git a/tests/misc/projects/Issue7669/compile-fail.hxml b/tests/misc/projects/Issue7669/compile-fail.hxml new file mode 100644 index 00000000..b30a7558 --- /dev/null +++ b/tests/misc/projects/Issue7669/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue7669/compile-fail.hxml.stderr b/tests/misc/projects/Issue7669/compile-fail.hxml.stderr new file mode 100644 index 00000000..62360ce2 --- /dev/null +++ b/tests/misc/projects/Issue7669/compile-fail.hxml.stderr @@ -0,0 +1,5 @@ +Main.hx:4: characters 9-23 : Unknown identifier : NumpadSubtract, pattern variables must be lower-case or with `var ` prefix +Main.hx:5: characters 9-22 : Unknown identifier : NumpadDecimal, pattern variables must be lower-case or with `var ` prefix +Main.hx:5: characters 4-23 : Warning : (WUnusedPattern) This case is unused +Main.hx:8: characters 15-29 : Unknown identifier : NumpadSubtract, pattern variables must be lower-case or with `var ` prefix +Main.hx:8: characters 15-29 : Variable NumpadSubtract must appear exactly once in each sub-pattern diff --git a/tests/misc/projects/Issue7871/Macro.hx b/tests/misc/projects/Issue7871/Macro.hx new file mode 100644 index 00000000..2ff336cb --- /dev/null +++ b/tests/misc/projects/Issue7871/Macro.hx @@ -0,0 +1,19 @@ +import haxe.macro.Compiler; +import haxe.macro.Context; + +function init() { + var e = macro 42; + Context.typeof(e); + if (Context.defined("haxe")) Context.warning("ok", (macro 0).pos); + Compiler.define("foo", "foo"); + + Context.onAfterInitMacros(() -> { + Context.warning("after init 1", (macro 0).pos); + Compiler.define("bar", "bar"); + Context.typeof(e); + }); + Compiler.include("hax.ds", true, true); + Context.onAfterInitMacros(() -> { + Context.warning("after init 2", (macro 0).pos); + }); +} diff --git a/tests/misc/projects/Issue7871/compile-fail.hxml b/tests/misc/projects/Issue7871/compile-fail.hxml new file mode 100644 index 00000000..a838b3a0 --- /dev/null +++ b/tests/misc/projects/Issue7871/compile-fail.hxml @@ -0,0 +1 @@ +--macro Macro.init() diff --git a/tests/misc/projects/Issue7871/compile-fail.hxml.stderr b/tests/misc/projects/Issue7871/compile-fail.hxml.stderr new file mode 100644 index 00000000..eccec245 --- /dev/null +++ b/tests/misc/projects/Issue7871/compile-fail.hxml.stderr @@ -0,0 +1,5 @@ +Macro.hx:7: characters 60-61 : Warning : ok +Macro.hx:11: characters 42-43 : Warning : after init 1 +Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros. +Package "hax.ds" was not found in any of class paths +Macro.hx:17: characters 42-43 : Warning : after init 2 diff --git a/tests/misc/projects/Issue7871/compile-next-fail.hxml b/tests/misc/projects/Issue7871/compile-next-fail.hxml new file mode 100644 index 00000000..f4272930 --- /dev/null +++ b/tests/misc/projects/Issue7871/compile-next-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D haxe-next diff --git a/tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr b/tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr new file mode 100644 index 00000000..76209e88 --- /dev/null +++ b/tests/misc/projects/Issue7871/compile-next-fail.hxml.stderr @@ -0,0 +1,7 @@ +Macro.hx:6: characters 2-19 : Warning : Cannot use this API from initialization macros. +Macro.hx:6: characters 2-19 : ... Use `Context.onAfterInitMacros` to register a callback to run when context is ready. +Macro.hx:7: characters 60-61 : Warning : ok +Macro.hx:11: characters 42-43 : Warning : after init 1 +Macro.hx:12: characters 3-32 : Warning : This API should only be used from initialization macros. +Package "hax.ds" was not found in any of class paths +Macro.hx:17: characters 42-43 : Warning : after init 2 diff --git a/tests/misc/projects/Issue7935/compile3-fail.hxml.stderr b/tests/misc/projects/Issue7935/compile3-fail.hxml.stderr index 6694b1fb..dff53d9c 100644 --- a/tests/misc/projects/Issue7935/compile3-fail.hxml.stderr +++ b/tests/misc/projects/Issue7935/compile3-fail.hxml.stderr @@ -1 +1 @@ -Main3.hx:4: characters 2-3 : Unexpected } \ No newline at end of file +Main3.hx:4: characters 2-3 : Expected , or ) diff --git a/tests/misc/projects/Issue7968/pretty-fail.hxml b/tests/misc/projects/Issue7968/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue7968/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue7968/pretty-fail.hxml.stderr b/tests/misc/projects/Issue7968/pretty-fail.hxml.stderr new file mode 100644 index 00000000..cb56d866 --- /dev/null +++ b/tests/misc/projects/Issue7968/pretty-fail.hxml.stderr @@ -0,0 +1,10 @@ +[ERROR] Foo.hx:2: characters 1-18 + + 2 | typedef A = Float; + | ^^^^^^^^^^^^^^^^^ + | Name A is already defined in this module + + 1 | typedef A = Int; + | ^ + | Previous declaration here + diff --git a/tests/misc/projects/Issue8173/compile-fail.hxml.stderr b/tests/misc/projects/Issue8173/compile-fail.hxml.stderr index 1fc6a39f..c7d7d364 100644 --- a/tests/misc/projects/Issue8173/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue8173/compile-fail.hxml.stderr @@ -1,2 +1,2 @@ -Main.hx:2: characters 2-7 : Static method cannot be final +Main.hx:2: characters 2-7 : Invalid modifier combination: final + static Main.hx:9: lines 9-11 : Cannot override final constructor \ No newline at end of file diff --git a/tests/misc/projects/Issue8303/indent-fail.hxml b/tests/misc/projects/Issue8303/indent-fail.hxml new file mode 100644 index 00000000..1a2ff3dc --- /dev/null +++ b/tests/misc/projects/Issue8303/indent-fail.hxml @@ -0,0 +1,2 @@ +compile-fail.hxml +-D message-reporting=indent diff --git a/tests/misc/projects/Issue8303/indent-fail.hxml.stderr b/tests/misc/projects/Issue8303/indent-fail.hxml.stderr new file mode 100644 index 00000000..85bcacbd --- /dev/null +++ b/tests/misc/projects/Issue8303/indent-fail.hxml.stderr @@ -0,0 +1,21 @@ +Uncaught exception Stack overflow + Main.hx:1: character 1 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:8: characters 4-9 : Called from here + Main.hx:10: characters 3-8 : Called from here + Main.hx:3: characters 3-9 : Called from here diff --git a/tests/misc/projects/Issue8303/pretty-fail.hxml b/tests/misc/projects/Issue8303/pretty-fail.hxml new file mode 100644 index 00000000..e9a41663 --- /dev/null +++ b/tests/misc/projects/Issue8303/pretty-fail.hxml @@ -0,0 +1,3 @@ +compile-fail.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue8303/pretty-fail.hxml.stderr b/tests/misc/projects/Issue8303/pretty-fail.hxml.stderr new file mode 100644 index 00000000..f3f273aa --- /dev/null +++ b/tests/misc/projects/Issue8303/pretty-fail.hxml.stderr @@ -0,0 +1,54 @@ +[ERROR] (unknown position) + + | Uncaught exception Stack overflow + + -> Main.hx:1: character 1 + + 1 | class Main { + | ^ + | Called from here + + 8 | log(); + | ^^^^^ + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + | Called from here + + 10 | log(); + | ^^^^^ + | Called from here + + 3 | test(); + | ^^^^^^ + | Called from here + diff --git a/tests/misc/projects/Issue8471/compile2-pretty.hxml b/tests/misc/projects/Issue8471/compile2-pretty.hxml new file mode 100644 index 00000000..f2c70f50 --- /dev/null +++ b/tests/misc/projects/Issue8471/compile2-pretty.hxml @@ -0,0 +1,3 @@ +compile2.hxml +-D message-reporting=pretty +-D no-color diff --git a/tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr b/tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr new file mode 100644 index 00000000..57a1826c --- /dev/null +++ b/tests/misc/projects/Issue8471/compile2-pretty.hxml.stderr @@ -0,0 +1,22 @@ +[WARNING] Macro2.hx:12: characters 25-39 + + 12 | Context.warning(("1" :DeprecatedType), Context.currentPos()); + | ^^^^^^^^^^^^^^ + | (WDeprecated) This typedef is deprecated in favor of String + +[WARNING] (unknown position) + + | 1 + +[WARNING] (unknown position) + + | 2 + +[WARNING] (unknown position) + + | 3 + +[WARNING] (unknown position) + + | i123|i123 + diff --git a/tests/misc/projects/Issue8471/compile2.hxml.stderr b/tests/misc/projects/Issue8471/compile2.hxml.stderr index f0f79d26..c3b0af5f 100644 --- a/tests/misc/projects/Issue8471/compile2.hxml.stderr +++ b/tests/misc/projects/Issue8471/compile2.hxml.stderr @@ -1,4 +1,4 @@ -Macro2.hx:12: characters 25-39 : Warning : This typedef is deprecated in favor of String +Macro2.hx:12: characters 25-39 : Warning : (WDeprecated) This typedef is deprecated in favor of String Warning : 1 Warning : 2 Warning : 3 diff --git a/tests/misc/projects/Issue8517/compile-fail.hxml.stderr b/tests/misc/projects/Issue8517/compile-fail.hxml.stderr index 98d9bb33..d350c581 100644 --- a/tests/misc/projects/Issue8517/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue8517/compile-fail.hxml.stderr @@ -1,4 +1,3 @@ Macro.hx:3: characters 60-69 : Invalid assign -Fail.hx:1: lines 1-7 : ... Defined in this class Fail.hx:4: characters 3-12 : Invalid assign Fail.hx:5: characters 3-12 : Invalid assign \ No newline at end of file diff --git a/tests/misc/projects/Issue8551/compile-fail.hxml.stderr b/tests/misc/projects/Issue8551/compile-fail.hxml.stderr index c8c8e932..b62bebe0 100644 --- a/tests/misc/projects/Issue8551/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue8551/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:6: characters 2-7 : Macro abstract constructors are not supported \ No newline at end of file +Main.hx:6: characters 2-7 : Invalid modifier: macro on abstract constructor \ No newline at end of file diff --git a/tests/misc/projects/Issue8634/Main.hx b/tests/misc/projects/Issue8634/Main.hx new file mode 100644 index 00000000..e531455b --- /dev/null +++ b/tests/misc/projects/Issue8634/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + var s:Null = null; + trace(s = null ? "null" : "not null"); + } +} diff --git a/tests/misc/projects/Issue8634/compile-fail.hxml b/tests/misc/projects/Issue8634/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue8634/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue8634/compile-fail.hxml.stderr b/tests/misc/projects/Issue8634/compile-fail.hxml.stderr new file mode 100644 index 00000000..7fba5a66 --- /dev/null +++ b/tests/misc/projects/Issue8634/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 13-17 : Cannot use null as ternary condition \ No newline at end of file diff --git a/tests/misc/projects/Issue8828/compile1-fail.hxml.stderr b/tests/misc/projects/Issue8828/compile1-fail.hxml.stderr index 5558e4f5..fd8a5f30 100644 --- a/tests/misc/projects/Issue8828/compile1-fail.hxml.stderr +++ b/tests/misc/projects/Issue8828/compile1-fail.hxml.stderr @@ -1 +1 @@ -Main1.hx:2: characters 2-8 : extern modifier is not allowed on enum abstract fields \ No newline at end of file +Main1.hx:2: characters 2-8 : Invalid modifier: extern on field of enum abstract \ No newline at end of file diff --git a/tests/misc/projects/Issue9014/compile-fail.hxml.stderr b/tests/misc/projects/Issue9014/compile-fail.hxml.stderr index 2b25ed33..a44015e9 100644 --- a/tests/misc/projects/Issue9014/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9014/compile-fail.hxml.stderr @@ -1,2 +1,2 @@ -Main.hx:3: characters 2-40 : foo: 'override' is not allowed on 'static' functions -Main.hx:4: characters 2-39 : bar: 'inline' is not allowed on 'dynamic' functions +Main.hx:3: characters 2-40 : Invalid modifier combination: override + static +Main.hx:4: characters 2-39 : Invalid modifier combination: dynamic + inline diff --git a/tests/misc/projects/Issue9017/compile-fail.hxml.stderr b/tests/misc/projects/Issue9017/compile-fail.hxml.stderr index 79927858..d8624c27 100644 --- a/tests/misc/projects/Issue9017/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9017/compile-fail.hxml.stderr @@ -1,3 +1,2 @@ Main.hx:8: characters 1-13 : @:structInit is not allowed on interfaces -Main.hx:9: lines 9-11 : ... Defined in this class Main.hx:3: characters 15-26 : Foo does not have a constructor \ No newline at end of file diff --git a/tests/misc/projects/Issue9163/Main.hx b/tests/misc/projects/Issue9163/Main.hx new file mode 100644 index 00000000..d4c7a0f2 --- /dev/null +++ b/tests/misc/projects/Issue9163/Main.hx @@ -0,0 +1,12 @@ +class Main { + static function main() { + var foo = '5'; + var bar = '5'; + var isEqual = switch (bar) { + case 'foo$ foo': true; + case '\\ foo': true; + case '$foo': true; + default: false; + } + } +} diff --git a/tests/misc/projects/Issue9163/compile-fail.hxml b/tests/misc/projects/Issue9163/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/Issue9163/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/Issue9163/compile-fail.hxml.stderr b/tests/misc/projects/Issue9163/compile-fail.hxml.stderr new file mode 100644 index 00000000..03916a21 --- /dev/null +++ b/tests/misc/projects/Issue9163/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:8: characters 10-14 : String interpolation is not allowed in case patterns diff --git a/tests/misc/projects/Issue9171/Main.hx b/tests/misc/projects/Issue9171/Main.hx new file mode 100644 index 00000000..14a31eb1 --- /dev/null +++ b/tests/misc/projects/Issue9171/Main.hx @@ -0,0 +1,11 @@ +class C { + final x:Int; + + function new() { + x = nonsense; + } +} + +function main() { + +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9171/compile-fail.hxml b/tests/misc/projects/Issue9171/compile-fail.hxml new file mode 100644 index 00000000..cafa855d --- /dev/null +++ b/tests/misc/projects/Issue9171/compile-fail.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp \ No newline at end of file diff --git a/tests/misc/projects/Issue9171/compile-fail.hxml.stderr b/tests/misc/projects/Issue9171/compile-fail.hxml.stderr new file mode 100644 index 00000000..c910f2b2 --- /dev/null +++ b/tests/misc/projects/Issue9171/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:5: characters 7-15 : Unknown identifier : nonsense \ No newline at end of file diff --git a/tests/misc/projects/Issue9192/compile.hxml.stderr b/tests/misc/projects/Issue9192/compile.hxml.stderr index bff3db89..1b42ce93 100644 --- a/tests/misc/projects/Issue9192/compile.hxml.stderr +++ b/tests/misc/projects/Issue9192/compile.hxml.stderr @@ -1 +1 @@ -Main.hx:3: characters 3-4 : Warning : Usage of this class is deprecated \ No newline at end of file +Main.hx:3: characters 3-4 : Warning : (WDeprecated) Usage of this class is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue9204/compile.hxml.stderr b/tests/misc/projects/Issue9204/compile.hxml.stderr index c0df8bc8..ecad5a96 100644 --- a/tests/misc/projects/Issue9204/compile.hxml.stderr +++ b/tests/misc/projects/Issue9204/compile.hxml.stderr @@ -1 +1 @@ -Main.hx:7: characters 9-13 : Warning : Usage of this field is deprecated \ No newline at end of file +Main.hx:7: characters 9-13 : Warning : (WDeprecated) Usage of this field is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue9378/compile-fail.hxml.stderr b/tests/misc/projects/Issue9378/compile-fail.hxml.stderr index bd536e66..4937450f 100644 --- a/tests/misc/projects/Issue9378/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9378/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:10: characters 3-17 : Cannot access field or identifier x for writing \ No newline at end of file +Main.hx:10: characters 11-12 : This expression cannot be accessed for writing \ No newline at end of file diff --git a/tests/misc/projects/Issue9381/FinalDisplay.hx b/tests/misc/projects/Issue9381/FinalDisplay.hx new file mode 100644 index 00000000..f38ec5d3 --- /dev/null +++ b/tests/misc/projects/Issue9381/FinalDisplay.hx @@ -0,0 +1,2 @@ +@:autoBuild(FinalDisplayBuilder.build()) +interface FinalDisplay {} \ No newline at end of file diff --git a/tests/misc/projects/Issue9381/FinalDisplayBuilder.hx b/tests/misc/projects/Issue9381/FinalDisplayBuilder.hx new file mode 100644 index 00000000..e27ecb10 --- /dev/null +++ b/tests/misc/projects/Issue9381/FinalDisplayBuilder.hx @@ -0,0 +1,15 @@ +import haxe.macro.Context; + +class FinalDisplayBuilder { + #if macro + public static function build() { + var cls = Context.getLocalClass().get(); + if (!cls.isFinal) { + Context.fatalError('class needs to be final', cls.pos); + } else { + Sys.stderr().writeString(cls.name + " is very final\n"); + } + return null; + } + #end +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9381/Main.hx b/tests/misc/projects/Issue9381/Main.hx new file mode 100644 index 00000000..b1a2c930 --- /dev/null +++ b/tests/misc/projects/Issue9381/Main.hx @@ -0,0 +1,4 @@ +final class FinalDisplayTest implements FinalDisplay {} // here it works fine +final class FinalDisplayTestFailed implements FinalDisplay {} // seconds class shows: class needs to be final diagnostics(2) + +function main() {} \ No newline at end of file diff --git a/tests/misc/projects/Issue9381/compile.hxml b/tests/misc/projects/Issue9381/compile.hxml new file mode 100644 index 00000000..20afe58f --- /dev/null +++ b/tests/misc/projects/Issue9381/compile.hxml @@ -0,0 +1 @@ +--main Main diff --git a/tests/misc/projects/Issue9381/compile.hxml.stderr b/tests/misc/projects/Issue9381/compile.hxml.stderr new file mode 100644 index 00000000..22b18bd3 --- /dev/null +++ b/tests/misc/projects/Issue9381/compile.hxml.stderr @@ -0,0 +1,2 @@ +FinalDisplayTestFailed is very final +FinalDisplayTest is very final \ No newline at end of file diff --git a/tests/misc/projects/Issue9391/compile-fail.hxml.stderr b/tests/misc/projects/Issue9391/compile-fail.hxml.stderr index ff677a30..e5cfc941 100644 --- a/tests/misc/projects/Issue9391/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9391/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:8: characters 9-20 : Foo is missing @:genericBuild meta. Was it removed by a macro? \ No newline at end of file +Main.hx:9: characters 9-17 : Foo is missing @:genericBuild meta. Was it removed by a macro? \ No newline at end of file diff --git a/tests/misc/projects/Issue9425/compile-success.hxml.stderr b/tests/misc/projects/Issue9425/compile-success.hxml.stderr index f8f05e62..cffc4930 100644 --- a/tests/misc/projects/Issue9425/compile-success.hxml.stderr +++ b/tests/misc/projects/Issue9425/compile-success.hxml.stderr @@ -1 +1 @@ -Main.hx:10: characters 3-14 : Warning : Usage of this field is deprecated \ No newline at end of file +Main.hx:10: characters 3-14 : Warning : (WDeprecated) Usage of this field is deprecated \ No newline at end of file diff --git a/tests/misc/projects/Issue9584/Main.hx b/tests/misc/projects/Issue9584/Main.hx new file mode 100644 index 00000000..8e1a8994 --- /dev/null +++ b/tests/misc/projects/Issue9584/Main.hx @@ -0,0 +1,8 @@ +class Main { + static function main() { + final foo:Array<{x:Int, y:Int}> = [ + {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, + {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0, y: 1}, {x: 0,} + ]; + } +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9584/compile-fail.hxml b/tests/misc/projects/Issue9584/compile-fail.hxml new file mode 100644 index 00000000..c098216e --- /dev/null +++ b/tests/misc/projects/Issue9584/compile-fail.hxml @@ -0,0 +1 @@ +Main diff --git a/tests/misc/projects/Issue9584/compile-fail.hxml.stderr b/tests/misc/projects/Issue9584/compile-fail.hxml.stderr new file mode 100644 index 00000000..3225c01a --- /dev/null +++ b/tests/misc/projects/Issue9584/compile-fail.hxml.stderr @@ -0,0 +1,3 @@ +Main.hx:5: characters 74-81 : Object requires field y +Main.hx:5: characters 74-81 : { x : Int } should be { y : Int, x : Int } +Main.hx:5: characters 74-81 : ... { x : Int } has no field y \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr index e490d699..43953e80 100644 --- a/tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9619/abstract-dynamic/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 2-52 : Abstract methods may not be dynamic \ No newline at end of file +Main.hx:2: characters 2-52 : Invalid modifier combination: abstract + dynamic \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr index 7bd4efa9..70d65a30 100644 --- a/tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9619/abstract-expression-inline/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:8: characters 3-30 : Cannot force inline on abstract method \ No newline at end of file +Main.hx:8: characters 3-9 : Cannot force inline on abstract method \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr index 412d5487..1a8ab861 100644 --- a/tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9619/abstract-final/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 26-42 : Abstract methods may not be final \ No newline at end of file +Main.hx:2: characters 26-42 : Invalid modifier combination: abstract + final \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr index 79dd8310..c6a42459 100644 --- a/tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9619/abstract-inline/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 34-50 : Abstract methods may not be inline \ No newline at end of file +Main.hx:2: characters 34-50 : Invalid modifier combination: abstract + inline \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr index c9f4b967..2bcfc269 100644 --- a/tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9619/abstract-static/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 27-43 : Static methods may not be abstract \ No newline at end of file +Main.hx:2: characters 27-43 : Invalid modifier combination: abstract + static \ No newline at end of file diff --git a/tests/misc/projects/Issue9619/abstract-structInit/Main.hx b/tests/misc/projects/Issue9619/abstract-structInit/Main.hx deleted file mode 100644 index 065e17f7..00000000 --- a/tests/misc/projects/Issue9619/abstract-structInit/Main.hx +++ /dev/null @@ -1,9 +0,0 @@ -@:structInit abstract class Abstract { - -} - -class Main extends Abstract { - static function main() { - - } -} diff --git a/tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr b/tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr deleted file mode 100644 index 44c41829..00000000 --- a/tests/misc/projects/Issue9619/abstract-structInit/compile-fail.hxml.stderr +++ /dev/null @@ -1,2 +0,0 @@ -Main.hx:1: characters 1-13 : @:structInit is not allowed on abstract classes -Main.hx:1: lines 1-3 : ... Defined in this class \ No newline at end of file diff --git a/tests/misc/projects/Issue9640/compile-fail.hxml.stderr b/tests/misc/projects/Issue9640/compile-fail.hxml.stderr index 512f91b3..c3ca086b 100644 --- a/tests/misc/projects/Issue9640/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9640/compile-fail.hxml.stderr @@ -2,4 +2,4 @@ Main.hx:5: characters 9-13 : Warning : Mono> Main.hx:8: characters 3-36 : Constraint check failure for new.B Main.hx:8: characters 3-36 : ... Foo should be BarLike Main.hx:8: characters 3-36 : ... Foo has no field bar -Main.hx:10: characters 9-13 : Warning : Mono> +Main.hx:10: characters 9-13 : Warning : Mono> \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/MainExpr.hx b/tests/misc/projects/Issue9676/MainExpr.hx new file mode 100644 index 00000000..dd23c73b --- /dev/null +++ b/tests/misc/projects/Issue9676/MainExpr.hx @@ -0,0 +1,3 @@ +function main() { + final var value:String; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/MainField.hx b/tests/misc/projects/Issue9676/MainField.hx new file mode 100644 index 00000000..1f7ef53f --- /dev/null +++ b/tests/misc/projects/Issue9676/MainField.hx @@ -0,0 +1,5 @@ +class MainField { + static final var value = "1"; + + static function main() {} +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/MainMacro.hx b/tests/misc/projects/Issue9676/MainMacro.hx new file mode 100644 index 00000000..9ffbd11a --- /dev/null +++ b/tests/misc/projects/Issue9676/MainMacro.hx @@ -0,0 +1,3 @@ +function main() { + macro final var value:String; +} \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile1-fail.hxml b/tests/misc/projects/Issue9676/compile1-fail.hxml new file mode 100644 index 00000000..fdd39207 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile1-fail.hxml @@ -0,0 +1 @@ +--main MainField diff --git a/tests/misc/projects/Issue9676/compile1-fail.hxml.stderr b/tests/misc/projects/Issue9676/compile1-fail.hxml.stderr new file mode 100644 index 00000000..93ef4f50 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile1-fail.hxml.stderr @@ -0,0 +1 @@ +MainField.hx:2: characters 9-18 : `final var` is not supported, use `final` instead \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile1D.hxml b/tests/misc/projects/Issue9676/compile1D.hxml new file mode 100644 index 00000000..0c40fd3c --- /dev/null +++ b/tests/misc/projects/Issue9676/compile1D.hxml @@ -0,0 +1,2 @@ +--main MainField +--display "MainField.hx@0@diagnostics" \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile1D.hxml.stderr b/tests/misc/projects/Issue9676/compile1D.hxml.stderr new file mode 100644 index 00000000..e9678bf7 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile1D.hxml.stderr @@ -0,0 +1 @@ +[{"file":"$$normPath(::cwd::/,true)MainField.hx","diagnostics":[{"kind":4,"severity":1,"range":{"start":{"line":1,"character":8},"end":{"line":1,"character":17}},"args":"`final var` is not supported, use `final` instead","relatedInformation":[]}]}] diff --git a/tests/misc/projects/Issue9676/compile2-fail.hxml b/tests/misc/projects/Issue9676/compile2-fail.hxml new file mode 100644 index 00000000..811f063c --- /dev/null +++ b/tests/misc/projects/Issue9676/compile2-fail.hxml @@ -0,0 +1 @@ +--main MainExpr \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile2-fail.hxml.stderr b/tests/misc/projects/Issue9676/compile2-fail.hxml.stderr new file mode 100644 index 00000000..c37670bb --- /dev/null +++ b/tests/misc/projects/Issue9676/compile2-fail.hxml.stderr @@ -0,0 +1 @@ +MainExpr.hx:2: characters 2-11 : `final var` is not supported, use `final` instead \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile2D.hxml b/tests/misc/projects/Issue9676/compile2D.hxml new file mode 100644 index 00000000..24cdc40d --- /dev/null +++ b/tests/misc/projects/Issue9676/compile2D.hxml @@ -0,0 +1,2 @@ +--main MainExpr +--display "MainExpr.hx@0@diagnostics" \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile2D.hxml.stderr b/tests/misc/projects/Issue9676/compile2D.hxml.stderr new file mode 100644 index 00000000..95a7b520 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile2D.hxml.stderr @@ -0,0 +1 @@ +[{"file":"$$normPath(::cwd::/,true)MainExpr.hx","diagnostics":[{"kind":4,"severity":1,"range":{"start":{"line":1,"character":1},"end":{"line":1,"character":10}},"args":"`final var` is not supported, use `final` instead","relatedInformation":[]}]}] diff --git a/tests/misc/projects/Issue9676/compile3-fail.hxml b/tests/misc/projects/Issue9676/compile3-fail.hxml new file mode 100644 index 00000000..074b1af5 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile3-fail.hxml @@ -0,0 +1 @@ +--main MainMacro \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile3-fail.hxml.stderr b/tests/misc/projects/Issue9676/compile3-fail.hxml.stderr new file mode 100644 index 00000000..8df65a43 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile3-fail.hxml.stderr @@ -0,0 +1 @@ +MainMacro.hx:2: characters 8-17 : `final var` is not supported, use `final` instead \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile3D.hxml b/tests/misc/projects/Issue9676/compile3D.hxml new file mode 100644 index 00000000..9f544db5 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile3D.hxml @@ -0,0 +1,2 @@ +--main MainMacro +--display "MainMacro.hx@0@diagnostics" \ No newline at end of file diff --git a/tests/misc/projects/Issue9676/compile3D.hxml.stderr b/tests/misc/projects/Issue9676/compile3D.hxml.stderr new file mode 100644 index 00000000..ff8a5f84 --- /dev/null +++ b/tests/misc/projects/Issue9676/compile3D.hxml.stderr @@ -0,0 +1 @@ +[{"file":"$$normPath(::cwd::/,true)MainMacro.hx","diagnostics":[{"kind":4,"severity":1,"range":{"start":{"line":1,"character":7},"end":{"line":1,"character":16}},"args":"`final var` is not supported, use `final` instead","relatedInformation":[]}]}] diff --git a/tests/misc/projects/Issue9785/compile-fail.hxml.stderr b/tests/misc/projects/Issue9785/compile-fail.hxml.stderr index f25b8f48..f6f3e6a2 100644 --- a/tests/misc/projects/Issue9785/compile-fail.hxml.stderr +++ b/tests/misc/projects/Issue9785/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 2-19 : x: Cannot declare member variable in abstract \ No newline at end of file +Main.hx:2: characters 2-19 : Cannot declare member variable in abstract \ No newline at end of file diff --git a/tests/misc/projects/Issue9919/Main.hx b/tests/misc/projects/Issue9919/Main.hx new file mode 100644 index 00000000..d0787643 --- /dev/null +++ b/tests/misc/projects/Issue9919/Main.hx @@ -0,0 +1,7 @@ +function main() { + var foo:Void->Void; + foo = function() { + trace("Hello"); + haxe.Timer.delay(foo, 1); + }; +} diff --git a/tests/misc/projects/Issue9919/compile.hxml b/tests/misc/projects/Issue9919/compile.hxml new file mode 100644 index 00000000..a711e039 --- /dev/null +++ b/tests/misc/projects/Issue9919/compile.hxml @@ -0,0 +1,3 @@ +-cp src +-main Main +--interp diff --git a/tests/misc/projects/Issue9919/compile.hxml.stderr b/tests/misc/projects/Issue9919/compile.hxml.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/misc/projects/default_type_params/Ctor.hx b/tests/misc/projects/default_type_params/Ctor.hx new file mode 100644 index 00000000..a37f7f1f --- /dev/null +++ b/tests/misc/projects/default_type_params/Ctor.hx @@ -0,0 +1,3 @@ +class C { + function new(){} +} \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/EnumCtor.hx b/tests/misc/projects/default_type_params/EnumCtor.hx new file mode 100644 index 00000000..3a8ec872 --- /dev/null +++ b/tests/misc/projects/default_type_params/EnumCtor.hx @@ -0,0 +1,3 @@ +enum E { + C(); +} \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/Method.hx b/tests/misc/projects/default_type_params/Method.hx new file mode 100644 index 00000000..8b7a3773 --- /dev/null +++ b/tests/misc/projects/default_type_params/Method.hx @@ -0,0 +1 @@ +function method() {} \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-ctor-fail.hxml b/tests/misc/projects/default_type_params/compile-ctor-fail.hxml new file mode 100644 index 00000000..ebbf54b2 --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-ctor-fail.hxml @@ -0,0 +1 @@ +--main Ctor \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-ctor-fail.hxml.stderr b/tests/misc/projects/default_type_params/compile-ctor-fail.hxml.stderr new file mode 100644 index 00000000..6f610732 --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-ctor-fail.hxml.stderr @@ -0,0 +1 @@ +Ctor.hx:2: characters 17-23 : Default type parameters are only supported on types \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml b/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml new file mode 100644 index 00000000..d13ee050 --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml @@ -0,0 +1 @@ +--main EnumCtor \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml.stderr b/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml.stderr new file mode 100644 index 00000000..0f1086b0 --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-enum-ctor-fail.hxml.stderr @@ -0,0 +1 @@ +EnumCtor.hx:2: characters 6-12 : Default type parameters are only supported on types \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-method-fail.hxml b/tests/misc/projects/default_type_params/compile-method-fail.hxml new file mode 100644 index 00000000..0417028b --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-method-fail.hxml @@ -0,0 +1 @@ +--main Method \ No newline at end of file diff --git a/tests/misc/projects/default_type_params/compile-method-fail.hxml.stderr b/tests/misc/projects/default_type_params/compile-method-fail.hxml.stderr new file mode 100644 index 00000000..da8abbd0 --- /dev/null +++ b/tests/misc/projects/default_type_params/compile-method-fail.hxml.stderr @@ -0,0 +1 @@ +Method.hx:1: characters 19-25 : Default type parameters are only supported on types \ No newline at end of file diff --git a/tests/misc/projects/eventLoop/Main.hx b/tests/misc/projects/eventLoop/Main.hx new file mode 100644 index 00000000..5e6b3294 --- /dev/null +++ b/tests/misc/projects/eventLoop/Main.hx @@ -0,0 +1,54 @@ +class Main { + + static function main() { + var event : haxe.MainLoop.MainEvent = null; + var count = 0; + event = haxe.MainLoop.add(function() { + trace(count++); + if( count == 10 ) { + event.stop(); + trace(haxe.MainLoop.hasEvents()); + } + }); + + #if sys + // if we don't use native timer, this should execute before + var t = new haxe.Timer(100); + t.run = function() { + trace("BEFORE1"); + t.stop(); + }; + #end + + var t = new haxe.Timer(200); + var count = 0; + t.run = function() { + trace("T" + count++); + if( count == 5 ) + t.stop(); + }; + + #if sys + // if we don't use native timer, this should execute before + var t = new haxe.Timer(100); + t.run = function() { + trace("BEFORE2"); + t.stop(); + }; + #end + + #if sys + Sys.sleep(0.3); + #end + + haxe.MainLoop.addThread(function() { + var event : haxe.MainLoop.MainEvent = null; + var count = 0; + event = haxe.MainLoop.add(function() { + trace(String.fromCharCode("A".code + count++)); + if( count == 5 ) event.stop(); + }); + }); + } + +} diff --git a/tests/misc/projects/eventLoop/compile.hxml b/tests/misc/projects/eventLoop/compile.hxml new file mode 100644 index 00000000..5c59d50a --- /dev/null +++ b/tests/misc/projects/eventLoop/compile.hxml @@ -0,0 +1,3 @@ +--main Main +--dce full +--interp diff --git a/tests/misc/projects/eventLoop/compile.hxml.stdout.disabled b/tests/misc/projects/eventLoop/compile.hxml.stdout.disabled new file mode 100644 index 00000000..5c4f3fa6 --- /dev/null +++ b/tests/misc/projects/eventLoop/compile.hxml.stdout.disabled @@ -0,0 +1,23 @@ +Main.hx:18: BEFORE1 +Main.hx:35: BEFORE2 +Main.hx:26: T0 +Main.hx:48: A +Main.hx:7: 0 +Main.hx:48: B +Main.hx:7: 1 +Main.hx:48: C +Main.hx:7: 2 +Main.hx:48: D +Main.hx:7: 3 +Main.hx:48: E +Main.hx:7: 4 +Main.hx:7: 5 +Main.hx:7: 6 +Main.hx:7: 7 +Main.hx:7: 8 +Main.hx:7: 9 +Main.hx:10: false +Main.hx:26: T1 +Main.hx:26: T2 +Main.hx:26: T3 +Main.hx:26: T4 diff --git a/tests/misc/projects/extern-overloads/not-not-extern/compile-fail.hxml.stderr b/tests/misc/projects/extern-overloads/not-not-extern/compile-fail.hxml.stderr index 4f29d27f..2bce8060 100644 --- a/tests/misc/projects/extern-overloads/not-not-extern/compile-fail.hxml.stderr +++ b/tests/misc/projects/extern-overloads/not-not-extern/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 2-10 : Only extern functions may be overloaded on this target \ No newline at end of file +Main.hx:2: characters 2-10 : Invalid modifier: overload is only supported on extern functions \ No newline at end of file diff --git a/tests/misc/projects/extern-overloads/not-on-constructor/compile-fail.hxml.stderr b/tests/misc/projects/extern-overloads/not-on-constructor/compile-fail.hxml.stderr index 87398916..5737b5ca 100644 --- a/tests/misc/projects/extern-overloads/not-on-constructor/compile-fail.hxml.stderr +++ b/tests/misc/projects/extern-overloads/not-on-constructor/compile-fail.hxml.stderr @@ -1 +1 @@ -Main.hx:2: characters 2-10 : Constructors cannot be overloaded on this target \ No newline at end of file +Main.hx:2: characters 2-10 : Invalid modifier: overload on constructor \ No newline at end of file diff --git a/tests/misc/projects/issue5002/compile-fail.hxml.stderr b/tests/misc/projects/issue5002/compile-fail.hxml.stderr index e1197f4c..3ef53a11 100644 --- a/tests/misc/projects/issue5002/compile-fail.hxml.stderr +++ b/tests/misc/projects/issue5002/compile-fail.hxml.stderr @@ -1,4 +1,2 @@ Main.hx:1: characters 1-8 : "0" is not a valid field name. -Main.hx:2: lines 2-4 : ... Defined in this class -Main.hx:1: characters 1-8 : "this" is not a valid field name. -Main.hx:2: lines 2-4 : ... Defined in this class \ No newline at end of file +Main.hx:1: characters 1-8 : "this" is not a valid field name. \ No newline at end of file diff --git a/tests/misc/projects/issue5002/compile2-fail.hxml.stderr b/tests/misc/projects/issue5002/compile2-fail.hxml.stderr index a9266516..455e91e7 100644 --- a/tests/misc/projects/issue5002/compile2-fail.hxml.stderr +++ b/tests/misc/projects/issue5002/compile2-fail.hxml.stderr @@ -1,18 +1,11 @@ -"0_variable" is not a valid variable name. -Main2.hx:6: characters 3-32 : ... Called from macro here -"var" is not a valid variable name. -Main2.hx:7: characters 3-25 : ... Called from macro here -"new" is not a valid variable name. -Main2.hx:8: characters 3-25 : ... Called from macro here -"foo \"\t\n" is not a valid variable name. -Main2.hx:9: characters 3-32 : ... Called from macro here -"0_catchVariable" is not a valid catch variable name. -Main2.hx:10: characters 3-25 : ... Called from macro here -"0_function" is not a valid function name. -Main2.hx:11: characters 3-24 : ... Called from macro here -"0_argument" is not a valid function argument name. -Main2.hx:12: characters 3-32 : ... Called from macro here -Main2.hx:13: characters 3-27 : Pattern variables must be lower-case +Main2.hx:6: characters 3-32 : "0_variable" is not a valid variable name. +Main2.hx:7: characters 3-25 : "var" is not a valid variable name. +Main2.hx:8: characters 3-25 : "new" is not a valid variable name. +Main2.hx:9: characters 3-32 : "foo \"\t\n" is not a valid variable name. +Main2.hx:10: characters 3-25 : "0_catchVariable" is not a valid catch variable name. +Main2.hx:11: characters 3-24 : "0_function" is not a valid function name. +Main2.hx:12: characters 3-32 : "0_argument" is not a valid function argument name. +Main2.hx:13: characters 3-27 : Unknown identifier : 0_patternVariable, pattern variables must be lower-case or with `var ` prefix Main2.hx:14: characters 3-23 : "0_forVariable" is not a valid for variable name. Main2.hx:15: characters 3-31 : "0_forVariableKey" is not a valid for variable name. -Main2.hx:15: characters 3-31 : "0_forVariableValue" is not a valid for variable name. \ No newline at end of file +Main2.hx:15: characters 3-31 : "0_forVariableValue" is not a valid for variable name. diff --git a/tests/misc/projects/issue5002/compile4-fail.hxml.stderr b/tests/misc/projects/issue5002/compile4-fail.hxml.stderr index e63d04ad..e9992dc4 100644 --- a/tests/misc/projects/issue5002/compile4-fail.hxml.stderr +++ b/tests/misc/projects/issue5002/compile4-fail.hxml.stderr @@ -1,4 +1,4 @@ Main4.hx:3: characters 7-9 : Variable names starting with a dollar are not allowed: "$i" Main4.hx:5: characters 17-19 : Catch variable names starting with a dollar are not allowed: "$i" Main4.hx:7: characters 8-10 : For variable names starting with a dollar are not allowed: "$i" -Main4.hx:10: characters 9-11 : Pattern variables must be lower-case \ No newline at end of file +Main4.hx:10: characters 9-11 : Unknown identifier : $i, pattern variables must be lower-case or with `var ` prefix diff --git a/tests/misc/projects/local-static/Break/Main.hx b/tests/misc/projects/local-static/Break/Main.hx new file mode 100644 index 00000000..315e24ad --- /dev/null +++ b/tests/misc/projects/local-static/Break/Main.hx @@ -0,0 +1,7 @@ +class Main { + static function main() { + while(Math.random() > 0.5) { + static var x = break; + } + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/Break/compile-fail.hxml b/tests/misc/projects/local-static/Break/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/Break/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/Break/compile-fail.hxml.stderr b/tests/misc/projects/local-static/Break/compile-fail.hxml.stderr new file mode 100644 index 00000000..919a1541 --- /dev/null +++ b/tests/misc/projects/local-static/Break/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 19-24 : Break outside loop \ No newline at end of file diff --git a/tests/misc/projects/local-static/Continue/Main.hx b/tests/misc/projects/local-static/Continue/Main.hx new file mode 100644 index 00000000..1c802199 --- /dev/null +++ b/tests/misc/projects/local-static/Continue/Main.hx @@ -0,0 +1,7 @@ +class Main { + static function main() { + while(Math.random() > 0.5) { + static var x = continue; + } + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/Continue/compile-fail.hxml b/tests/misc/projects/local-static/Continue/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/Continue/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr b/tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr new file mode 100644 index 00000000..4cb5353c --- /dev/null +++ b/tests/misc/projects/local-static/Continue/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 19-27 : Continue outside loop \ No newline at end of file diff --git a/tests/misc/projects/local-static/Function/Main.hx b/tests/misc/projects/local-static/Function/Main.hx new file mode 100644 index 00000000..3e6d0441 --- /dev/null +++ b/tests/misc/projects/local-static/Function/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + function x() {} + static var y = x; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/Function/compile-fail.hxml b/tests/misc/projects/local-static/Function/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/Function/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/Function/compile-fail.hxml.stderr b/tests/misc/projects/local-static/Function/compile-fail.hxml.stderr new file mode 100644 index 00000000..594efc9e --- /dev/null +++ b/tests/misc/projects/local-static/Function/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 18-19 : Accessing local variables in static initialization is not allowed \ No newline at end of file diff --git a/tests/misc/projects/local-static/Local/Main.hx b/tests/misc/projects/local-static/Local/Main.hx new file mode 100644 index 00000000..5a5e97a8 --- /dev/null +++ b/tests/misc/projects/local-static/Local/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + var x = 1; + static var y = x; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/Local/compile-fail.hxml b/tests/misc/projects/local-static/Local/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/Local/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/Local/compile-fail.hxml.stderr b/tests/misc/projects/local-static/Local/compile-fail.hxml.stderr new file mode 100644 index 00000000..594efc9e --- /dev/null +++ b/tests/misc/projects/local-static/Local/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:4: characters 18-19 : Accessing local variables in static initialization is not allowed \ No newline at end of file diff --git a/tests/misc/projects/local-static/LocalVsLocal/Main.hx b/tests/misc/projects/local-static/LocalVsLocal/Main.hx new file mode 100644 index 00000000..fb93563a --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsLocal/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + static var x = 1; + static var x = 2; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml b/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr b/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr new file mode 100644 index 00000000..20c1a9f4 --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsLocal/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:4: characters 14-15 : The expanded name of this local (main_x) conflicts with another static field +Main.hx:3: characters 14-15 : Conflicting field was found here \ No newline at end of file diff --git a/tests/misc/projects/local-static/LocalVsStatic/Main.hx b/tests/misc/projects/local-static/LocalVsStatic/Main.hx new file mode 100644 index 00000000..300ef5c0 --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsStatic/Main.hx @@ -0,0 +1,6 @@ +class Main { + static var main_x = 1; + static function main() { + static var x = 2; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml b/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr b/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr new file mode 100644 index 00000000..0f9142ee --- /dev/null +++ b/tests/misc/projects/local-static/LocalVsStatic/compile-fail.hxml.stderr @@ -0,0 +1,2 @@ +Main.hx:4: characters 14-15 : The expanded name of this local (main_x) conflicts with another static field +Main.hx:2: characters 13-19 : Conflicting field was found here \ No newline at end of file diff --git a/tests/misc/projects/local-static/Return/Main.hx b/tests/misc/projects/local-static/Return/Main.hx new file mode 100644 index 00000000..8e8c8d1e --- /dev/null +++ b/tests/misc/projects/local-static/Return/Main.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + static var x = return; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/Return/compile-fail.hxml b/tests/misc/projects/local-static/Return/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/Return/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/Return/compile-fail.hxml.stderr b/tests/misc/projects/local-static/Return/compile-fail.hxml.stderr new file mode 100644 index 00000000..a43cc252 --- /dev/null +++ b/tests/misc/projects/local-static/Return/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:3: characters 18-24 : This kind of control flow in static initialization is not allowed \ No newline at end of file diff --git a/tests/misc/projects/local-static/This/Main.hx b/tests/misc/projects/local-static/This/Main.hx new file mode 100644 index 00000000..82fee475 --- /dev/null +++ b/tests/misc/projects/local-static/This/Main.hx @@ -0,0 +1,11 @@ +class Main { + static function main() { + + } + + var x = 1; + + function test() { + static var y = x; + } +} \ No newline at end of file diff --git a/tests/misc/projects/local-static/This/compile-fail.hxml b/tests/misc/projects/local-static/This/compile-fail.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/local-static/This/compile-fail.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/local-static/This/compile-fail.hxml.stderr b/tests/misc/projects/local-static/This/compile-fail.hxml.stderr new file mode 100644 index 00000000..72e222b5 --- /dev/null +++ b/tests/misc/projects/local-static/This/compile-fail.hxml.stderr @@ -0,0 +1 @@ +Main.hx:9: characters 18-19 : Accessing `this` in static initialization is not allowed \ No newline at end of file diff --git a/tests/misc/projects/recursive-callbacks/Main.hx b/tests/misc/projects/recursive-callbacks/Main.hx new file mode 100644 index 00000000..3c2ac853 --- /dev/null +++ b/tests/misc/projects/recursive-callbacks/Main.hx @@ -0,0 +1,30 @@ +#if macro +import haxe.macro.Context; + +function init() { + var here = (macro here).pos; + + Context.onAfterInitMacros(() -> { + Context.warning("1", here); + Context.onAfterInitMacros(() -> { + Context.warning("2", here); + }); + }); + + Context.onGenerate((_) -> { + Context.warning("3", here); + Context.onGenerate((_) -> { + Context.warning("4", here); + }); + }); + + Context.onAfterGenerate(() -> { + Context.warning("5", here); + Context.onAfterGenerate(() -> { + Context.warning("6", here); + }); + }); +} +#end + +function main() {} diff --git a/tests/misc/projects/recursive-callbacks/compile.hxml b/tests/misc/projects/recursive-callbacks/compile.hxml new file mode 100644 index 00000000..8e7a3e00 --- /dev/null +++ b/tests/misc/projects/recursive-callbacks/compile.hxml @@ -0,0 +1,2 @@ +--main Main +--macro Main.init() diff --git a/tests/misc/projects/recursive-callbacks/compile.hxml.stderr b/tests/misc/projects/recursive-callbacks/compile.hxml.stderr new file mode 100644 index 00000000..cf44340f --- /dev/null +++ b/tests/misc/projects/recursive-callbacks/compile.hxml.stderr @@ -0,0 +1,7 @@ +Main.hx:5: characters 20-24 : Warning : 1 +Main.hx:9: lines 9-11 : Warning : This API should only be used from initialization macros. +Main.hx:5: characters 20-24 : Warning : 2 +Main.hx:5: characters 20-24 : Warning : 3 +Main.hx:5: characters 20-24 : Warning : 4 +Main.hx:5: characters 20-24 : Warning : 5 +Main.hx:5: characters 20-24 : Warning : 6 diff --git a/tests/misc/projects/sys-exit/SysExit0.hx b/tests/misc/projects/sys-exit/SysExit0.hx new file mode 100644 index 00000000..0c8e48ee --- /dev/null +++ b/tests/misc/projects/sys-exit/SysExit0.hx @@ -0,0 +1,6 @@ +class SysExit0 { + static function main() { + Sys.stderr().writeString("Exiting with 0\n"); + Sys.exit(0); + } +} \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/SysExit1.hx b/tests/misc/projects/sys-exit/SysExit1.hx new file mode 100644 index 00000000..f447ffac --- /dev/null +++ b/tests/misc/projects/sys-exit/SysExit1.hx @@ -0,0 +1,6 @@ +class SysExit1 { + static function main() { + Sys.stderr().writeString("Exiting with 1\n"); + Sys.exit(1); + } +} \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile00.hxml b/tests/misc/projects/sys-exit/compile00.hxml new file mode 100644 index 00000000..878356be --- /dev/null +++ b/tests/misc/projects/sys-exit/compile00.hxml @@ -0,0 +1,7 @@ +--main SysExit0 +--interp + +--next + +--main SysExit0 +--interp \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile00.hxml.stderr b/tests/misc/projects/sys-exit/compile00.hxml.stderr new file mode 100644 index 00000000..da58f045 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile00.hxml.stderr @@ -0,0 +1,2 @@ +Exiting with 0 +Exiting with 0 \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile01-fail.hxml b/tests/misc/projects/sys-exit/compile01-fail.hxml new file mode 100644 index 00000000..4478fb88 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile01-fail.hxml @@ -0,0 +1,7 @@ +--main SysExit0 +--interp + +--next + +--main SysExit1 +--interp \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile01-fail.hxml.stderr b/tests/misc/projects/sys-exit/compile01-fail.hxml.stderr new file mode 100644 index 00000000..59489da6 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile01-fail.hxml.stderr @@ -0,0 +1,2 @@ +Exiting with 0 +Exiting with 1 \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile10-fail.hxml b/tests/misc/projects/sys-exit/compile10-fail.hxml new file mode 100644 index 00000000..7398a5d6 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile10-fail.hxml @@ -0,0 +1,7 @@ +--main SysExit1 +--interp + +--next + +--main SysExit0 +--interp \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile10-fail.hxml.stderr b/tests/misc/projects/sys-exit/compile10-fail.hxml.stderr new file mode 100644 index 00000000..c916e2d5 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile10-fail.hxml.stderr @@ -0,0 +1 @@ +Exiting with 1 \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile11-fail.hxml b/tests/misc/projects/sys-exit/compile11-fail.hxml new file mode 100644 index 00000000..b40df28e --- /dev/null +++ b/tests/misc/projects/sys-exit/compile11-fail.hxml @@ -0,0 +1,7 @@ +--main SysExit1 +--interp + +--next + +--main SysExit1 +--interp \ No newline at end of file diff --git a/tests/misc/projects/sys-exit/compile11-fail.hxml.stderr b/tests/misc/projects/sys-exit/compile11-fail.hxml.stderr new file mode 100644 index 00000000..c916e2d5 --- /dev/null +++ b/tests/misc/projects/sys-exit/compile11-fail.hxml.stderr @@ -0,0 +1 @@ +Exiting with 1 \ No newline at end of file diff --git a/tests/misc/projects/trailing-commas/Main.hx b/tests/misc/projects/trailing-commas/Main.hx new file mode 100644 index 00000000..3f56da1c --- /dev/null +++ b/tests/misc/projects/trailing-commas/Main.hx @@ -0,0 +1,43 @@ +class Main { + static function f(t:Type.ValueType, t2:Type.ValueType,) {} + public static function main():Void { + f(TNull,TNull,); + foobar( + 1, + 2, + ); + function blah( + a:Int, + b:Int, + ):Void {} + var a = ( + a:Int, + b:Int, + ) -> 0; + var a:( + a:Int, + b:Int, + )->Int = null; + a = (a, b,) -> a + b; + trace(a(2, 3,) + moduleFoobar(5, 5,)); + } + + static function foobar( + a:Int, + b:Int, + ):Void {} +} + +enum Foo { + Bar( + a:Int, + b:Int, + ); +} + +function moduleFoobar( + a:Int, + b:Int, +):Int { + return a + b; +} diff --git a/tests/misc/projects/trailing-commas/Main2.hx b/tests/misc/projects/trailing-commas/Main2.hx new file mode 100644 index 00000000..58fef1a7 --- /dev/null +++ b/tests/misc/projects/trailing-commas/Main2.hx @@ -0,0 +1,6 @@ +class Main2 { + static function f(t:Type.ValueType, t2:Type.ValueType,,) {} + public static function main():Void { + f(TNull,TNull,); + } +} diff --git a/tests/misc/projects/trailing-commas/Main3.hx b/tests/misc/projects/trailing-commas/Main3.hx new file mode 100644 index 00000000..6a8f2c0a --- /dev/null +++ b/tests/misc/projects/trailing-commas/Main3.hx @@ -0,0 +1,6 @@ +class Main3 { + public static function main():Void { + f(,); + } + static function f():Void {} +} diff --git a/tests/misc/projects/trailing-commas/compile1.hxml b/tests/misc/projects/trailing-commas/compile1.hxml new file mode 100644 index 00000000..e2a3d27a --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile1.hxml @@ -0,0 +1,2 @@ +--main Main +--interp diff --git a/tests/misc/projects/trailing-commas/compile1.hxml.stdout b/tests/misc/projects/trailing-commas/compile1.hxml.stdout new file mode 100644 index 00000000..59eafe4b --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile1.hxml.stdout @@ -0,0 +1 @@ +Main.hx:22: 15 diff --git a/tests/misc/projects/trailing-commas/compile2-fail.hxml b/tests/misc/projects/trailing-commas/compile2-fail.hxml new file mode 100644 index 00000000..47f833e8 --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile2-fail.hxml @@ -0,0 +1,2 @@ +--main Main2 +--interp diff --git a/tests/misc/projects/trailing-commas/compile2-fail.hxml.stderr b/tests/misc/projects/trailing-commas/compile2-fail.hxml.stderr new file mode 100644 index 00000000..b1846f96 --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile2-fail.hxml.stderr @@ -0,0 +1 @@ +Main2.hx:2: characters 56-57 : Unexpected , diff --git a/tests/misc/projects/trailing-commas/compile3-fail.hxml b/tests/misc/projects/trailing-commas/compile3-fail.hxml new file mode 100644 index 00000000..3ac6966d --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile3-fail.hxml @@ -0,0 +1,2 @@ +--main Main3 +--interp diff --git a/tests/misc/projects/trailing-commas/compile3-fail.hxml.stderr b/tests/misc/projects/trailing-commas/compile3-fail.hxml.stderr new file mode 100644 index 00000000..97316f46 --- /dev/null +++ b/tests/misc/projects/trailing-commas/compile3-fail.hxml.stderr @@ -0,0 +1 @@ +Main3.hx:3: characters 5-6 : Expected expression or ) diff --git a/tests/misc/python/projects/Issue11060/Main.hx b/tests/misc/python/projects/Issue11060/Main.hx new file mode 100644 index 00000000..359ee306 --- /dev/null +++ b/tests/misc/python/projects/Issue11060/Main.hx @@ -0,0 +1,3 @@ +function main() { + trace(new haxe.ds.Vector(1)[0]); +} diff --git a/tests/misc/python/projects/Issue11060/build.hxml b/tests/misc/python/projects/Issue11060/build.hxml new file mode 100644 index 00000000..fcf1ca0c --- /dev/null +++ b/tests/misc/python/projects/Issue11060/build.hxml @@ -0,0 +1,3 @@ +-python bin/main.py +--main Main +-cmd python3 bin/main.py diff --git a/tests/misc/python/projects/Issue11060/build.hxml.stdout b/tests/misc/python/projects/Issue11060/build.hxml.stdout new file mode 100644 index 00000000..b0047fa4 --- /dev/null +++ b/tests/misc/python/projects/Issue11060/build.hxml.stdout @@ -0,0 +1 @@ +None diff --git a/tests/misc/src/Main.hx b/tests/misc/src/Main.hx index 633630ef..7c8dc2c0 100644 --- a/tests/misc/src/Main.hx +++ b/tests/misc/src/Main.hx @@ -1,3 +1,4 @@ +import haxe.io.Bytes; import haxe.macro.Compiler; import sys.FileSystem; import sys.io.File; @@ -44,8 +45,9 @@ class Main { Sys.setCwd(dirPath); Sys.println('Running haxe $path'); var expectFailure = file.endsWith("-fail.hxml"); + var expectStdout = if (FileSystem.exists('$file.stdout')) prepareExpectedOutput(File.getContent('$file.stdout')) else null; var expectStderr = if (FileSystem.exists('$file.stderr')) prepareExpectedOutput(File.getContent('$file.stderr')) else null; - var result = runCommand("haxe", [file], expectFailure, expectStderr); + var result = runCommand("haxe", [file], expectFailure, expectStdout, expectStderr); ++count; if (!result.success) { failures++; @@ -67,23 +69,27 @@ class Main { s = s.replace("\r\n", "\n"); // get rid of windows newlines var cwd = Path.removeTrailingSlashes(FileSystem.fullPath(Sys.getCwd())); + var std = Path.removeTrailingSlashes(getStd()); - var context = {cwd: cwd}; + var context = {cwd: cwd, std: std}; var macros = {normPath: normPath}; return new haxe.Template(s).execute(context, macros); } - static function normPath(_, p:String):String { + static function normPath(_, p:String, escape:String = "false"):String { + var escape = escape != "false"; + if (Sys.systemName() == "Windows") { // on windows, haxe returns lowercase paths with backslashes, drive letter uppercased p = p.substr(0, 1).toUpperCase() + p.substr(1); p = p.replace("/", "\\"); + if (escape) p = p.replace("\\", "\\\\"); } return p; } - static function runCommand(command:String, args:Array, expectFailure:Bool, expectStderr:String):{success:Bool, summary:String} { + static function runCommand(command:String, args:Array, expectFailure:Bool, expectStdout:String, expectStderr:String):{success:Bool, summary:String} { var summary = []; function println(msg:String) { summary.push(msg); @@ -121,28 +127,51 @@ class Main { false; } - if (stdout.length > 0) { + if (result && expectStdout != null) { + result = checkOutput(println, "stdout", stdout.toString(), expectStdout); + } else if (stdout.length > 0) { println(stdout.toString()); } if (result && expectStderr != null) { - var stderr = proc.stderr.readAll().toString().replace("\r\n", "\n").trim(); - var expected = expectStderr.trim(); - if (stderr != expected) { - // "Picked up JAVA_TOOL_OPTIONS: <...>" is printed by JVM sometimes. - // @see https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html - stderr = stderr.split('\n') - .filter(s -> 0 != s.indexOf('Picked up JAVA_TOOL_OPTIONS:')) - .join('\n'); - if(stderr != expected) { - println("Actual stderr output doesn't match the expected one"); - println('Expected:\n"$expectStderr"'); - println('Actual:\n"$stderr"'); - result = false; - } - } + result = checkOutput(println, "stderr", proc.stderr.readAll().toString(), expectStderr); } + proc.close(); return {success:result, summary:summary.join('\n')}; } + + static function checkOutput(println:String->Void, name:String, content:String, expected:String):Bool { + var content = content.replace("\r\n", "\n").trim(); + var expected = expected.trim(); + if (content != expected) { + // "Picked up JAVA_TOOL_OPTIONS: <...>" is printed by JVM sometimes. + // @see https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/envvars002.html + content = content.split('\n') + .filter(s -> 0 != s.indexOf('Picked up JAVA_TOOL_OPTIONS:')) + .join('\n'); + + if (content != expected) { + final a = new diff.FileData(Bytes.ofString(expected), "expected", Date.now()); + final b = new diff.FileData(Bytes.ofString(content), "actual", Date.now()); + var ctx:diff.Context = { + file1: a, + file2: b, + context: 10 + } + final script = diff.Analyze.diff2Files(ctx); + var diff = diff.Printer.printUnidiff(ctx, script); + diff = diff.split("\n").slice(3).join("\n"); + println(diff); + return false; + } + } + + return true; + } + + static macro function getStd() { + var std = Compiler.getConfiguration().stdPath; + return macro $v{std.shift()}; + } } diff --git a/tests/misc/src/diff/Analyze.hx b/tests/misc/src/diff/Analyze.hx new file mode 100644 index 00000000..135cf7a9 --- /dev/null +++ b/tests/misc/src/diff/Analyze.hx @@ -0,0 +1,132 @@ +package diff; + +import haxe.ds.Vector; +import diff.Diffseq; + +class Analyze { + static function discardConfusingLines(file1:FileData, file2:FileData) {} + + static function analyzeHunk(ctx:Context, script:Array) { + var showFrom = 0; + var showTo = 0; + + final first0 = script[0].line0; + final first1 = script[0].line1; + + var l0 = 0; + var l1 = 0; + for (next in script) { + l0 = next.line0 + next.deleted - 1; + l1 = next.line1 + next.inserted - 1; + showFrom += next.deleted; + showTo += next.inserted; + } + + return { + first0: first0, + first1: first1, + last0: l0, + last1: l1, + showFrom: showFrom > 0, + showTo: showTo > 0 + } + } + + static function buildScript(ctx:Context) { + var currentChange = null; + var firstChange = null; + function addChange(line0:Int, line1:Int, deleted:Int, inserted:Int) { + var change:Change = { + line0: line0, + line1: line1, + deleted: deleted, + inserted: inserted + }; + if (currentChange == null) { + currentChange = change; + firstChange = change; + } else { + currentChange.next = change; + currentChange = change; + } + }; + final changed0 = ctx.file1.changed; + final changed1 = ctx.file2.changed; + final len0 = ctx.file1.lines.length; + final len1 = ctx.file2.lines.length; + var i0 = 0; + var i1 = 0; + while (i0 < len0 || i1 < len1) { + if (changed0[i0] | changed1[i1] != 0) { + final line0 = i0; + final line1 = i1; + while (changed0[i0] != 0) { + ++i0; + } + while (changed1[i1] != 0) { + ++i1; + } + addChange(line0, line1, i0 - line0, i1 - line1); + } + i0++; + i1++; + } + return firstChange; + } + + static function printNormalHunk(ctx:Context, script:Array) { + // final changes = analyzeHunk(ctx, script); + var buf = new StringBuf(); + for (change in script) { + if (change.deleted > 0) { + for (i in change.line0...change.line0 + change.deleted) { + final line = ctx.file1.lines[i]; + buf.add("< "); + buf.add(ctx.file1.data.sub(line.pos, line.length)); + buf.addChar("\n".code); + } + } + buf.add("---\n"); + if (change.inserted > 0) { + for (i in change.line1...change.line1 + change.inserted) { + final line = ctx.file2.lines[i]; + buf.add("> "); + buf.add(ctx.file2.data.sub(line.pos, line.length).toString()); + buf.addChar("\n".code); + } + } + } + return buf.toString(); + } + + static public function diff2Files(ctx:Context) { + Io.readFiles(ctx); + final flagSpace = new Vector(ctx.file1.lines.length + ctx.file2.lines.length + 4); + for (i in 0...flagSpace.length) { + flagSpace[i] = 0; + } + ctx.file1.changed = new IndexVector(flagSpace, 1); + ctx.file2.changed = new IndexVector(flagSpace, ctx.file1.lines.length + 3); + // TODO: discardConfusingLines + final diags = ctx.file1.equivs.length + ctx.file2.equivs.length + 3; + var fdiag = new IndexVector(new Vector(diags * 2), 0); + var bdiag = fdiag + diags; + fdiag += ctx.file2.equivs.length + 1; + bdiag += ctx.file2.equivs.length + 1; + var dCtx:DiffseqContext = { + xvec: ctx.file1.equivs, + yvec: ctx.file2.equivs, + fdiag: fdiag, + bdiag: bdiag, + NOTE_INSERT: (d) -> { + ctx.file2.markChange(d); + }, + NOTE_DELETE: (d) -> { + ctx.file1.markChange(d); + } + } + Diffseq.compareseq(0, ctx.file1.equivs.length, 0, ctx.file2.equivs.length, dCtx); + // TODO: shiftBoundaries + return buildScript(ctx); + } +} diff --git a/tests/misc/src/diff/Change.hx b/tests/misc/src/diff/Change.hx new file mode 100644 index 00000000..e4cb63c6 --- /dev/null +++ b/tests/misc/src/diff/Change.hx @@ -0,0 +1,15 @@ +package diff; + +@:structInit +class Change { + @:optional + public var next:Null; + public var inserted:Int; + public var deleted:Int; + public var line0:Int; + public var line1:Int; + + public function toString() { + return '[CHANGE inserted: $inserted, deleted: $deleted, line0: $line0, line1: $line1]'; + } +} diff --git a/tests/misc/src/diff/Context.hx b/tests/misc/src/diff/Context.hx new file mode 100644 index 00000000..7fe4aea4 --- /dev/null +++ b/tests/misc/src/diff/Context.hx @@ -0,0 +1,8 @@ +package diff; + +@:structInit +class Context { + public final file1:FileData; + public final file2:FileData; + public var context:Int; +} diff --git a/tests/misc/src/diff/Diffseq.hx b/tests/misc/src/diff/Diffseq.hx new file mode 100644 index 00000000..cb62748b --- /dev/null +++ b/tests/misc/src/diff/Diffseq.hx @@ -0,0 +1,176 @@ +package diff; + +import haxe.ds.Vector; + +@:structInit +private class Parition { + public var xmid:Int; + public var ymid:Int; + + function toString() { + return '[Partition xmid: $xmid, ymid: $ymid]'; + } +} + +final OFFSET_MAX = 1 << 30; + +@:structInit +class DiffseqContext { + public final xvec:Vector; + public final yvec:Vector; + public final fdiag:IndexVector; + public final bdiag:IndexVector; + public final NOTE_DELETE:(Int) -> Void; + public final NOTE_INSERT:(Int) -> Void; +} + +class Diffseq { + static public function diag(xoff:Int, xlim:Int, yoff:Int, ylim:Int, ctxt:DiffseqContext):Parition { + var fd = ctxt.fdiag; + var bd = ctxt.bdiag; + var xv = ctxt.xvec; + var yv = ctxt.yvec; + inline function XREF_YREF_EQUAL(x, y) { + return xv[x] == yv[y]; + } + final dmin = xoff - ylim; + final dmax = xlim - yoff; + final fmid = xoff - yoff; + final bmid = xlim - ylim; + var fmin = fmid; + var fmax = fmid; + var bmin = bmid; + var bmax = bmid; + var odd = ((fmid - bmid) & 1) != 0; + fd[fmid] = xoff; + bd[bmid] = xlim; + while (true) { + var d; + if (fmin > dmin) { + fd[--fmin - 1] = -1; + } else { + ++fmin; + } + if (fmax < dmax) { + fd[++fmax + 1] = -1; + } else { + --fmax; + } + d = fmax; + while (d >= fmin) { + var tlo = fd[d - 1]; + var thi = fd[d + 1]; + var x0 = tlo < thi ? thi : tlo + 1; + var x = x0; + var y = x0 - d; + while (x < xlim && y < ylim && XREF_YREF_EQUAL(x, y)) { + x++; + y++; + } + fd[d] = x; + if (odd && bmin <= d && d <= bmax && bd[d] <= x) { + return { + xmid: x, + ymid: y + } + } + d -= 2; + } + + if (bmin > dmin) { + bd[--bmin - 1] = OFFSET_MAX; + } else { + ++bmin; + } + if (bmax < dmax) { + bd[++bmax + 1] = OFFSET_MAX; + } else { + --bmax; + } + d = bmax; + while (d >= bmin) { + var tlo = bd[d - 1]; + var thi = bd[d + 1]; + var x0 = tlo < thi ? tlo : thi - 1; + var x = x0; + var y = x0 - d; + while (xoff < x && yoff < y && XREF_YREF_EQUAL(x - 1, y - 1)) { + x--; + y--; + } + bd[d] = x; + if (!odd && fmin <= d && d <= fmax && x <= fd[d]) { + return { + xmid: x, + ymid: y + } + } + d -= 2; + } + } + } + + static public function compareseq(xoff:Int, xlim:Int, yoff:Int, ylim:Int, ctxt:DiffseqContext) { + final xv = ctxt.xvec; + final yv = ctxt.yvec; + inline function XREF_YREF_EQUAL(x, y) { + return xv[x] == yv[y]; + } + while (true) { + while (xoff < xlim && yoff < ylim && XREF_YREF_EQUAL(xoff, yoff)) { + xoff++; + yoff++; + } + + while (xoff < xlim && yoff < ylim && XREF_YREF_EQUAL(xlim - 1, ylim - 1)) { + xlim--; + ylim--; + } + + if (xoff == xlim) { + while (yoff < ylim) { + ctxt.NOTE_INSERT(yoff); + yoff++; + } + break; + } + if (yoff == ylim) { + while (xoff < xlim) { + ctxt.NOTE_DELETE(xoff); + xoff++; + } + break; + } + + var part = diag(xoff, xlim, yoff, ylim, ctxt); + + var xoff1, xlim1, yoff1, ylim1, xoff2, xlim2, yoff2, ylim2; + if ((xlim + ylim) - (part.xmid + part.ymid) < (part.xmid + part.ymid) - (xoff + yoff)) { + xoff1 = part.xmid; + xlim1 = xlim; + yoff1 = part.ymid; + ylim1 = ylim; + xoff2 = xoff; + xlim2 = part.xmid; + yoff2 = yoff; + ylim2 = part.ymid; + } else { + xoff1 = xoff; + xlim1 = part.xmid; + yoff1 = yoff; + ylim1 = part.ymid; + xoff2 = part.xmid; + xlim2 = xlim; + yoff2 = part.ymid; + ylim2 = ylim; + } + compareseq(xoff1, xlim1, yoff1, ylim1, ctxt); + + xoff = xoff2; + xlim = xlim2; + yoff = yoff2; + ylim = ylim2; + } + return false; + } +} diff --git a/tests/misc/src/diff/FileData.hx b/tests/misc/src/diff/FileData.hx new file mode 100644 index 00000000..f10c1e0c --- /dev/null +++ b/tests/misc/src/diff/FileData.hx @@ -0,0 +1,73 @@ +package diff; + +import haxe.io.Bytes; +import haxe.ds.Vector; + +@:structInit +private class Line { + public final pos:Int; + public final length:Int; +} + +class FileData { + public final data:Bytes; + public final label:String; + public final mtime:Date; + public var prefixEnd:Int; + public var prefixNewlines:Int; + public var suffixBegin:Int; + public var equivs:Vector; + public final equivCount:Array>; + public final lines:Array; + + public var changed:IndexVector; + + public function new(data:Bytes, label:String, mtime:Date) { + this.label = label; + this.mtime = mtime; + this.data = data; + prefixEnd = 0; + suffixBegin = data.length; + equivCount = []; + lines = []; + } + + #if sys + static public function loadFile(path:String) { + final stat = sys.FileSystem.stat(path); + return new FileData(sys.io.File.getBytes(path), path, stat.mtime); + } + #end + + public function addLine(pos:Int, length:Int) { + lines.push({pos: pos, length: length}); + } + + public function markChange(line:Int) { + changed[line] = 1; + } + + public function finishLineProcessing(equivs:Array) { + this.equivs = Vector.fromArrayCopy(equivs); + } + + public function increaseEquivCount(index:Int) { + if (equivCount[index] == null) { + equivCount[index] = 1; + } else { + equivCount[index]++; + } + } + + public function dump() { + var buf = new StringBuf(); + inline function add(s:String) { + buf.add(s); + } + add(' prefixEnd: ${prefixEnd}\n'); + add('suffixBegin: ${suffixBegin}\n'); + add(' equivs: ${equivs}\n'); + add(' equivCount: ${equivCount}\n'); + return buf.toString(); + } +} diff --git a/tests/misc/src/diff/IndexVector.hx b/tests/misc/src/diff/IndexVector.hx new file mode 100644 index 00000000..5de2a92f --- /dev/null +++ b/tests/misc/src/diff/IndexVector.hx @@ -0,0 +1,42 @@ +package diff; + +import haxe.ds.Vector; + +@:structInit +private class IV { + public final v:Vector; + public var index:Int; +} + +abstract IndexVector(IV) { + public inline function new(v:Vector, index:Int) { + this = { + v: v, + index: index + } + } + + @:op(A += B) + inline function addAssign(rhs:Int) { + this.index += rhs; + } + + @:op(A + B) + inline function add(delta:Int) { + return new IndexVector(this.v, this.index + delta); + } + + @:op([]) + inline function read(delta:Int) { + return this.v[this.index + delta]; + } + + @:op([]) + inline function write(delta:Int, v:Int) { + return this.v[this.index + delta] = v; + } + + public inline function toString() { + return '[IV +${this.index}]'; + } +} diff --git a/tests/misc/src/diff/Io.hx b/tests/misc/src/diff/Io.hx new file mode 100644 index 00000000..2e0e9208 --- /dev/null +++ b/tests/misc/src/diff/Io.hx @@ -0,0 +1,159 @@ +package diff; + +import haxe.io.Bytes; +import haxe.ds.IntMap; +import haxe.ds.Vector; + +using diff.Io.BytesTools; + +private typedef Hash = Int; + +class BytesTools { + static public function compareSub(bytes1:Bytes, pos1:Int, bytes2:Bytes, pos2:Int, length:Int) { + for (i in 0...length) { + if (bytes1.get(pos1 + i) != bytes2.get(pos2 + i)) { + return false; + } + } + return true; + } +} + +@:structInit +private class Equivclass { + public final hash:Hash; + public final bytes:Bytes; // This is the complete bytes, not the sub-range + public final pos:Int; + public final length:Int; +} + +private class IoContext { + final equivLut:IntMap>; + final equivs:Array; + + public function new() { + equivLut = new IntMap(); + equivs = []; + } + + public function addEquiv(equiv:Equivclass) { + return equivs.push(equiv) - 1; + } + + public function getEquiv(i:Int) { + return equivs[i]; + } + + public function lookup(hash:Hash) { + var a = equivLut.get(hash); + if (a == null) { + a = []; + equivLut.set(hash, a); + } + return a; + } +} + +class Io { + static inline function ROL(v, n) + return ((v) << (n) | (v) >> (32 - (n))); + + static inline function HASH(h, c) + return ((c) + ROL(h, 7)); + + static public function findIdenticalEnds(ctx:Context) { + var w1 = 0; + final file1 = ctx.file1; + final file2 = ctx.file2; + final len1 = file1.data.length; + final len2 = file2.data.length; + final min = len1 < len2 ? len1 : len2; + var newlineCount = 0; + final ringMod = ctx.context + 1; + final newlineRing = new Vector(ringMod); + var c; + while (w1 < min && (c = file1.data.get(w1)) == file2.data.get(w1)) { + if (c == '\n'.code) { + newlineRing[newlineCount++ % ringMod] = w1; + } + ++w1; + } + if (newlineCount < ringMod) { + file1.prefixEnd = 0; + file2.prefixEnd = 0; + newlineCount = 0; + } else { + file1.prefixEnd = newlineRing[newlineCount % ringMod] + 1; + file2.prefixEnd = file1.prefixEnd; + newlineCount -= ringMod - 1; + } + file1.prefixNewlines = file2.prefixNewlines = newlineCount; + + w1 = len1 - 1; + var w2 = len2 - 1; + newlineCount = 0; + while (w1 > file1.prefixEnd && w2 > file2.prefixEnd && (c = file1.data.get(w1)) == file2.data.get(w2)) { + if (c == '\n'.code) { + newlineRing[newlineCount++ % ringMod] = w1; + } + w1--; + w2--; + } + if (newlineCount < ringMod) { + file1.suffixBegin = len1 - 1; + file2.suffixBegin = len2 - 1; + } else { + file1.suffixBegin = newlineRing[newlineCount % ringMod] + 1; + file2.suffixBegin = file1.suffixBegin + (len2 - len1); + } + } + + static public function findAndHashEachLine(ctx:IoContext, file:FileData) { + var p = file.prefixEnd; + var c = 0; + final cureqs = []; + while (p < file.suffixBegin) { + final ip = p; + var h = 0; + // TODO: This has a big switch in io.c that depends on the whitespace policy + while ((c = file.data.get(p++)) != '\n'.code) { + h = HASH(h, c); + if (p == file.data.length) { + p++; + break; + } + } + final length = p - ip - 1; + final a = ctx.lookup(h); + var equivIndex = -1; + for (i in a) { + final equiv = ctx.getEquiv(i); + if (equiv.hash == h && equiv.length == length && file.data.compareSub(ip, equiv.bytes, equiv.pos, length)) { + equivIndex = i; + break; + } + } + if (equivIndex < 0) { + final equiv:Equivclass = { + hash: h, + bytes: file.data, + pos: ip, + length: length + } + equivIndex = ctx.addEquiv(equiv); + a.push(equivIndex); + } + cureqs.push(equivIndex); + file.increaseEquivCount(equivIndex); + file.addLine(ip, length); + } + file.finishLineProcessing(cureqs); + } + + static public function readFiles(ctx:Context) { + findIdenticalEnds(ctx); + var ioctx = new IoContext(); + findAndHashEachLine(ioctx, ctx.file1); + findAndHashEachLine(ioctx, ctx.file2); + } +} diff --git a/tests/misc/src/diff/Printer.hx b/tests/misc/src/diff/Printer.hx new file mode 100644 index 00000000..59bda18f --- /dev/null +++ b/tests/misc/src/diff/Printer.hx @@ -0,0 +1,122 @@ +package diff; + +class Printer { + static function printContextLabel(mark:String, file:FileData) { + var buf = new StringBuf(); + buf.add(mark); + buf.add(" "); + buf.add(file.label); + buf.add("\t"); + buf.add(file.mtime.toString()); + buf.add("\n"); + return buf.toString(); + } + + static function printContextHeader(ctx:Context) { + var buf = new StringBuf(); + buf.add(printContextLabel("---", ctx.file1)); + buf.add(printContextLabel("+++", ctx.file2)); + return buf.toString(); + } + + static function translateLineNumber(file:FileData, a:Int) { + return file.prefixNewlines + a + 1; + } + + static function printUnidiffNumberRange(file:FileData, a:Int, b:Int) { + var transA = translateLineNumber(file, a - 1) + 1; + var transB = translateLineNumber(file, b + 1) - 1; + + if (transB == transA) { + return '$transB,0'; + } else if (transB - transA == 1) { + return '1'; + } else { + return '$transA,${transB - transA}'; + } + } + + static function printUnidiffHunk(ctx:Context, change:Change) { + var buf = new StringBuf(); + var from0 = change.line0 - ctx.context; + if (from0 < 0) { + from0 = 0; + } + var from1 = change.line1 - ctx.context; + if (from1 < 0) { + from1 = 0; + } + final last = { + var next = change; + while (next.next != null) { + next = next.next; + } + next; + } + var to0 = last.line0 + last.deleted + ctx.context; + if (to0 >= ctx.file1.lines.length) { + to0 = ctx.file1.lines.length; + } + var to1 = last.line1 + last.inserted + ctx.context; + if (to1 >= ctx.file2.lines.length) { + to1 = ctx.file2.lines.length; + } + buf.add("@@ -"); + buf.add(printUnidiffNumberRange(ctx.file1, from0, to0)); + buf.add(" +"); + buf.add(printUnidiffNumberRange(ctx.file2, from1, to1)); + buf.add(" @@\n"); + + function printLine(file:FileData, prefix:String, i:Int) { + final line = file.lines[i]; + buf.add(prefix); + buf.add(file.data.sub(line.pos, line.length).toString()); + buf.addChar("\n".code); + } + var i = from0; + var j = from1; + while (i < to0 || j < to1) { + if (change == null || i < change.line0) { + printLine(ctx.file1, " ", i); + ++i; + ++j; + } else { + for (_ in 0...change.deleted) { + printLine(ctx.file1, "-", i++); + } + for (_ in 0...change.inserted) { + printLine(ctx.file2, "+", j++); + } + change = change.next; + } + } + return buf.toString(); + } + + static public function findHunk(ctx:Context, change:Change) { + final threshold = ctx.context * 2 + 1; + var previousChange = change; + while ((change = change.next) != null) { + if (change.line0 - (previousChange.line0 + previousChange.deleted) < threshold) { + previousChange = change; + continue; + } + break; + } + return previousChange; + } + + static public function printUnidiff(ctx:Context, change:Change) { + var buf = new StringBuf(); + buf.add(printContextHeader(ctx)); + var next = change; + while (next != null) { + var end = findHunk(ctx, next); + final cur = next; + next = end.next; + end.next = null; + buf.add(printUnidiffHunk(ctx, cur)); + } + return buf.toString(); + } +} diff --git a/tests/nullsafety/src/cases/TestStrict.hx b/tests/nullsafety/src/cases/TestStrict.hx index d3cc778c..49be574f 100644 --- a/tests/nullsafety/src/cases/TestStrict.hx +++ b/tests/nullsafety/src/cases/TestStrict.hx @@ -134,8 +134,8 @@ class TestStrict { function get_str() { shouldFail(return (null:Null)); } - function set_str(v) { - shouldFail(return (v:Null)); + function set_str(v:Null) { + shouldFail(return v); } /** @@ -996,6 +996,35 @@ class TestStrict { else a = 'hello'; var s:String = a; } + + /** + * @see https://github.com/HaxeFoundation/haxe/pull/10428#issuecomment-951574457 + */ + static function issue10428() { + final arr:Array = [ + { + var tmp = (1 : Null); + if (tmp != null) tmp else 2; + } + ]; + } + + static function issue9588_DynamicIsNullable_AnyIsNotNullable(?a:String) { + function dyn(v:Dynamic):Dynamic + return v; + var d:Dynamic = dyn(a); + // TODO: decide if 'dynamic to non-nullable' should fail since we allow 'nullable to dynamic now'. + //shouldFail(var s:String = d); + + function any(v:Any):Any + return v; + shouldFail(any(a)); + var s:String = any(''); + } + + static function issue10272_nullableConcatString_shouldPass(msg:Null) { + trace("Message: " + msg); + } } private class FinalNullableFields { diff --git a/tests/optimization/run.hxml b/tests/optimization/run.hxml index 19da056e..f9dd676b 100644 --- a/tests/optimization/run.hxml +++ b/tests/optimization/run.hxml @@ -20,6 +20,7 @@ --macro Macro.register('Test') --macro Macro.register('TestJs') --macro Macro.register('TestLocalDce') +--macro Macro.register('TestNadako') --macro Macro.register('TestTreGeneration') --macro Macro.register('TestInlineConstructors') --macro Macro.register('issues') diff --git a/tests/optimization/src/TestJs.hx b/tests/optimization/src/TestJs.hx index 96fb1dc6..8b57b3f9 100644 --- a/tests/optimization/src/TestJs.hx +++ b/tests/optimization/src/TestJs.hx @@ -26,6 +26,8 @@ private enum EnumFlagTest { @:analyzer(no_user_var_fusion) class TestJs { + static var notFalse = true; + //@:js('var x = 10;"" + x;var x1 = 10;"" + x1;var x2 = 10.0;"" + x2;var x3 = "10";x3;var x4 = true;"" + x4;') //static function testStdString() { //var x = 10; @@ -274,13 +276,26 @@ class TestJs { @:js(' var x = TestJs.getInt(); - var tmp = [x,"foo"]; - x = TestJs.getInt(); + TestJs.getInt(); + TestJs.call([x,"foo"],TestJs.getInt()); + ') + static function testMightActuallyNotBeAffected() { + var x = getInt(); + call([x, "foo"], { + x = getInt(); + getInt(); + }); + } + + @:js(' + TestJs.getInt(); + var tmp = TestJs.getImpureArray(); + TestJs.getInt(); TestJs.call(tmp,TestJs.getInt()); ') static function testMightBeAffected2() { var x = getInt(); - call([x, "foo"], { + call(getImpureArray(), { x = getInt(); getInt(); }); @@ -288,7 +303,7 @@ class TestJs { @:js(' var x = TestJs.getInt(); - TestJs.call(x++,TestJs.getInt()); + TestJs.call(x,TestJs.getInt()); ') static function testMightBeAffected3() { var x = getInt(); @@ -323,6 +338,29 @@ class TestJs { use(b); } + @:js(' + var a = Std.random(100); + var b = Std.random(100); + var c = Std.random(100); + var d = Std.random(100); + Std.random(100); + a = b + c; + b -= d; + TestJs.use(a + b); + ') + static function testIssue10972() { + var a = Std.random(100); + var b = Std.random(100); + var c = Std.random(100); + var d = Std.random(100); + var e = Std.random(100); + a = b + c; + b = b - d; + c = c + d; + e = b + c; + use(a + b); + } + @:js(' var a = TestJs.getInt(); TestJs.use(a); @@ -336,7 +374,7 @@ class TestJs { @:js(' var a = TestJs.getInt(); var b = a; - a = TestJs.getInt(); + TestJs.getInt(); TestJs.use(b); ') static function testCopyPropagation2() { @@ -484,10 +522,16 @@ class TestJs { @:pure(false) static function getInt(?d:Dynamic) { return 1; } static function getArray() { return [0, 1]; } + + @:pure(false) + static function getImpureArray() { return [0, 1]; } + @:pure(false) static function call(d1:Dynamic, d2:Dynamic) { return d1; } @:pure(false) static public function use(t:T) { return t; } + @:pure(false) + static function run(f:()->Void) {} static var intField = 12; static var stringField(default, never) = "foo"; @@ -647,6 +691,57 @@ class TestJs { var f = Issue9227.new.bind(1); f(3); } + + @:js(' + var c = new Issue10737(); + var _g = c; + var value = 42; + TestJs.run(function() {_g.process(value);}); + ') + static function testIssue10737_avoidInstanceMethodClosure() { + var c = new Issue10737(); + run(c.process.bind(42)); + c = null; + } + + @:js(' + var _g = new Issue10737(); + var value = 42; + TestJs.run(function() {_g.process(value);}); + ') + static function testIssue10737_avoidInstanceMethodClosure2() { + run(new Issue10737().process.bind(42)); + } + + @:js(' + var tmp = Issue10740.inst; + if(tmp != null) { + Issue10740.use(tmp.value); + } + ') + static function testIssue10740_forceInlineInSafeNav() { + inline Issue10740.inst?.f(); + } + + @:js(' + var offset = 0; + do { + TestJs.use(offset); + if(offset >= 3) { + break; + } + offset = 3; + } while(TestJs.notFalse); + ') + static function testDoWhile() { + var offset = 0; + do { + use(offset); + if (offset >= 3) + break; + offset = 3; + } while (notFalse); + } } class Issue9227 { @@ -665,4 +760,23 @@ abstract Issue8751Int(Int) from Int { inline public function toInt():Int { return this; } +} + +class Issue10737 { + public function new() {} + public function process(value:Int) {} +} + +class Issue10740 { + public static var inst:Issue10740; + + public final value:Int; + public function new() { + value = 42; + } + public function f() { + use(value); + } + @:pure(false) + static function use(v:Int) {} } \ No newline at end of file diff --git a/tests/optimization/src/TestNadako.hx b/tests/optimization/src/TestNadako.hx new file mode 100644 index 00000000..df6e6008 --- /dev/null +++ b/tests/optimization/src/TestNadako.hx @@ -0,0 +1,18 @@ +import TestJs.use; + +class TestNadako { + @:js('var r = []; + var _g_current = 0; + while(_g_current < _requirements.length) { + var _g_value = _requirements[_g_current]; + TestJs.use(_g_value); + r[_g_current++] = _g_value; + }') + static function testNadako1(_requirements:Array<{}>) { + var r = []; + for (i => resourceData in _requirements) { + use(resourceData); + r[i] = resourceData; + } + } +} \ No newline at end of file diff --git a/tests/optimization/src/issues/Issue10188.hx b/tests/optimization/src/issues/Issue10188.hx new file mode 100644 index 00000000..1eb5f0eb --- /dev/null +++ b/tests/optimization/src/issues/Issue10188.hx @@ -0,0 +1,64 @@ +package issues; + +enum abstract Value(Int) to Int { + final V1 = 1; + final V2 = 2; + + public inline function toString():String { + return switch (cast this:Value) { + case V1: 'V1'; + case V2: 'V2'; + } + } +} + +@:enum abstract ActorStateType(Int) from Int to Int { + var GoingLeft = 0; + var GoingRight = 1; +} + +class Issue10188 { + @:js(' + var _g = []; + if(issues_Issue10188.has(1)) { + _g.push("V1"); + } + issues_Issue10188.use(_g); + ') + static function testKevin() { + use([for(v in [V1]) if(has(v)) v.toString()]); + } + + + static function has(v:Value) { + return true; + } + + @:js(' + var state = 0; + if(Math.random() > 0.5) { + state = 0; + issues_Issue10188.use("left"); + } + issues_Issue10188.use(state); + ') + static function testNotKevin() { + var state = GoingLeft; + inline function set(s) { + state = s; + use(getAnim(s)); + } + if(Math.random() > 0.5) set(GoingLeft); + use(state); + } + + static inline function getAnim(state:ActorStateType):String { + return switch(state) { + case GoingLeft: 'left'; + case GoingRight: 'right'; + } + } + + @:pure(false) + static public function use(t:T) { return t; } +} \ No newline at end of file diff --git a/tests/optimization/src/issues/Issue10765.hx b/tests/optimization/src/issues/Issue10765.hx new file mode 100644 index 00000000..4ec153f3 --- /dev/null +++ b/tests/optimization/src/issues/Issue10765.hx @@ -0,0 +1,52 @@ +package issues; + +@:forward +private abstract ComplexArray(js.lib.DataView) { + public inline function new(length: Int) { + final buffer = new js.lib.ArrayBuffer(length * 2 * 4); + this = new js.lib.DataView(buffer, 0, buffer.byteLength); + } + + @:arrayAccess + public static inline function get(impl: ComplexArray, index: Int): Complex { + return new Complex(impl.getFloat32(index * 2 * 4), impl.getFloat32((index * 2 + 1) * 4)); + } + + @:arrayAccess + public static inline function set(impl: ComplexArray, index: Int, value: Complex): Complex { + impl.setFloat32(index * 2 * 4, value.real); + impl.setFloat32((index * 2 + 1) * 4, value.imag); + return value; + } +} + +private class Complex { + public var real: Float; + public var imag: Float; + + public inline function new(real: Float, imag: Float) { + this.real = real; + this.imag = imag; + } + + public inline function add(other: Complex): Complex { + return new Complex(this.real + other.real, this.imag + other.imag); + } +} + +class Issue10765 { + @:js(' + var buffer = new ArrayBuffer(80); + var array = new DataView(buffer,0,buffer.byteLength); + var real = array.getFloat32(0); + var imag = array.getFloat32(4); + array.setFloat32(0,real + real); + array.setFloat32(4,imag + imag); + ') + static function test() { + final array = new ComplexArray(10); + + final tmp = array[0]; + array[0] = tmp.add(tmp); + } +} \ No newline at end of file diff --git a/tests/optimization/src/issues/Issue7237.hx b/tests/optimization/src/issues/Issue7237.hx new file mode 100644 index 00000000..c384c6d7 --- /dev/null +++ b/tests/optimization/src/issues/Issue7237.hx @@ -0,0 +1,26 @@ +package issues; + +class Issue7237 { + + @:js(' + var f = function(a) { + var b = a; + return b; + }; + var f2 = function(a) { + var b = a; + return b; + }; + ') + @:analyzer(ignore) + static function main() { + var f = function(a) { + var b = a; + return b; + } + var f2 = function(a) { + var b = a; + return b; + } + } +} diff --git a/tests/optimization/src/issues/Issue7765.hx.disabled b/tests/optimization/src/issues/Issue7765.hx.disabled new file mode 100644 index 00000000..c782c5c8 --- /dev/null +++ b/tests/optimization/src/issues/Issue7765.hx.disabled @@ -0,0 +1,47 @@ +package issues; + +class Issue7765 { + @:pure(false) function add(m:Issue7765) + return this; + + function new(i:Int) {} + + static inline function pipe(m:Issue7765) + return m; + + @:js(' + new issues_Issue7765(0).add(new issues_Issue7765(1)).add(new issues_Issue7765(2)); + ') + static function test() { + new Issue7765(0).add(pipe(new Issue7765(1))).add(pipe(new Issue7765(2))); + } + + @:js(' + var tmp = new issues_Issue7765(0); + var m = new issues_Issue7765(1); + var tmp1 = tmp.add(m); + var m = new issues_Issue7765(2); + tmp1.add(m); + ') + @:analyzer(no_fusion) + static function testNoFusion() { + new Issue7765(0).add(pipe(new Issue7765(1))).add(pipe(new Issue7765(2))); + } + + @:js(' + new issues_Issue7765(0).add(new issues_Issue7765(1)); + ') + static function test2() { + new Issue7765(0).add({var m = new Issue7765(1); m; }); + } + + @:js(' + var tmp = new issues_Issue7765(0); + var m = new issues_Issue7765(1); + tmp.add(m); + ') + @:analyzer(no_fusion) + static function test2NoFusion() { + new Issue7765(0).add({var m = new Issue7765(1); m; }); + } +} diff --git a/tests/optimization/src/issues/Issue9943.hx b/tests/optimization/src/issues/Issue9943.hx new file mode 100644 index 00000000..d57b67f2 --- /dev/null +++ b/tests/optimization/src/issues/Issue9943.hx @@ -0,0 +1,55 @@ +package issues; + +private extern class Element { + static function get():Element; + var offset(default,null) : Int; + var style(default,null) : CSSStyleDeclaration; +} +@:pure +private extern class PureElement { + static function get():PureElement; + var offset(default,null) : Int; + var style(default,null) : CSSStyleDeclaration; +} +private extern class CSSStyleDeclaration { + var verticalAlign : Int; +} + +class Issue9943 { + static final foo = Std.random(0); + + @:js(' + var el = issues._Issue9943.Element.get(); + var a = el.offset + issues_Issue9943.foo; + el.style.verticalAlign = 1; + var b = el.offset + issues_Issue9943.foo; + el.style.verticalAlign = 2; + var c = el.offset + issues_Issue9943.foo; + issues_Issue9943.values(a,b,c); + ') + static function test() { + final el = Element.get(); + final a = (el.offset + foo); + el.style.verticalAlign = 1; + final b = (el.offset + foo); + el.style.verticalAlign = 2; + final c = (el.offset + foo); + values(a, b, c); + } + @:js(' + var el = issues._Issue9943.PureElement.get(); + el.style.verticalAlign = 1; + el.style.verticalAlign = 2; + issues_Issue9943.values(el.offset + issues_Issue9943.foo,el.offset + issues_Issue9943.foo,el.offset + issues_Issue9943.foo); + ') + static function test2() { + final el = PureElement.get(); + final a = (el.offset + foo); + el.style.verticalAlign = 1; + final b = (el.offset + foo); + el.style.verticalAlign = 2; + final c = (el.offset + foo); + values(a, b, c); + } + static function values(a, b, c) trace(a, b, c); +} diff --git a/tests/runci/Config.hx b/tests/runci/Config.hx index 7d1bd324..19faea00 100644 --- a/tests/runci/Config.hx +++ b/tests/runci/Config.hx @@ -1,6 +1,7 @@ package runci; import sys.FileSystem; +import haxe.io.Path; enum Ci { GithubActions; @@ -10,24 +11,28 @@ class Config { static public final systemName = Sys.systemName(); static public final cwd = Sys.getCwd(); static public final repoDir = FileSystem.fullPath("..") + "/"; - static public final unitDir = cwd + "unit/"; - static public final sysDir = cwd + "sys/"; + static public final unitDir = Path.join([cwd, "unit"]); + static public final sysDir = Path.join([cwd, "sys"]); static public final optDir = cwd + "optimization/"; - static public final miscDir = cwd + "misc/"; - static public final displayDir = cwd + "display/"; - static public final serverDir = cwd + "server/"; - static public final sourcemapsDir = cwd + "sourcemaps/"; - static public final nullSafetyDir = cwd + "nullsafety/"; - static public final threadsDir = cwd + "threads/"; + static public final displayDir = Path.join([cwd, "display"]); + static public final serverDir = Path.join([cwd, "server"]); + static public final sourcemapsDir = Path.join([cwd, "sourcemaps"]); + static public final nullSafetyDir = Path.join([cwd, "nullsafety"]); + static public final threadsDir = Path.join([cwd, "threads"]); + // this stands for third-party, but I've always wanted a party directory + static public final partyDir = Path.join([cwd, "party"]); + + static public function getMiscSubDir(...subDir:String) + return Path.join([cwd, "misc"].concat(subDir.toArray())); static public final ci:Null = - if (Sys.getEnv("GITHUB_WORKSPACE") != null) + if (Sys.getEnv("GITHUB_ACTIONS") == "true") GithubActions; else null; - static public function isCi():Bool { - return ci != null; + static public macro function isCi() { + return macro $v{ci != null}; } static public final colorSupported = switch [ci, systemName] { diff --git a/tests/runci/Linux.hx b/tests/runci/Linux.hx index 87200a8c..4d84e6c9 100644 --- a/tests/runci/Linux.hx +++ b/tests/runci/Linux.hx @@ -2,12 +2,34 @@ package runci; import runci.System.*; +using StringTools; + class Linux { + static public var arch(get, null):Arch; + + static function get_arch() { + if(arch == null) + arch = switch commandResult('arch', []).stdout.replace('\n', '') { + case 'arm64' | 'aarch64': Arm64; + case _: Amd64; + } + return arch; + } + static public function isAptPackageInstalled(aptPackage:String):Bool { return commandSucceed("dpkg-query", ["-W", "-f='${Status}'", aptPackage]); } + static inline function hasAptGet() { + // CI always runs on ubuntu, otherwise check for apt-get + return Config.isCi() || commandSucceed("type", ["apt-get"]); + } + static public function requireAptPackages(packages:Array):Void { + if (!hasAptGet()){ + infoMsg("System does not have apt-get installed."); + return; + } var notYetInstalled = [for (p in packages) if (!isAptPackageInstalled(p)) p]; if (notYetInstalled.length > 0) { var aptCacheDir = Sys.getEnv("APT_CACHE_DIR"); @@ -16,7 +38,12 @@ class Linux { } else { ["apt-get", "install", "-qqy"]; }; - runCommand("sudo", baseCommand.concat(notYetInstalled), true); + runNetworkCommand("sudo", baseCommand.concat(notYetInstalled)); } } -} \ No newline at end of file +} + +enum abstract Arch(Int) { + final Arm64; + final Amd64; +} diff --git a/tests/runci/System.hx b/tests/runci/System.hx index 4f1accd7..3e5ff48c 100644 --- a/tests/runci/System.hx +++ b/tests/runci/System.hx @@ -6,14 +6,15 @@ import runci.Config.*; using StringTools; -enum Failure { - Fail; +class CommandFailure extends haxe.Exception { + public final exitCode:Int; + public function new(exitCode:Int = 1) { + super("Command failed: " + Std.string(exitCode)); + this.exitCode = exitCode; + } } class System { - static public var success(default, null) = true; - - static public function successMsg(msg:String):Void { Sys.println(colorSupported ? '\x1b[32m' + msg + '\x1b[0m' : msg); } @@ -26,8 +27,8 @@ class System { static public function commandSucceed(cmd:String, args:Array):Bool { return try { - var p = new Process(cmd, args); - var succeed = p.exitCode() == 0; + final p = new Process(cmd, args); + final succeed = p.exitCode() == 0; p.close(); succeed; } catch(e:Dynamic) false; @@ -38,8 +39,8 @@ class System { stderr:String, exitCode:Int } { - var p = new Process(cmd, args); - var out = { + final p = new Process(cmd, args); + final out = { stdout: p.stdout.readAll().toString(), stderr: p.stderr.readAll().toString(), exitCode: p.exitCode() @@ -48,39 +49,65 @@ class System { return out; } + static inline function getDisplayCmd(cmd:String, ?args:Array) { + return cmd + (args == null ? '' : ' $args'); + } + /** Run a command using `Sys.command()`. - If the command exits with non-zero code, exit the whole script with the same code. - - If `useRetry` is `true`, the command will be re-run if it exits with non-zero code (3 trials). - It is useful for running network-dependent commands. + If the command exits with non-zero code, throws `CommandFailure` with the same code. */ - static public function runCommand(cmd:String, ?args:Array, useRetry:Bool = false, allowFailure:Bool = false):Void { - var trials = useRetry ? 3 : 1; - var exitCode:Int = 1; - var cmdStr = cmd + (args == null ? '' : ' $args'); + static public function runCommand(cmd:String, ?args:Array):Void { + final exitCode = showAndRunCommand(cmd, args); + + if (exitCode != 0) + throw new CommandFailure(exitCode); + } + + /** Runs command using `Sys.command()` but ignores failures. **/ + static public function attemptCommand(cmd:String, ?args:Array) { + showAndRunCommand(cmd, args); + } + + static function showAndRunCommand(cmd:String, args:Null>, ?displayed:String):Int { + if (displayed == null) + displayed = getDisplayCmd(cmd, args); - while (trials-->0) { - infoMsg('Command: $cmdStr'); + infoMsg('Command: $displayed'); - var t = Timer.stamp(); - exitCode = Sys.command(cmd, args); - var dt = Math.round(Timer.stamp() - t); + final t = Timer.stamp(); + final exitCode = Sys.command(cmd, args); + final dt = Math.round(Timer.stamp() - t); + + final msg = 'Command exited with $exitCode in ${dt}s: $displayed'; + if (exitCode != 0) + failMsg(msg); + else + successMsg(msg); + + return exitCode; + } - if (exitCode == 0) { - successMsg('Command exited with $exitCode in ${dt}s: $cmdStr'); + + static final TRIALS = 3; + /** + Run a command using `Sys.command()` with up to three attempts. Useful for running network-dependent commands. + + If all three attempts fail, throws a `CommandFailure` with the exit code of the last run. + **/ + static public function runNetworkCommand(cmd:String, ?args:Array) { + final cmdStr = getDisplayCmd(cmd, args); + + for (trial in 1...TRIALS+1){ + final exitCode = showAndRunCommand(cmd, args, cmdStr); + if (exitCode == 0) return; - } - else - failMsg('Command exited with $exitCode in ${dt}s: $cmdStr'); - if (trials > 0) { - infoMsg('Command will be re-run...'); - } - } + if (trial == TRIALS) + throw new CommandFailure(exitCode); - if (!allowFailure) - fail(); + infoMsg('Command will be re-run...'); + } } /** @@ -96,11 +123,6 @@ class System { } } - static public function fail():Void { - success = false; - throw Fail; - } - static public function addToPATH(path:String):Void { infoMsg('Prepending $path to PATH.'); switch (systemName) { @@ -111,41 +133,54 @@ class System { } } - static public function haxelibInstallGit(account:String, repository:String, ?branch:String, ?srcPath:String, useRetry:Bool = false, ?altName:String):Void { - var name:String = (altName == null) ? repository : altName; - try { - getHaxelibPath(name); - infoMsg('$name has already been installed.'); - } catch (e:Dynamic) { - var args:Array = ["git", name, 'https://github.com/$account/$repository']; - if (branch != null) { - args.push(branch); - } - if (srcPath != null) { - args.push(srcPath); - } + static function isLibraryInstalled(library:String):Bool { + return new Process("haxelib", ["path", library]).exitCode() == 0; + } - runCommand("haxelib", args, useRetry); - } + static public function haxelibInstallGit(account:String, repository:String, ?branch:String, ?srcPath:String, useRetry:Bool = false, ?altName:String):Void { + final name = (altName == null) ? repository : altName; + // It could only be installed already if we're on a local machine + if (!isCi() && isLibraryInstalled(name)) + return infoMsg('$name has already been installed.'); + + final args = ["git", name, 'https://github.com/$account/$repository']; + if (branch != null) + args.push(branch); + if (srcPath != null) + args.push(srcPath); + + if (useRetry) + runNetworkCommand("haxelib", args); + else + runCommand("haxelib", args); } static public function haxelibInstall(library:String):Void { - try { - getHaxelibPath(library); - infoMsg('$library has already been installed.'); - } catch (e:Dynamic) { - runCommand("haxelib", ["install", library]); - } + // It could only be installed already if we're on a local machine + if (!isCi() && isLibraryInstalled(library)) + return infoMsg('$library has already been installed.'); + runCommand("haxelib", ["install", library]); + } + + static public function haxelibDev(library:String, path:String):Void { + // It could only be installed already if we're on a local machine + if (!isCi() && isLibraryInstalled(library)) + return infoMsg('$library has already been installed.'); + runCommand("haxelib", ["dev", library, path]); } static public function haxelibRun(args:Array, useRetry:Bool = false):Void { - runCommand("haxelib", ["run"].concat(args), useRetry); + final allArgs = ["run"].concat(args); + if (useRetry) + runNetworkCommand("haxelib", allArgs); + else + runCommand("haxelib", allArgs); } static public function getHaxelibPath(libName:String) { - var proc = new Process("haxelib", ["path", libName]); + final proc = new Process("haxelib", ["path", libName]); + final code = proc.exitCode(); var result; - var code = proc.exitCode(); do { result = proc.stdout.readLine(); if (!result.startsWith("-L")) { @@ -163,4 +198,48 @@ class System { Sys.println('Changing directory to $path'); Sys.setCwd(path); } -} \ No newline at end of file + + static function mergeArgs(cmd:String, args:Array) { + return switch (Sys.systemName()) { + case "Windows": + [StringTools.replace(cmd, "/", "\\")].concat(args).map(haxe.SysTools.quoteWinArg.bind(_, true)).join(" "); + case _: + [cmd].concat(args).map(haxe.SysTools.quoteUnixArg).join(" "); + } + } + + /* command for setting the environment variable to set before sys tests */ + static final setCommand = if (Sys.systemName() == "Windows") "set"; else "export"; + static final nameAndValue = "EXISTS=1"; + + /** Prepares environment for system tests and runs `cmd` with `args` **/ + static public function runSysTest(cmd:String, ?args:Array) { + final toDisplay = getDisplayCmd(setCommand, [nameAndValue]) + ' && ' + getDisplayCmd(cmd, args); + + if (args != null) + cmd = mergeArgs(cmd, args); + + final fullCmd = '$setCommand $nameAndValue && $cmd'; + + final exitCode = showAndRunCommand(fullCmd, null, toDisplay); + + if (exitCode != 0) + throw new CommandFailure(exitCode); + } + + static final installPath = if (systemName == "Windows") + Sys.getEnv("USERPROFILE") + "/haxe-ci"; + else + Sys.getEnv("HOME") + "/haxe-ci"; + + /** Returns path where packages should be installed. **/ + public static inline function getInstallPath():String { + return installPath; + } + + /** Returns path where downloads should be placed. **/ + public static inline function getDownloadPath():String { + return installPath + "/downloads"; + } + +} diff --git a/tests/runci/TestTarget.hx b/tests/runci/TestTarget.hx index ade86653..ba33266a 100644 --- a/tests/runci/TestTarget.hx +++ b/tests/runci/TestTarget.hx @@ -6,10 +6,9 @@ enum abstract TestTarget(String) from String { var Js = "js"; var Lua = "lua"; var Php = "php"; - var Php7 = "php7"; var Cpp = "cpp"; var Cppia = "cppia"; - var Flash9 = "flash9"; + var Flash = "flash"; var Java = "java"; var Jvm = "jvm"; var Cs = "cs"; diff --git a/tests/runci/targets/Cpp.hx b/tests/runci/targets/Cpp.hx index d4d9349a..df65381f 100644 --- a/tests/runci/targets/Cpp.hx +++ b/tests/runci/targets/Cpp.hx @@ -6,6 +6,7 @@ import runci.Config.*; class Cpp { static public var gotCppDependencies = false; + static final miscCppDir = getMiscSubDir('cpp'); static public function getCppDependencies() { if (gotCppDependencies) return; @@ -13,7 +14,10 @@ class Cpp { //hxcpp dependencies switch (systemName) { case "Linux": - Linux.requireAptPackages(["gcc-multilib", "g++-multilib"]); + Linux.requireAptPackages(["gcc-multilib", switch Linux.arch { + case Arm64: "g++-multilib-arm-linux-gnueabi"; + case Amd64: "g++-multilib"; + }]); case "Mac": //pass } @@ -21,11 +25,11 @@ class Cpp { //install and build hxcpp try { - var path = getHaxelibPath("hxcpp"); + final path = getHaxelibPath("hxcpp"); infoMsg('hxcpp has already been installed in $path.'); } catch(e:Dynamic) { haxelibInstallGit("HaxeFoundation", "hxcpp", true); - var oldDir = Sys.getCwd(); + final oldDir = Sys.getCwd(); changeDirectory(getHaxelibPath("hxcpp") + "tools/hxcpp/"); runCommand("haxe", ["-D", "source-header=''", "compile.hxml"]); changeDirectory(oldDir); @@ -43,7 +47,17 @@ class Cpp { static public function run(args:Array, testCompiled:Bool, testCppia:Bool) { getCppDependencies(); - var archFlag = if (systemName == "Windows") "HXCPP_M32" else "HXCPP_M64"; + final isLinuxArm64 = systemName == 'Linux' && Linux.arch == Arm64; + + final archFlag = switch systemName { + case 'Windows': + 'HXCPP_M32'; + case 'Linux' if(Linux.arch == Arm64): + 'HXCPP_LINUX_ARM64'; + case _: + 'HXCPP_M64'; + } + if (testCompiled) { runCommand("rm", ["-rf", "cpp"]); runCommand("haxe", ["compile-cpp.hxml", "-D", archFlag].concat(args)); @@ -51,25 +65,37 @@ class Cpp { } if (testCppia) { - runCommand("haxe", ["compile-cppia-host.hxml"].concat(args)); + runCommand("haxe", ["compile-cppia-host.hxml", "-D", archFlag].concat(args)); runCommand("haxe", ["compile-cppia.hxml"].concat(args)); runCpp("bin/cppia/Host-debug", ["bin/unit.cppia"]); - runCpp("bin/cppia/Host-debug", ["bin/unit.cppia", "-jit"]); + + if (!isLinuxArm64) // FIXME + runCpp("bin/cppia/Host-debug", ["bin/unit.cppia", "-jit"]); } changeDirectory(sysDir); - runCommand("haxe", ["compile-cpp.hxml"].concat(args)); - runCpp("bin/cpp/Main-debug", []); + runCommand("haxe", ["-D", archFlag, "--each", "compile-cpp.hxml"].concat(args)); + runSysTest(FileSystem.fullPath("bin/cpp/Main-debug")); - changeDirectory(threadsDir); - runCommand("haxe", ["build.hxml", "-cpp", "export/cpp"]); - runCpp("export/cpp/Main"); + if (!isLinuxArm64) { // FIXME + changeDirectory(threadsDir); + runCommand("haxe", ["-D", archFlag, "build.hxml", "-cpp", "export/cpp"]); + runCpp("export/cpp/Main"); + } + + changeDirectory(getMiscSubDir("eventLoop")); + runCommand("haxe", ["build-cpp.hxml"]); + // TODO: check output like misc tests do + runCpp("cpp/Main"); // if (Sys.systemName() == "Mac") // { - // changeDirectory(miscDir + "cppObjc"); + // changeDirectory(getMiscSubDir("cppObjc")); // runCommand("haxe", ["build.hxml"]); // runCpp("bin/TestObjc-debug"); // } + + changeDirectory(miscCppDir); + runCommand("haxe", ["run.hxml"]); } } diff --git a/tests/runci/targets/Cs.hx b/tests/runci/targets/Cs.hx index 0060dc05..a2879cf6 100644 --- a/tests/runci/targets/Cs.hx +++ b/tests/runci/targets/Cs.hx @@ -5,8 +5,7 @@ import runci.System.*; import runci.Config.*; class Cs { - static var miscCsDir(get,never):String; - static inline function get_miscCsDir() return miscDir + 'cs/'; + static final miscCsDir = getMiscSubDir('cs'); static public function getCsDependencies() { switch (systemName) { @@ -20,7 +19,7 @@ class Cs { if (commandSucceed("mono", ["--version"])) infoMsg('mono has already been installed.'); else - runCommand("brew", ["install", "mono"], true); + runNetworkCommand("brew", ["install", "mono"]); runCommand("mono", ["--version"]); case "Windows": //pass @@ -33,7 +32,7 @@ class Cs { if (args == null) args = []; exe = FileSystem.fullPath(exe); switch (systemName) { - case "Linux", "Mac": + case "Linux" | "Mac": runCommand("mono", [exe].concat(args)); case "Windows": runCommand(exe, args); @@ -47,7 +46,7 @@ class Cs { for (noroot in [[], ["-D", "no_root"]]) for (erasegenerics in [[], ["-D", "erase_generics"]]) { - var extras = fastcast.concat(erasegenerics).concat(noroot); + final extras = fastcast.concat(erasegenerics).concat(noroot); runCommand("haxe", ['compile-cs.hxml'].concat(extras).concat(args)); runCs("bin/cs/bin/TestMain-Debug.exe"); @@ -60,7 +59,13 @@ class Cs { changeDirectory(sysDir); runCommand("haxe", ["compile-cs.hxml",'-D','fast_cast'].concat(args)); - runCs("bin/cs/bin/Main-Debug.exe", []); + final exe = FileSystem.fullPath("bin/cs/bin/Main-Debug.exe"); + switch (systemName) { + case "Windows": + runSysTest(exe); + case _: + runSysTest("mono", [exe]); + } changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "-cs", "export/cs"]); @@ -69,11 +74,11 @@ class Cs { changeDirectory(miscCsDir); runCommand("haxe", ["run.hxml"]); - changeDirectory(miscCsDir + "csTwoLibs"); + changeDirectory(getMiscSubDir("cs", "csTwoLibs")); for (i in 1...5) { runCommand("haxe", ['compile-$i.hxml','-D','fast_cast']); runCs("bin/main/bin/Main.exe"); } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Flash.hx b/tests/runci/targets/Flash.hx index 59263d2b..7c2710f9 100644 --- a/tests/runci/targets/Flash.hx +++ b/tests/runci/targets/Flash.hx @@ -1,180 +1,259 @@ package runci.targets; -import sys.io.File; import sys.FileSystem; -import haxe.io.Path; -import haxe.Json; +import sys.io.File; import sys.io.Process; +import haxe.Json; import haxe.Http; +import haxe.io.Path; import runci.System.*; +import runci.System.CommandFailure; import runci.Config.*; +using StringTools; + class Flash { - static var miscFlashDir(get,never):String; - static inline function get_miscFlashDir() return miscDir + 'flash/'; + static final miscFlashDir = getMiscSubDir('flash'); + static var playerLocation:String; - static public function getLatestFPVersion():Array { - var appcast = Xml.parse(haxe.Http.requestUrl("http://fpdownload2.macromedia.com/get/flashplayer/update/current/xml/version_en_mac_pep.xml")); - var versionStr = new haxe.xml.Access(appcast).node.XML.node.update.att.version; - return versionStr.split(",").map(Std.parseInt); + static final DEFAULT_APACHE_MIRROR = "https://downloads.apache.org/"; + + static function getPreferredApacheMirror() { + return try { + Json.parse(Http.requestUrl("https://www.apache.org/dyn/closer.lua?as_json=1")).preferred; + } catch (e) { + failMsg('Unable to determine preferred Apache mirror. Defaulting to $DEFAULT_APACHE_MIRROR'); + DEFAULT_APACHE_MIRROR; + }; + } + + static function downloadAndExtractFlexSdk(version:String, sdkPath:String):Void { + final apacheMirror = getPreferredApacheMirror(); + final archiveExtension = if (systemName == "Windows") "zip" else "tar.gz"; + final archiveName = 'apache-flex-sdk-${version}-bin.$archiveExtension'; + runNetworkCommand("wget", ["-nv", '${apacheMirror}/flex/${version}/binaries/$archiveName', "-O", getDownloadPath() + '/$archiveName']); + + if (systemName == "Windows") { + runCommand("7z", ["x", getDownloadPath() + '/$archiveName', "-o" + sdkPath, "-y"]); + } else { + runCommand("tar", ["-xf", getDownloadPath() + '/$archiveName', "-C", getInstallPath()]); + } } - static public function setupFlexSdk():Void { + static function setupFlexSdk():Void { if (commandSucceed("mxmlc", ["--version"])) { infoMsg('mxmlc has already been installed.'); + return; + } + // download flex sdk + final flexVersion = "4.16.1"; + final flexSdkPath = Path.normalize(getInstallPath() + '/apache-flex-sdk-${flexVersion}-bin'); + + if (FileSystem.exists(flexSdkPath)) { + infoMsg('Flex SDK found at $flexSdkPath'); } else { - var apacheMirror = Json.parse(Http.requestUrl("http://www.apache.org/dyn/closer.lua?as_json=1")).preferred; - var flexVersion = "4.16.0"; - runCommand("wget", ["-nv", '${apacheMirror}/flex/${flexVersion}/binaries/apache-flex-sdk-${flexVersion}-bin.tar.gz'], true); - runCommand("tar", ["-xf", 'apache-flex-sdk-${flexVersion}-bin.tar.gz', "-C", Sys.getEnv("HOME")]); - var flexsdkPath = Sys.getEnv("HOME") + '/apache-flex-sdk-${flexVersion}-bin'; - addToPATH(flexsdkPath + "/bin"); - var playerglobalswcFolder = flexsdkPath + "/player"; - FileSystem.createDirectory(playerglobalswcFolder + "/11.1"); - var flashVersion = runci.targets.Flash.getLatestFPVersion(); - runCommand("wget", ["-nv", 'http://download.macromedia.com/get/flashplayer/updaters/${flashVersion[0]}/playerglobal${flashVersion[0]}_${flashVersion[1]}.swc', "-O", playerglobalswcFolder + "/11.1/playerglobal.swc"], true); - File.saveContent(flexsdkPath + "/env.properties", 'env.PLAYERGLOBAL_HOME=$playerglobalswcFolder'); - runCommand("mxmlc", ["--version"]); + downloadAndExtractFlexSdk(flexVersion, flexSdkPath); } + addToPATH(flexSdkPath + "/bin"); + + // download playerglobal.swc + final playerGlobalSwcFolder = flexSdkPath + "/player"; + final playerGlobalSwcSubFolder = playerGlobalSwcFolder + "/27.0"; + FileSystem.createDirectory(playerGlobalSwcSubFolder); + + final playerGlobalSwcPath = '$playerGlobalSwcSubFolder/playerglobal.swc'; + + if (FileSystem.exists(playerGlobalSwcPath)) { + infoMsg('playerglobal.swc found at $playerGlobalSwcPath'); + } else { + final flashVersion = getLatestFPVersion(); + runNetworkCommand("wget", [ + "-nv", + 'https://fpdownload.macromedia.com/get/flashplayer/updaters/${flashVersion[0]}/playerglobal${flashVersion[0]}_${flashVersion[1]}.swc', + "-O", + playerGlobalSwcPath + ]); + } + // set playerglobal.swc + File.saveContent(flexSdkPath + "/env.properties", 'env.PLAYERGLOBAL_HOME=$playerGlobalSwcFolder'); + + // ensure flex sdk is working + runCommand("mxmlc", ["--version"]); } - static public var playerCmd:String; + static function getLatestFPVersion():Array { + final appcast = Xml.parse(haxe.Http.requestUrl("http://fpdownload.macromedia.com/get/flashplayer/update/current/xml/version_en_mac_pep.xml")); + final versionStr = new haxe.xml.Access(appcast).node.XML.node.update.att.version; + return versionStr.split(",").map(Std.parseInt); + } - static public function setupFlashPlayerDebugger():Void { - var mmcfgPath = switch (systemName) { - case "Linux": - Sys.getEnv("HOME") + "/mm.cfg"; - case "Mac": - "/Library/Application Support/Macromedia/mm.cfg"; + static function downloadPlayer(version:Int, fileName:String, outputPath:String) { + runNetworkCommand("wget", [ + "-nv", + 'https://fpdownload.macromedia.com/pub/flashplayer/updaters/$version/$fileName', + "-O", + '$outputPath/$fileName' + ]); + } + + static function setupFlashPlayerMac():Void { + playerLocation = getInstallPath() + "/Flash Player.app"; + + if (FileSystem.exists(playerLocation)) + infoMsg('Flash player found at $playerLocation'); + else { + final majorVersion = getLatestFPVersion()[0]; + final packageName = 'flashplayer_${majorVersion}_sa_debug.dmg'; + downloadPlayer(majorVersion, packageName, getDownloadPath()); + runCommand("hdiutil", ["attach", getDownloadPath() + '/$packageName']); + + runCommand("cp", ["-R", "/Volumes/Flash Player/Flash Player.app", getInstallPath()]); + runCommand("hdiutil", ["detach", "/Volumes/Flash Player"]); + + // Disable the "application downloaded from Internet" warning + runCommand("xattr", ["-d", "-r", "com.apple.quarantine", playerLocation]); + } + + runCommand("open", ["-a", playerLocation, "-v"]); + } + + static function setupFlashPlayerLinux():Void { + final playerName = "flashplayerdebugger"; + playerLocation = Path.join([getInstallPath(), playerName]); + if (Sys.command("type", [playerName]) == 0) { + playerLocation = playerName; + infoMsg('Using $playerName from PATH'); + } else if(FileSystem.exists(playerLocation)) { + infoMsg('Flash player found at $playerLocation'); + } else { + Linux.requireAptPackages(["libglib2.0-0", "libfreetype6"]); + final tarFileName = 'flash_player_sa_linux_debug.x86_64.tar.gz'; + downloadPlayer(getLatestFPVersion()[0], tarFileName, getDownloadPath()); + runCommand("tar", ["-xf", getDownloadPath() + '/$tarFileName', "-C", getInstallPath()]); + } + + // ensure the debugger works + switch (ci) { + case GithubActions: + runCommand("xvfb-run", ["-a", playerLocation, "-v"]); case _: - throw "unsupported system"; + runCommand(playerLocation, ["-v"]); } + } + + static function setupFlashPlayerWindows():Void { + final majorVersion = getLatestFPVersion()[0]; + final exeName = 'flashplayer_${majorVersion}_sa_debug.exe'; + playerLocation = Path.join([getInstallPath(), exeName]); + + if (FileSystem.exists(playerLocation)) + infoMsg('Flash player found at $playerLocation'); + else + downloadPlayer(majorVersion, exeName, getInstallPath()); + } + static function setupFlashPlayer():Void { switch (systemName) { case "Linux": - playerCmd = "flashplayerdebugger"; - if(Sys.command("type", [playerCmd]) != 0) { - Linux.requireAptPackages([ - "libglib2.0", "libfreetype6" - ]); - var majorVersion = getLatestFPVersion()[0]; - runCommand("wget", ["-nv", 'http://fpdownload.macromedia.com/pub/flashplayer/updaters/${majorVersion}/flash_player_sa_linux_debug.x86_64.tar.gz'], true); - runCommand("tar", ["-xf", "flash_player_sa_linux_debug.x86_64.tar.gz", "-C", Sys.getEnv("HOME")]); - playerCmd = Path.join([Sys.getEnv("HOME"), "flashplayerdebugger"]); - } - if (!FileSystem.exists(mmcfgPath)) { - File.saveContent(mmcfgPath, "ErrorReportingEnable=1\nTraceOutputFileEnable=1"); - } - switch (ci) { - case GithubActions: - runCommand("xvfb-run", ["-a", playerCmd, "-v"]); - case _: - runCommand(playerCmd, ["-v"]); - } + setupFlashPlayerLinux(); case "Mac": - if (commandResult("brew", ["cask", "list", "flash-player-debugger"]).exitCode == 0) { + setupFlashPlayerMac(); + case "Windows": + setupFlashPlayerWindows(); + case _: + throw "unsupported system"; + } + } + + static function readUntil(stdout:haxe.io.Input, expected:String, ?unexpectedStrings:MapVoid>) { + final possibleStrings = unexpectedStrings?.copy() ?? []; + possibleStrings[expected] = function() {}; + var output = ""; + while (true) { + final char = try { + String.fromCharCode(stdout.readByte()); + } catch (e:haxe.io.Eof) { + failMsg('Expected to find "$expected" in stdout'); + throw new CommandFailure(); + }; + Sys.print(char); + output += char; + for (string => onMatch in possibleStrings) { + if (output.endsWith(string)) { + onMatch(); return; } - runCommand("brew", ["uninstall", "openssl@1.0.2t"], false, true); - runCommand("brew", ["uninstall", "python@2.7.17"], false, true); - runCommand("brew", ["untap", "local/openssl"], false, true); - runCommand("brew", ["untap", "local/python2"], false, true); - runCommand("brew", ["update"]); - runCommand("brew", ["install", "--cask", "flash-player-debugger"]); - - // Disable the "application downloaded from Internet" warning - runCommand("xattr", ["-d", "-r", "com.apple.quarantine", "/Applications/Flash Player Debugger.app"]); - - var dir = Path.directory(mmcfgPath); - if (!FileSystem.exists(dir)) { - runCommand("sudo", ["mkdir", "-p", dir]); - runCommand("sudo", ["chmod", "a+w", dir]); - } - if (!FileSystem.exists(mmcfgPath)) { - File.saveContent(mmcfgPath, "ErrorReportingEnable=1\nTraceOutputFileEnable=1"); - } + } } } /** Run a Flash swf file. - Return whether the test is successful or not. - It detemines the test result by reading the flashlog.txt, looking for "SUCCESS: true". + Throws `CommandFailure` if unsuccessful. */ - static public function runFlash(swf:String):Bool { + static function runFlash(swf:String):Void { swf = FileSystem.fullPath(swf); - Sys.println('going to run $swf'); - switch (systemName) { - case "Linux": - switch (ci) { - case GithubActions: - new Process("xvfb-run", ["-a", playerCmd, swf]); - case _: - new Process(playerCmd, [swf]); - } - case "Mac": - Sys.command("open", ["-a", "/Applications/Flash Player Debugger.app", swf]); - } + infoMsg('Running .swf file: $swf'); - //wait a little until flashlog.txt is created - var flashlogPath = switch (systemName) { - case "Linux": - Sys.getEnv("HOME") + "/.macromedia/Flash_Player/Logs/flashlog.txt"; - case "Mac": - Sys.getEnv("HOME") + "/Library/Preferences/Macromedia/Flash Player/Logs/flashlog.txt"; - case _: - throw "unsupported system"; - } + final debuggerProcess = new Process("fdb"); - for (t in 0...5) { - runCommand("sleep", ["2"]); - if (FileSystem.exists(flashlogPath)) - break; - } - if (!FileSystem.exists(flashlogPath)) { - failMsg('$flashlogPath not found.'); - return false; + final FDB_PROMPT = "(fdb) "; + // waits for the fdb prompt and runs a command + function runDebuggerCommand(command:String) { + readUntil(debuggerProcess.stdout, FDB_PROMPT); + Sys.println(command); + debuggerProcess.stdin.writeString('$command\n'); } - //read flashlog.txt continously - var traceProcess = new Process("tail", ["-f", flashlogPath]); - var success = false; - while (true) { - try { - var line = traceProcess.stdout.readLine(); - if (line.indexOf("success: ") >= 0) { - success = line.indexOf("success: true") >= 0; - break; - } - } catch (e:haxe.io.Eof) { - break; - } + runDebuggerCommand("run"); + + function onUnexpectedPrompt() { + Sys.println("quit"); + debuggerProcess.stdin.writeString("quit\n"); + throw new CommandFailure(); } - traceProcess.kill(); - traceProcess.close(); - Sys.command("cat", [flashlogPath]); - return success; + + readUntil(debuggerProcess.stdout, "Waiting for Player to connect", [FDB_PROMPT => onUnexpectedPrompt]); + final playerProcess = switch (systemName) { + case "Linux" if (ci == GithubActions): + new Process("xvfb-run", ["-a", playerLocation, swf]); + case "Mac": + new Process("open", ["-a", playerLocation, swf]); + default: + new Process(playerLocation, [swf]); + }; + + readUntil(debuggerProcess.stdout, "Player connected; session starting.", [FDB_PROMPT => onUnexpectedPrompt]); + runDebuggerCommand("continue"); + + var success = true; + readUntil(debuggerProcess.stdout, "(success: true)", [ + FDB_PROMPT => onUnexpectedPrompt, + "(success: false)" => () -> { success = false; } + ]); + + runDebuggerCommand("quit"); + + debuggerProcess.kill(); + debuggerProcess.close(); + playerProcess.kill(); + playerProcess.close(); + + if (!success) + throw new CommandFailure(); } static public function run(args:Array) { - setupFlashPlayerDebugger(); + setupFlashPlayer(); setupFlexSdk(); - var success = true; - for (argsVariant in [[], ["--swf-version", "32"]]) { - runCommand("haxe", ["compile-flash9.hxml", "-D", "fdb", "-D", "dump", "-D", "dump_ignore_var_ids"].concat(args).concat(argsVariant)); - var runSuccess = runFlash("bin/unit9.swf"); - if (!runSuccess) { - success = false; - } + for (flashVersion in ["11", "32"]) { + runCommand("haxe", ["compile-flash.hxml", "-D", "fdb", "-D", "dump", "-D", "dump_ignore_var_ids", "--swf-version", flashVersion].concat(args)); + runFlash("bin/unit.swf"); } changeDirectory(miscFlashDir); runCommand("haxe", ["run.hxml"]); - - if (!success) - fail(); } - } diff --git a/tests/runci/targets/Hl.hx b/tests/runci/targets/Hl.hx index 6c3725de..4fdddf8d 100644 --- a/tests/runci/targets/Hl.hx +++ b/tests/runci/targets/Hl.hx @@ -5,79 +5,98 @@ import sys.FileSystem; import runci.System.*; import runci.Config.*; +using StringTools; + class Hl { - static var hlSrc = switch [ci, systemName] { - case [GithubActions, "Windows"]: "C:\\hashlink"; - case _: Path.join([Sys.getEnv("HOME"), "hashlink"]); - }; - static var hlBuild = switch [ci, systemName] { - case [GithubActions, "Windows"]: "C:\\hashlink_build"; - case _: Path.join([Sys.getEnv("HOME"), "hashlink_build"]); - }; - static var hlBinDir = switch [ci, systemName] { - case [GithubActions, "Windows"]: "C:\\hashlink_build\\bin"; - case _: Path.join([Sys.getEnv("HOME"), "hashlink_build", "bin"]); - }; - static var hlBinary = switch [ci, systemName] { - case [GithubActions, "Windows"]: "C:\\hashlink_build\\bin\\hl.exe"; - case _: Path.join([Sys.getEnv("HOME"), "hashlink_build", "bin", "hl"]); - }; - - static public function getHlDependencies() { - if (commandSucceed("hl", ["--version"])) { - infoMsg('hl has already been installed.'); - return; - } - if (!FileSystem.exists(hlSrc)) { - runCommand("git", ["clone", "https://github.com/HaxeFoundation/hashlink.git", hlSrc]); - } else infoMsg("Reusing hashlink repository"); - - switch (systemName) { - case "Linux": - Linux.requireAptPackages(["libpng-dev", "libjpeg-turbo8-dev", "libturbojpeg", "zlib1g-dev", "libvorbis-dev"]); - case "Mac": - runCommand("brew", ["update", '--preinstall'], true); - runCommand("brew", ["bundle", '--file=${hlSrc}/Brewfile'], true); - case "Windows": - //pass - } - - FileSystem.createDirectory(hlBuild); - var generator = systemName == "Windows" ? ["-DCMAKE_SYSTEM_VERSION=10.0.19041.0"] : ["-GNinja"]; - runCommand("cmake", generator.concat([ - "-DBUILD_TESTING=OFF", - "-DWITH_BULLET=OFF", - "-DWITH_DIRECTX=OFF", - "-DWITH_FMT=ON", - "-DWITH_OPENAL=OFF", - "-DWITH_SDL=OFF", - "-DWITH_SQLITE=OFF", - "-DWITH_SSL=OFF", - "-DWITH_UI=OFF", - "-DWITH_UV=OFF", - "-DWITH_VIDEO=OFF", - "-B" + hlBuild, - "-H" + hlSrc - ])); - runCommand("cmake", [ - "--build", hlBuild - ]); - - runCommand(hlBinary, ["--version"]); - addToPATH(hlBinDir); - } - - static public function run(args:Array) { - getHlDependencies(); - runCommand("haxe", ["compile-hl.hxml"].concat(args)); - runCommand(hlBinary, ["bin/unit.hl"]); - - changeDirectory(threadsDir); - runCommand("haxe", ["build.hxml", "-hl", "export/threads.hl"]); - runCommand("hl", ["export/threads.hl"]); - - changeDirectory(sysDir); - runCommand("haxe", ["compile-hl.hxml"].concat(args)); - runCommand(hlBinary, ["bin/hl/sys.hl"]); - } + static final hlSrc = Path.join([getDownloadPath(), "hashlink"]); + + static final hlBuild = Path.join([getInstallPath(), "hashlink_build"]); + + static final hlBuildBinDir = Path.join([getInstallPath(), "hashlink_build", "bin"]); + + static final hlBinary = + if (isCi() || !commandSucceed("hl", ["--version"])){ + Path.join([hlBuildBinDir, "hl"]) + ((systemName == "Windows") ? ".exe" : ""); + } else { + commandResult(if(systemName == "Windows") "where" else "which", ["hl"]).stdout.trim(); + }; + + static final miscHlDir = getMiscSubDir('hl'); + + static public function getHlDependencies() { + if (!isCi() && FileSystem.exists(hlBinary)) { + infoMsg('hl has already been installed at $hlBinary.'); + return; + } + if (!FileSystem.exists(hlSrc)) + runCommand("git", ["clone", "https://github.com/HaxeFoundation/hashlink.git", hlSrc]); + else + infoMsg("Reusing hashlink repository"); + + switch (systemName) { + case "Linux": + Linux.requireAptPackages(["libpng-dev", "libjpeg-turbo8-dev", "libturbojpeg", "zlib1g-dev", "libvorbis-dev", "libsqlite3-dev"]); + case "Mac": + runNetworkCommand("brew", ["update", '--preinstall']); + runNetworkCommand("brew", ["bundle", '--file=${hlSrc}/Brewfile']); + case "Windows": + //pass + } + + FileSystem.createDirectory(hlBuild); + final generator = systemName == "Windows" ? ["-DCMAKE_SYSTEM_VERSION=10.0.19041.0"] : ["-GNinja"]; + runCommand("cmake", generator.concat([ + "-DBUILD_TESTING=OFF", + "-DWITH_DIRECTX=OFF", + "-DWITH_FMT=ON", + "-DWITH_OPENAL=OFF", + "-DWITH_SDL=OFF", + "-DWITH_SQLITE=ON", + "-DWITH_SSL=OFF", + "-DWITH_UI=OFF", + "-DWITH_UV=OFF", + "-DWITH_VIDEO=OFF", + "-B" + hlBuild, + "-H" + hlSrc + ])); + runCommand("cmake", [ + "--build", hlBuild + ]); + + runCommand(hlBinary, ["--version"]); + addToPATH(hlBuildBinDir); + + haxelibDev("hashlink", '$hlSrc/other/haxelib/'); + } + + static public function run(args:Array) { + getHlDependencies(); + + switch (systemName) { + case "Windows": + runCommand("haxe", ["compile-hl.hxml"].concat(args)); + case _: + runCommand("haxe", [ + "compile-hl.hxml", + "-D", "no_http", // hl's ssl.hdll is only built on Windows + ].concat(args)); + } + runCommand(hlBinary, ["bin/unit.hl"]); + + changeDirectory(threadsDir); + runCommand("haxe", ["build.hxml", "-hl", "export/threads.hl"]); + runCommand(hlBinary, ["export/threads.hl"]); + + changeDirectory(sysDir); + runCommand("haxe", ["compile-hl.hxml"].concat(args)); + runSysTest(hlBinary, ["bin/hl/sys.hl"]); + + changeDirectory(getMiscSubDir("eventLoop")); + runCommand("haxe", ["build-hl.hxml"]); + // TODO: check output like misc tests do + runCommand(hlBinary, ["eventLoop.hl"]); + + changeDirectory(miscHlDir); + runCommand("haxe", ["run.hxml"]); + } } diff --git a/tests/runci/targets/Java.hx b/tests/runci/targets/Java.hx index 3de76911..1248b417 100644 --- a/tests/runci/targets/Java.hx +++ b/tests/runci/targets/Java.hx @@ -1,13 +1,13 @@ package runci.targets; import sys.FileSystem; +import haxe.io.Path; import runci.System.*; import runci.Config.*; using StringTools; class Java { - static var miscJavaDir(get,never):String; - static inline function get_miscJavaDir() return miscDir + 'java/'; + static final miscJavaDir = getMiscSubDir('java'); static public function getJavaDependencies() { haxelibInstallGit("HaxeFoundation", "hxjava", true); @@ -29,27 +29,24 @@ class Java { changeDirectory(sysDir); runCommand("haxe", ["compile-java.hxml"].concat(args)); - runCommand("java", ["-jar", "bin/java/Main-Debug.jar"]); + runSysTest("java", ["-jar", "bin/java/Main-Debug.jar"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "-java", "export/java"].concat(args)); runCommand("java", ["-jar", "export/java/Main.jar"]); infoMsg("Testing java-lib extras"); - changeDirectory('$unitDir/bin'); - if (!FileSystem.exists('java-lib-tests')) { - runCommand("git", ["clone", "https://github.com/waneck/java-lib-tests.git", "--depth", "1"], true); - } - for (dir in FileSystem.readDirectory('java-lib-tests')) - { - var path = 'java-lib-tests/$dir'; - if (FileSystem.isDirectory(path)) for (file in FileSystem.readDirectory(path)) - { - if (file.endsWith('.hxml')) - { - runCommand("haxe", ["--cwd",'java-lib-tests/$dir',file]); - } - } + changeDirectory(Path.join([unitDir, 'bin'])); + final libTestDir = 'java-lib-tests'; + if (!FileSystem.exists(libTestDir)) + runNetworkCommand("git", ["clone", "https://github.com/waneck/java-lib-tests.git", "--depth", "1"]); + + for (dir in FileSystem.readDirectory(libTestDir)) { + final path = Path.join([libTestDir, dir]); + if (FileSystem.isDirectory(path)) + for (file in FileSystem.readDirectory(path)) + if (file.endsWith('.hxml')) + runCommand("haxe", ["--cwd", path, file]); } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Js.hx b/tests/runci/targets/Js.hx index cb62df8d..33de3b78 100644 --- a/tests/runci/targets/Js.hx +++ b/tests/runci/targets/Js.hx @@ -9,6 +9,8 @@ import sys.io.Process; using StringTools; class Js { + static final miscJsDir = getMiscSubDir('js'); + static public function getJSDependencies() { switch [ci, systemName] { case [_, "Linux"]: @@ -24,28 +26,45 @@ class Js { runCommand("node", ["-v"]); } + static function installNpmPackages(packages:Array) { + final required = if (isCi()) { + packages; + } else { + final filtered = packages.filter( (lib) -> { + final isInstalled = commandSucceed("npm", ["list", lib]); + if (isInstalled) + infoMsg('npm package `$lib` has already been installed.'); + return !isInstalled; + }); + if (filtered.length == 0) + return; + filtered; + }; + runNetworkCommand("npm", ["install"].concat(required)); + } + static public function run(args:Array) { getJSDependencies(); - var jsOutputs = [ + final jsOutputs = [ for (es_ver in [[], ["-D", "js-es=3"], ["-D", "js-es=6"]]) for (unflatten in [[], ["-D", "js-unflatten"]]) for (classic in [[], ["-D", "js-classic"]]) for (enums_as_objects in [[], ["-D", "js-enums-as-arrays"]]) { - var extras = args.concat(es_ver).concat(unflatten).concat(classic).concat(enums_as_objects); + final extras = args.concat(es_ver).concat(unflatten).concat(classic).concat(enums_as_objects); runCommand("haxe", ["compile-js.hxml"].concat(extras)); - var output = if (extras.length > 0) { + final output = if (extras.length > 0) { "bin/js/" + extras.join("") + "/unit.js"; } else { "bin/js/default/unit.js"; } - var outputDir = Path.directory(output); - if (!FileSystem.exists(outputDir)) { + final outputDir = Path.directory(output); + if (!FileSystem.exists(outputDir)) FileSystem.createDirectory(outputDir); - } + FileSystem.rename("bin/unit.js", output); FileSystem.rename("bin/unit.js.map", output + ".map"); runCommand("node", ["-e", "require('./" + output + "').unit.TestMain.main();"]); @@ -54,17 +73,17 @@ class Js { ]; infoMsg("Test ES6:"); - changeDirectory(miscDir + "es6"); + changeDirectory(getMiscSubDir("es6")); runCommand("haxe", ["run.hxml"]); haxelibInstallGit("HaxeFoundation", "hxnodejs"); - var env = Sys.environment(); + final env = Sys.environment(); if ( env.exists("SAUCE") && env.exists("SAUCE_USERNAME") && env.exists("SAUCE_ACCESS_KEY") ) { - var sc = switch (ci) { + final sc = switch (ci) { // TODO: figure out SauceConnect for GitHub Actions // case AzurePipelines: // var scVersion = "sc-4.5.3-linux"; @@ -87,9 +106,9 @@ class Js { } changeDirectory(unitDir); - runCommand("npm", ["install", "wd", "q"], true); + installNpmPackages(["wd", "q"]); runCommand("haxe", ["compile-saucelabs-runner.hxml"]); - var server = new Process("nekotools", ["server"]); + final server = new Process("nekotools", ["server"]); runCommand("node", ["bin/RunSauceLabs.js"].concat([for (js in jsOutputs) "unit-js.html?js=" + js.urlEncode()])); server.close(); @@ -107,5 +126,13 @@ class Js { changeDirectory(serverDir); runCommand("haxe", ["build.hxml"]); runCommand("node", ["test.js"]); + + changeDirectory(sysDir); + installNpmPackages(["deasync"]); + runCommand("haxe", ["compile-js.hxml"].concat(args)); + runSysTest("node", ["bin/js/sys.js"]); + + changeDirectory(miscJsDir); + runCommand("haxe", ["run.hxml"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Jvm.hx b/tests/runci/targets/Jvm.hx index 50b8c109..1aa4927e 100644 --- a/tests/runci/targets/Jvm.hx +++ b/tests/runci/targets/Jvm.hx @@ -5,8 +5,11 @@ import runci.Config.*; class Jvm { static public function run(args:Array) { + deleteDirectoryRecursively("bin/jvm"); + Java.getJavaDependencies(); + for (level in 0...3) { - var args = args.concat(["-D", "jvm.dynamic-level=" + level]); + final args = args.concat(["-D", "jvm.dynamic-level=" + level]); runCommand("haxe", ["compile-jvm.hxml"].concat(args)); runCommand("java", ["-jar", "bin/unit.jar"]); @@ -16,10 +19,10 @@ class Jvm { changeDirectory(sysDir); runCommand("haxe", ["compile-jvm.hxml"].concat(args)); - runCommand("java", ["-jar", "bin/jvm/sys.jar"]); + runSysTest("java", ["-jar", "bin/jvm/sys.jar"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--jvm", "export/threads.jar"].concat(args)); runCommand("java", ["-jar", "export/threads.jar"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Lua.hx b/tests/runci/targets/Lua.hx index a630ad92..c50a279b 100644 --- a/tests/runci/targets/Lua.hx +++ b/tests/runci/targets/Lua.hx @@ -1,39 +1,41 @@ package runci.targets; -import sys.FileSystem; import runci.System.*; import runci.Config.*; import haxe.io.*; using StringTools; class Lua { - static var miscLuaDir(get,never):String; - static inline function get_miscLuaDir() return miscDir + 'lua/'; + static final miscLuaDir = getMiscSubDir('lua'); static public function getLuaDependencies(){ switch (systemName){ case "Linux": - Linux.requireAptPackages(["libpcre3-dev", "libssl-dev", "libreadline-dev"]); + Linux.requireAptPackages(["libpcre2-dev", "libssl-dev", "libreadline-dev"]); runCommand("pip", ["install", "--user", "hererocks"]); - var pyUserBase = commandResult("python", ["-m", "site", "--user-base"]).stdout.trim(); + final pyUserBase = commandResult("python", ["-m", "site", "--user-base"]).stdout.trim(); addToPATH(Path.join([pyUserBase, "bin"])); case "Mac": { if (commandSucceed("python3", ["-V"])) infoMsg('python3 has already been installed.'); else - runCommand("brew", ["install", "python3"], true); + runNetworkCommand("brew", ["install", "python3"]); - runCommand("brew", ["install", "pcre"], false, true); + attemptCommand("brew", ["install", "pcre2"]); runCommand("pip3", ["install", "hererocks"]); + runCommand("brew", ["install", "openssl"]); } } } static function installLib(lib : String, version : String, ?server :String){ if (!commandSucceed("luarocks", ["show", lib, version])) { - var args = ["install", lib, version]; + final args = ["install", lib, version]; + if (systemName == "Mac") { + args.push('OPENSSL_DIR=/usr/local/opt/openssl@3'); + } if (server != null){ - var server_arg = '--server=$server'; + final server_arg = '--server=$server'; args.push(server_arg); } runCommand("luarocks", args); @@ -46,9 +48,8 @@ class Lua { getLuaDependencies(); - for (lv in ["-l5.1", "-l5.2", "-l5.3", "-j2.0", "-j2.1" ]){ - - var envpath = Sys.getEnv("HOME") + '/lua_env$lv'; + for (lv in ["-l5.1", "-l5.2", "-l5.3"].concat(systemName == 'Linux' && Linux.arch == Arm64 ? [] : ["-j2.0", "-j2.1"])) { + final envpath = getInstallPath() + '/lua_env/lua$lv'; addToPATH(envpath + '/bin'); if (systemName == "Mac" && lv.startsWith("-j")) continue; @@ -67,9 +68,21 @@ class Lua { runCommand("luarocks", ["config", "--rock-trees"]); // Note: don't use a user config - // runCommand("luarocks", ["config", "--user-config"], false, true); + // attemptCommand("luarocks", ["config", "--user-config"]); + + installLib("luasec", "1.0.2-1"); + + installLib("lrexlib-pcre2", "2.9.1-1"); + installLib("luv", "1.36.0-0"); + installLib("luasocket", "3.0rc1-2"); + installLib("luautf8", "0.1.1-1"); + + //Install bit32 for lua 5.1 + if(lv == "-l5.1"){ + installLib("bit32", "5.2.2-1"); + } - installLib("haxe-deps", "0.0.1-6"); + installLib("hx-lua-simdjson", "0.0.1-1"); changeDirectory(unitDir); runCommand("haxe", ["compile-lua.hxml"].concat(args)); @@ -77,9 +90,9 @@ class Lua { changeDirectory(sysDir); runCommand("haxe", ["compile-lua.hxml"].concat(args)); - runCommand("lua", ["bin/lua/sys.lua"]); + runSysTest("lua", ["bin/lua/sys.lua"]); - changeDirectory(miscDir + "luaDeadCode/stringReflection"); + changeDirectory(getMiscSubDir("luaDeadCode", "stringReflection")); runCommand("haxe", ["compile.hxml"]); changeDirectory(miscLuaDir); diff --git a/tests/runci/targets/Macro.hx b/tests/runci/targets/Macro.hx index f2d5ae78..c0eab03e 100644 --- a/tests/runci/targets/Macro.hx +++ b/tests/runci/targets/Macro.hx @@ -1,6 +1,5 @@ package runci.targets; -import sys.FileSystem; import runci.System.*; import runci.Config.*; @@ -21,23 +20,37 @@ class Macro { infoMsg("Js-es6 null safety:"); runCommand("haxe", ["test-js-es6.hxml"]); - changeDirectory(miscDir); + changeDirectory(getMiscSubDir()); runCommand("haxe", ["compile.hxml"]); - changeDirectory(miscDir + "resolution"); + changeDirectory(getMiscSubDir("resolution")); runCommand("haxe", ["run.hxml"]); changeDirectory(sysDir); - runCommand("haxe", ["compile-macro.hxml"].concat(args)); + runSysTest("haxe", ["compile-macro.hxml"].concat(args)); switch Sys.systemName() { case 'Linux': - changeDirectory(miscDir + 'compiler_loops'); + changeDirectory(getMiscSubDir('compiler_loops')); runCommand("haxe", ["run.hxml"]); case _: // TODO } changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--interp"]); + + deleteDirectoryRecursively(partyDir); + runCommand("mkdir", [partyDir]); + changeDirectory(partyDir); + party(); + } + + static function party() { + runCommand("git", ["clone", "https://github.com/haxetink/tink_core", "tink_core"]); + changeDirectory("tink_core"); + runCommand("haxelib", ["newrepo"]); + runCommand("haxelib", ["install", "tests.hxml", "--always"]); + runCommand("haxelib", ["dev", "tink_core", "."]); + runCommand("haxe", ["tests.hxml", "-w", "-WDeprecated", "--interp", "--macro", "addMetadata('@:exclude','Futures','testDelay')"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Neko.hx b/tests/runci/targets/Neko.hx index 8c855105..549992db 100644 --- a/tests/runci/targets/Neko.hx +++ b/tests/runci/targets/Neko.hx @@ -1,6 +1,5 @@ package runci.targets; -import sys.FileSystem; import runci.System.*; import runci.Config.*; @@ -9,12 +8,15 @@ class Neko { runCommand("haxe", ["compile-neko.hxml", "-D", "dump", "-D", "dump_ignore_var_ids"].concat(args)); runCommand("neko", ["bin/unit.n"]); + changeDirectory(getMiscSubDir('neko')); + runCommand("haxe", ["run.hxml"].concat(args)); + changeDirectory(sysDir); runCommand("haxe", ["compile-neko.hxml"].concat(args)); - runCommand("neko", ["bin/neko/sys.n"]); + runSysTest("neko", ["bin/neko/sys.n"]); changeDirectory(threadsDir); runCommand("haxe", ["build.hxml", "--neko", "export/threads.n"]); runCommand("neko", ["export/threads.n"]); } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Php.hx b/tests/runci/targets/Php.hx index bb48b862..e09a0a4a 100644 --- a/tests/runci/targets/Php.hx +++ b/tests/runci/targets/Php.hx @@ -1,17 +1,41 @@ package runci.targets; -import sys.FileSystem; import runci.System.*; import runci.Config.*; +using haxe.io.Path; + class Php { - static var miscPhpDir(get,never):String; - static inline function get_miscPhpDir() return miscDir + 'php/'; + static final miscPhpDir = getMiscSubDir('php'); + + static final windowsPhpIni = cwd + 'PHP.ini'; + + static var windowsPhpExtPath(get, null) = null; + static function get_windowsPhpExtPath() { + if (windowsPhpExtPath != null) + return windowsPhpExtPath; + + final phpPath = commandResult("where", ["php"]).stdout; + return windowsPhpExtPath = Path.join([phpPath.directory(), "ext"]); + } + + + static function generateArgs(file:String) { + if (systemName != "Windows") + return [file]; + return [ + "-c", + windowsPhpIni, + "-d", + 'extension_dir=$windowsPhpExtPath', + file + ]; + } static public function getPhpDependencies() { - var phpCmd = commandResult("php", ["-v"]); - var phpVerReg = ~/PHP ([0-9]+\.[0-9]+)/i; - var phpVer = if (phpVerReg.match(phpCmd.stdout)) + final phpCmd = commandResult("php", ["-v"]); + final phpVerReg = ~/PHP ([0-9]+\.[0-9]+)/i; + final phpVer = if (phpVerReg.match(phpCmd.stdout)) Std.parseFloat(phpVerReg.matched(1)); else null; @@ -19,22 +43,22 @@ class Php { if (phpCmd.exitCode == 0 && phpVer != null && phpVer >= 7.0) { switch systemName { case "Linux": - var phpInfo = commandResult("php", ["-i"]); - if(phpInfo.stdout.indexOf("mbstring => enabled") < 0) { + var phpInfo = commandResult("php", ["-i"]).stdout; + if(phpInfo.indexOf("mbstring => enabled") < 0) { Linux.requireAptPackages(["php-mbstring"]); } case _: } - infoMsg('php ${phpVer} has already been installed.'); + infoMsg('php $phpVer has already been installed.'); return; } switch systemName { case "Linux": - Linux.requireAptPackages(["php-cli", "php-mbstring"]); + Linux.requireAptPackages(["php-cli", "php-mbstring", "php-sqlite3"]); case "Mac": - runCommand("brew", ["install", "php"], true); + runNetworkCommand("brew", ["install", "php"]); case "Windows": - runCommand("cinst", ["php", "-version", "7.1.8", "-y"], true); + runNetworkCommand("cinst", ["php", "-version", "7.1.8", "-y"]); case _: throw 'unknown system: $systemName'; } @@ -47,9 +71,9 @@ class Php { changeDirectory(miscPhpDir); runCommand("haxe", ["run.hxml"]); - var binDir = "bin/php"; + final binDir = "bin/php"; - var prefixes = [[]]; + final prefixes = [[]]; if(isCi()) { prefixes.push(['-D', 'php-prefix=haxe']); prefixes.push(['-D', 'php-prefix=my.pack']); @@ -57,30 +81,18 @@ class Php { for(prefix in prefixes) { changeDirectory(unitDir); - if(isCi()) { + if(isCi()) deleteDirectoryRecursively(binDir); - } runCommand("haxe", ["compile-php.hxml"].concat(prefix).concat(args)); - runThroughPhpVersions(runCommand.bind(_, [binDir + "/index.php"])); + runCommand("php", generateArgs(binDir + "/index.php")); changeDirectory(sysDir); - if(isCi()) { + if(isCi()) deleteDirectoryRecursively(binDir); - } - runCommand("haxe", ["compile-php.hxml"].concat(prefix).concat(args)); - runThroughPhpVersions(runCommand.bind(_, ["bin/php/Main/index.php"])); - } - } - static function runThroughPhpVersions(fn:(phpCmd:String)->Void) { - switch [ci, systemName] { - case [GithubActions, "Linux"]: - for(version in ['7.1', '7.2', '7.3', '7.4']) { - fn('php$version'); - } - case _: - fn('php'); + runCommand("haxe", ["compile-php.hxml"].concat(prefix).concat(args)); + runSysTest("php", generateArgs(binDir + "/Main/index.php")); } } -} \ No newline at end of file +} diff --git a/tests/runci/targets/Python.hx b/tests/runci/targets/Python.hx index 71795d2b..ea7e1141 100644 --- a/tests/runci/targets/Python.hx +++ b/tests/runci/targets/Python.hx @@ -5,9 +5,6 @@ import runci.System.*; import runci.Config.*; class Python { - static var miscPythonDir(get,never):String; - static inline function get_miscPythonDir() return miscDir + 'python/'; - static public function getPythonDependencies():Array { switch (systemName) { case "Linux": @@ -21,10 +18,13 @@ class Python { if (commandSucceed(pypy, ["-V"])) { infoMsg('pypy3 has already been installed.'); } else { - var pypyVersion = "pypy3.6-v7.3.1-linux64"; - var file = '${pypyVersion}.tar.bz2'; + final pypyVersion = "pypy3.8-v7.3.7-" + (switch Linux.arch { + case Arm64: "aarch64"; + case Amd64: "linux64"; + }); + final file = '${pypyVersion}.tar.bz2'; if(!FileSystem.exists(file)) { - runCommand("wget", ["-nv", 'https://downloads.python.org/pypy/$file'], true); + runNetworkCommand("wget", ["-nv", 'https://downloads.python.org/pypy/$file']); } runCommand("tar", ["-xf", file]); pypy = FileSystem.fullPath('${pypyVersion}/bin/pypy3'); @@ -36,13 +36,13 @@ class Python { if (commandSucceed("python3", ["-V"])) infoMsg('python3 has already been installed.'); else - runCommand("brew", ["install", "python3"], true); + runNetworkCommand("brew", ["install", "python3"]); runCommand("python3", ["-V"]); if (commandSucceed("pypy3", ["-V"])) infoMsg('pypy3 has already been installed.'); else - runCommand("brew", ["install", "pypy3"], true); + runNetworkCommand("brew", ["install", "pypy3"]); runCommand("pypy3", ["-V"]); return ["python3", "pypy3"]; @@ -59,22 +59,24 @@ class Python { } static public function run(args:Array) { - var pys = getPythonDependencies(); + final pys = getPythonDependencies(); runCommand("haxe", ["compile-python.hxml"].concat(args)); for (py in pys) { runCommand(py, ["bin/unit.py"]); + // Additional test for python-version >= 3.4 + runCommand(py, ["bin/unit34.py"]); } changeDirectory(sysDir); runCommand("haxe", ["compile-python.hxml"].concat(args)); for (py in pys) { - runCommand(py, ["bin/python/sys.py"]); + runSysTest(py, ["bin/python/sys.py"]); } - changeDirectory(miscPythonDir); + changeDirectory(getMiscSubDir("python")); runCommand("haxe", ["run.hxml"]); - changeDirectory(miscPythonDir + "pythonImport"); + changeDirectory(getMiscSubDir('python', "pythonImport")); runCommand("haxe", ["compile.hxml"]); for (py in pys) { runCommand(py, ["test.py"]); @@ -86,4 +88,4 @@ class Python { runCommand(py, ["export/threads.py"]); } } -} \ No newline at end of file +} diff --git a/tests/server/.vscode/settings.json b/tests/server/.vscode/settings.json index 41760e5e..d9765e5c 100644 --- a/tests/server/.vscode/settings.json +++ b/tests/server/.vscode/settings.json @@ -1,6 +1,6 @@ { "haxe.diagnosticsPathFilter": "${workspaceRoot}/src", - // "editor.formatOnSave": true, + "editor.formatOnSave": true, "haxeTestExplorer.testCommand": [ "${haxe}", "build.hxml", @@ -8,5 +8,6 @@ "test-adapter", "-cmd", "node test.js" - ] + ], + "haxe.enableCompilationServer": false } \ No newline at end of file diff --git a/tests/server/.vscode/tasks.json b/tests/server/.vscode/tasks.json index 534edcce..8c651cfd 100644 --- a/tests/server/.vscode/tasks.json +++ b/tests/server/.vscode/tasks.json @@ -3,7 +3,7 @@ "tasks": [ { "type": "hxml", - "file": "build.hxml", + "file": "run.hxml", "group": { "kind": "build", "isDefault": true diff --git a/tests/server/build.hxml b/tests/server/build.hxml index d5e2a60a..5f279dfa 100644 --- a/tests/server/build.hxml +++ b/tests/server/build.hxml @@ -10,4 +10,4 @@ # or set UTEST_PATTERN environment variable #Temporary. To find out what's wrong with random CI failures ---macro addMetadata('@:build(utils.macro.DebugFailures.patchAssert())', 'utest.Assert') +#--macro addMetadata('@:build(utils.macro.DebugFailures.patchAssert())', 'utest.Assert') diff --git a/tests/server/src/SkipReason.hx b/tests/server/src/SkipReason.hx new file mode 100644 index 00000000..e2e0ca33 --- /dev/null +++ b/tests/server/src/SkipReason.hx @@ -0,0 +1,8 @@ + +enum SkipReason { + DependencyDirty(path:String); + Tainted(cause:String); + FileChanged(file:String); + Shadowed(file:String); + LibraryChanged; +} \ No newline at end of file diff --git a/tests/server/src/TestCase.hx b/tests/server/src/TestCase.hx index 4e4ea47e..c9b68996 100644 --- a/tests/server/src/TestCase.hx +++ b/tests/server/src/TestCase.hx @@ -1,3 +1,4 @@ +import SkipReason; import haxe.PosInfos; import haxe.Exception; import haxe.display.Position; @@ -17,7 +18,12 @@ using Lambda; @:autoBuild(utils.macro.BuildHub.build()) class TestCase implements ITest { - static public var debugLastResult:{hasError:Bool, stdout:String, stderr:String, prints:Array}; + static public var debugLastResult:{ + hasError:Bool, + stdout:String, + stderr:String, + prints:Array + }; var server:HaxeServerAsync; var vfs:Vfs; @@ -30,17 +36,27 @@ class TestCase implements ITest { public function new() {} - public function setup() { + static public function printSkipReason(ddr:SkipReason) { + return switch (ddr) { + case DependencyDirty(path): 'DependencyDirty $path'; + case Tainted(cause): 'Tainted $cause'; + case FileChanged(file): 'FileChanged $file'; + case Shadowed(file): 'Shadowed $file'; + case LibraryChanged: 'LibraryChanged'; + } + } + + public function setup(async:utest.Async) { testDir = "test/cases/" + i++; vfs = new Vfs(testDir); - server = new HaxeServerAsync(() -> new HaxeServerProcessNode("haxe", ["-v", "--cwd", testDir])); + server = new HaxeServerAsync(() -> new HaxeServerProcessNode("haxe", ["-v", "--cwd", testDir], {}, () -> async.done())); } public function teardown() { server.stop(); } - function runHaxe(args:Array, done:()->Void) { + function runHaxe(args:Array, done:() -> Void) { messages = []; errorMessages = []; server.rawRequest(args, null, function(result) { @@ -63,12 +79,25 @@ class TestCase implements ITest { }, sendErrorMessage); } - function runHaxeJson(args:Array, method:HaxeRequestMethod, methodArgs:TParams, done:()->Void) { + function runHaxeJson(args:Array, method:HaxeRequestMethod, methodArgs:TParams, done:() -> Void) { var methodArgs = {method: method, id: 1, params: methodArgs}; args = args.concat(['--display', Json.stringify(methodArgs)]); runHaxe(args, done); } + function runHaxeJsonCb(args:Array, method:HaxeRequestMethod>, methodArgs:TParams, + callback:TResponse->Void, done:() -> Void) { + var methodArgs = {method: method, id: 1, params: methodArgs}; + args = args.concat(['--display', Json.stringify(methodArgs)]); + server.rawRequest(args, null, function(result) { + callback(Json.parse(result.stderr).result.result); + done(); + }, function(msg) { + sendErrorMessage(msg); + done(); + }); + } + function sendErrorMessage(msg:String) { var split = msg.split("\n"); for (message in split) { @@ -150,35 +179,32 @@ class TestCase implements ITest { } function assertSuccess(?p:haxe.PosInfos) { - Assert.isTrue(0 == errorMessages.length, p); + return Assert.isTrue(0 == errorMessages.length, p); } function assertErrorMessage(message:String, ?p:haxe.PosInfos) { - Assert.isTrue(hasErrorMessage(message), p); + return Assert.isTrue(hasErrorMessage(message), p); } function assertHasPrint(line:String, ?p:haxe.PosInfos) { - Assert.isTrue(hasMessage("Haxe print: " + line), null, p); + return Assert.isTrue(hasMessage("Haxe print: " + line), null, p); } function assertReuse(module:String, ?p:haxe.PosInfos) { - Assert.isTrue(hasMessage('reusing $module'), null, p); + return Assert.isTrue(hasMessage('reusing $module'), null, p); } - function assertSkipping(module:String, ?dependency:String, ?p:haxe.PosInfos) { - var msg = 'skipping $module'; - if (dependency != null) { - msg += '($dependency)'; - } - Assert.isTrue(hasMessage(msg), null, p); + function assertSkipping(module:String, reason:SkipReason, ?p:haxe.PosInfos) { + var msg = 'skipping $module (${printSkipReason(reason))})'; + return Assert.isTrue(hasMessage(msg), null, p); } function assertNotCacheModified(module:String, ?p:haxe.PosInfos) { - Assert.isTrue(hasMessage('$module not cached (modified)'), null, p); + return Assert.isTrue(hasMessage('$module not cached (modified)'), null, p); } function assertHasType(typePackage:String, typeName:String, ?p:haxe.PosInfos) { - Assert.isTrue(getStoredType(typePackage, typeName) != null, null, p); + return Assert.isTrue(getStoredType(typePackage, typeName) != null, null, p); } function assertHasField(typePackage:String, typeName:String, fieldName:String, isStatic:Bool, ?p:haxe.PosInfos) { @@ -196,10 +222,10 @@ class TestCase implements ITest { } } - function assertClassField(completion:CompletionResult, name:String, ?callback:(field:JsonClassField)->Void, ?pos:PosInfos) { + function assertClassField(completion:CompletionResult, name:String, ?callback:(field:JsonClassField) -> Void, ?pos:PosInfos) { for (item in completion.result.items) { switch item.kind { - case ClassField if(item.args.field.name == name): + case ClassField if (item.args.field.name == name): switch callback { case null: Assert.pass(pos); case fn: fn(item.args.field); diff --git a/tests/server/src/cases/RetyperTests.hx b/tests/server/src/cases/RetyperTests.hx new file mode 100644 index 00000000..8a4db7a7 --- /dev/null +++ b/tests/server/src/cases/RetyperTests.hx @@ -0,0 +1,203 @@ +package cases; + +import haxe.display.FsPath; +import haxe.display.Server; +import utest.Assert; + +using StringTools; +using Lambda; + +class RetyperTests extends TestCase { + static function getBaseArgs(moduleName:String) { + return [ + moduleName + ".hx", + "--no-output", + "-js", + "no.js", + "--macro", + "haxe.macro.CompilationServer.setModuleCheckPolicy(['" + moduleName + "'], [Retype], false)" + ]; + } + + function testNonSignature() { + vfs.putContent("WithDependency.hx", getTemplate("WithDependency.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('retyped WithDependency')); + } + + function testSignature() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependency.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureInferredArg() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyInferredArg.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureInferredRet() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyInferredRet.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureVariable() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyVariable.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureInferredVariable() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyInferredVariable.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureProperty() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyProperty.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testSignatureInferredProperty() { + vfs.putContent("WithSignatureDependency.hx", getTemplate("retyper/WithSignatureDependencyInferredProperty.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithSignatureDependency')); + Assert.isTrue(hasMessage('[Module WithSignatureDependency] [Class WithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } + + function testMutual() { + vfs.putContent("WithMutualDependency.hx", getTemplate("retyper/WithMutualDependency.hx")); + vfs.putContent("MutualDependency.hx", getTemplate("retyper/MutualDependency.hx")); + var args = getBaseArgs("WithMutualDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("MutualDependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('retyped WithMutualDependency')); + } + + function testParent() { + vfs.putContent("WithParentDependency.hx", getTemplate("retyper/WithParentDependency.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("WithParentDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithParentDependency')); + Assert.isTrue(hasMessage('[Module WithParentDependency] [Class WithParentDependency] [Relations]: Could not load [Module Dependency]')); + } + + function testInterface() { + vfs.putContent("WithInterfaceDependency.hx", getTemplate("retyper/WithInterfaceDependency.hx")); + vfs.putContent("InterfaceDependency.hx", getTemplate("retyper/InterfaceDependency.hx")); + var args = getBaseArgs("WithInterfaceDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("InterfaceDependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping WithInterfaceDependency')); + Assert.isTrue(hasMessage('[Module WithInterfaceDependency] [Class WithInterfaceDependency] [Relations]: Could not load [Module InterfaceDependency]')); + } + + function testIndependentEnum() { + vfs.putContent("IndependentEnum.hx", getTemplate("retyper/IndependentEnum.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("IndependentEnum"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('retyped IndependentEnum')); + } + + function testDependentEnum() { + vfs.putContent("DependentEnum.hx", getTemplate("retyper/DependentEnum.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("DependentEnum"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping DependentEnum')); + Assert.isTrue(hasMessage('[Module DependentEnum] [Enum DependentEnum] [Field Constructor]: Could not load [Module Dependency]')); + } + + function testIndependentTypedef() { + vfs.putContent("IndependentTypedef.hx", getTemplate("retyper/IndependentTypedef.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("IndependentTypedef"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('retyped IndependentTypedef')); + } + + function testDependentTypedef() { + vfs.putContent("DependentTypedef.hx", getTemplate("retyper/DependentTypedef.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("DependentTypedef"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping DependentTypedef')); + Assert.isTrue(hasMessage('[Module DependentTypedef] [Typedef DependentTypedef]: Could not load [Module Dependency]')); + } + + function testAbstractNonSignature() { + vfs.putContent("AbstractWithDependency.hx", getTemplate("retyper/AbstractWithDependency.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("AbstractWithDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('retyped AbstractWithDependency')); + } + + function testAbstractSignature() { + vfs.putContent("AbstractWithSignatureDependency.hx", getTemplate("retyper/AbstractWithSignatureDependency.hx")); + vfs.putContent("Dependency.hx", getTemplate("Dependency.hx")); + var args = getBaseArgs("AbstractWithSignatureDependency"); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); + runHaxe(args); + Assert.isTrue(hasMessage('failed retyping AbstractWithSignatureDependency')); + Assert.isTrue(hasMessage('[Module AbstractWithSignatureDependency] [Abstract AbstractWithSignatureDependency] [Field test]: Could not load [Module Dependency]')); + } +} diff --git a/tests/server/src/cases/ServerTests.hx b/tests/server/src/cases/ServerTests.hx index 7c9468e1..b9987e32 100644 --- a/tests/server/src/cases/ServerTests.hx +++ b/tests/server/src/cases/ServerTests.hx @@ -25,7 +25,7 @@ class ServerTests extends TestCase { runHaxe(args); runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("HelloWorld.hx")}); runHaxe(args); - assertSkipping("HelloWorld"); + assertSkipping("HelloWorld", Tainted("server/invalidate")); // assertNotCacheModified("HelloWorld"); } @@ -36,7 +36,7 @@ class ServerTests extends TestCase { runHaxe(args); runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Dependency.hx")}); runHaxe(args); - assertSkipping("WithDependency", "Dependency"); + assertSkipping("WithDependency", DependencyDirty("Dependency - Tainted server/invalidate")); // assertNotCacheModified("Dependency"); runHaxe(args); assertReuse("Dependency"); @@ -78,12 +78,16 @@ class ServerTests extends TestCase { var args = ["-main", "BuiltClass.hx", "--interp"]; runHaxe(args); runHaxe(args); - assertReuse("BuiltClass"); + /* This often fails on our CI because the reported stdout is empty. I don't know why this is the case, + but it's probably some obscure timing issue related to pipes which has nothing to do with that we + actually want to test here. */ + // trace(lastResult); + // assertReuse("BuiltClass"); runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("BuildMacro.hx")}); runHaxe(args); // assertNotCacheModified("BuildMacro"); - assertSkipping("BuiltClass", "BuildMacro"); - assertSkipping("BuildMacro"); + assertSkipping("BuiltClass", DependencyDirty("BuildMacro - Tainted server/invalidate")); + assertSkipping("BuildMacro", Tainted("server/invalidate")); } function testBrokenSyntaxDiagnostics() { @@ -105,7 +109,6 @@ class ServerTests extends TestCase { assertSuccess(); } - #if false // @see https://github.com/HaxeFoundation/haxe/issues/8596#issuecomment-518815594 function testDisplayModuleRecache() { vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); var args = ["--main", "HelloWorld", "--interp"]; @@ -124,11 +127,9 @@ class ServerTests extends TestCase { runHaxe(args2); runHaxe(args); - assertSkipping("HelloWorld"); + assertSkipping("HelloWorld", Tainted("check_display_file")); } - #end - #if false // @see https://github.com/HaxeFoundation/haxe/issues/8596#issuecomment-518815594 function testMutuallyDependent() { vfs.putContent("MutuallyDependent1.hx", getTemplate("MutuallyDependent1.hx")); vfs.putContent("MutuallyDependent2.hx", getTemplate("MutuallyDependent2.hx")); @@ -140,7 +141,40 @@ class ServerTests extends TestCase { runHaxe(args); assertSuccess(); } - #end + + function testDiagnosticsRecache() { + vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); + var args = ["--main", "HelloWorld", "--interp"]; + runHaxe(args); + runHaxe(args); + assertReuse("HelloWorld"); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("HelloWorld.hx")}); + runHaxe(args); + assertSkipping("HelloWorld", Tainted("server/invalidate")); + runHaxe(args.concat(["--display", "HelloWorld.hx@0@diagnostics"])); + runHaxe(args); + assertReuse("HelloWorld"); + } + + function testDiagnosticsRecache2() { + vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); + var args = ["--main", "HelloWorld", "--interp"]; + runHaxe(args.concat(["--display", "HelloWorld.hx@0@diagnostics"])); + runHaxe(args); + assertReuse("HelloWorld"); + } + + function testDiagnosticsRecache3() { + vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); + var args = ["--main", "HelloWorld", "--interp"]; + runHaxe(args); + runHaxe(args); + assertReuse("HelloWorld"); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("HelloWorld.hx")}); + runHaxe(args.concat(["--display", "HelloWorld.hx@0@diagnostics"])); + runHaxe(args.concat(["--display", "HelloWorld.hx@0@hover"])); + assertReuse("HelloWorld"); + } function testSyntaxCache() { vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); @@ -201,6 +235,17 @@ class ServerTests extends TestCase { utest.Assert.equals("function() {_Vector.Vector_Impl_.toIntVector(null);}", moreHack(type.args.statics[0].expr.testHack)); // lmao } + function test10986() { + vfs.putContent("Main.hx", getTemplate("issues/Issue10986/Main.hx")); + vfs.putContent("haxe/ds/Vector.hx", getTemplate("issues/Issue10986/Vector.hx")); + var args = ["-main", "Main", "--jvm", "Main.jar"]; + runHaxe(args); + vfs.touchFile("haxe/ds/Vector.hx"); + runHaxe(args); + assertSuccess(); + + } + // See https://github.com/HaxeFoundation/haxe/issues/8368#issuecomment-525379060 #if false function testXRedefinedFromX() { @@ -215,6 +260,50 @@ class ServerTests extends TestCase { } #end + function testXRedefinedFromX_2() { + vfs.putContent("Main.hx", getTemplate("issues/Issue8368/Main2.hx")); + var transform = Marker.extractMarkers(getTemplate("issues/Issue8368/MyMacro2.macro.hx")); + var args = ["-main", "Main", "--macro", "define('whatever')"]; + + vfs.putContent( + "MyMacro.macro.hx", + transform.source.substr(0, transform.markers[1]) + + transform.source.substr(transform.markers[2], transform.source.length) + ); + + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("MyMacro.macro.hx")}); + + var completionRequest = {file: new FsPath("MyMacro.macro.hx"), contents: transform.source, offset: transform.markers[2], wasAutoTriggered: false}; + runHaxeJson(args, DisplayMethods.Completion, completionRequest); + Assert.isTrue(parseCompletion().result.items.length == 23); + runHaxeJson(args, DisplayMethods.Completion, completionRequest); + Assert.isTrue(parseCompletion().result.items.length == 23); + runHaxeJson(args, DisplayMethods.Completion, completionRequest); + Assert.isTrue(parseCompletion().result.items.length == 23); + + runHaxe(args); + assertSuccess(); + } + + @:async function testStackOverflow(async:utest.Async) { + vfs.putContent("Empty.hx", getTemplate("Empty.hx")); + var args = ["-main", "Empty.hx", "--macro", "allowPackage('sys')", "--interp", "--no-output"]; + var runs = 0; + + function runLoop() { + runHaxe(args.concat(["--display", "Empty.hx@0@diagnostics"]), () -> { + runHaxe(args.concat(["-D", "compile-only-define"]), () -> { + if (assertSuccess() && ++runs < 20) runLoop(); + else async.done(); + }); + }); + } + + async.setTimeout(20000); + runLoop(); + } + function testMacroStaticsReset() { vfs.putContent("Main.hx", getTemplate("issues/Issue8631/Main.hx")); vfs.putContent("Init.hx", getTemplate("issues/Issue8631/Init.hx")); @@ -226,16 +315,17 @@ class ServerTests extends TestCase { utest.Assert.equals('2', counter); } - // function testIssue8616() { - // vfs.putContent("Main.hx", getTemplate("issues/Issue8616/Main.hx")); - // vfs.putContent("A.hx", getTemplate("issues/Issue8616/A.hx")); - // var args = ["-main", "Main", "-js", "out.js"]; - // runHaxe(args); - // runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); - // runHaxe(args); - // var content = sys.io.File.getContent(haxe.io.Path.join([testDir, "out.js"])); - // Assert.isTrue(content.indexOf("this1.use(v1)") != -1); - // } + function testIssue8616() { + vfs.putContent("Main.hx", getTemplate("issues/Issue8616/Main.hx")); + vfs.putContent("A.hx", getTemplate("issues/Issue8616/A.hx")); + var args = ["-main", "Main", "-js", "out.js"]; + runHaxe(args); + var originalContent = sys.io.File.getContent(haxe.io.Path.join([testDir, "out.js"])); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args); + var content = sys.io.File.getContent(haxe.io.Path.join([testDir, "out.js"])); + Assert.isTrue(content == originalContent); + } function test9918() { vfs.putContent("Issue9918.hx", getTemplate("Issue9918.hx")); diff --git a/tests/server/src/cases/display/issues/Issue10414.hx b/tests/server/src/cases/display/issues/Issue10414.hx index 41a6256e..ae6a7c97 100644 --- a/tests/server/src/cases/display/issues/Issue10414.hx +++ b/tests/server/src/cases/display/issues/Issue10414.hx @@ -21,6 +21,142 @@ class Issue10414 extends DisplayTestCase { offset: offset(1), wasAutoTriggered: true }); - assertHasCompletion(parseCompletion(), item -> item.args.name == 'a'); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'a'); + Assert.equals("", completion.result.filterString); + } + + /** + class Main { + static function main() { + var abc = 1; + var obj:{ + foo:Int, + bar:Bool + } = { + foo:{-1-}a{-2-}b{-3-} + bar: true + } + } + } + **/ + function test2(_) { + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(1), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'abc'); + Assert.equals("ab", completion.result.filterString); + + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(2), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'abc'); + Assert.equals("a", completion.result.filterString); + + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(3), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'abc'); + Assert.equals("ab", completion.result.filterString); + } + + /** + class Container { + public function new() {}; + } + class Main { + static function main() { + final rockC = addContainer(); + addSprite({ + container: {-1-} + frame: "" + }); + } + static function addContainer():Container return new Container(); + static function addSprite(obj:{ + container:Container, + frame:String + }):Void {} + } + **/ + function test3(_) { + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(1), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'rockC'); + Assert.equals("", completion.result.filterString); + } + + /** + class Container { + public function new() {}; + } + class Main { + static function main() { + final rockC = addContainer(); + addSprite({ + container: {-1-}, + frame: "" + }); + } + static function addContainer():Container return new Container(); + static function addSprite(obj:{ + container:Container, + frame:String + }):Void {} + } + **/ + function test4(_) { + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(1), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'rockC'); + Assert.equals("", completion.result.filterString); + } + + /** + class Container { + public function new() {}; + } + class Main { + static function main() { + final rockC = addContainer(); + addSprite({ + container: rockC, + container: {-1-} + frame: "" + }); + } + static function addContainer():Container return new Container(); + static function addSprite(obj:{ + container:Container, + frame:String + }):Void {} + } + **/ + function test5(_) { + runHaxeJson([], DisplayMethods.Completion, { + file: file, + offset: offset(1), + wasAutoTriggered: true + }); + final completion = parseCompletion(); + assertHasCompletion(completion, item -> item.args.name == 'rockC'); + Assert.equals("", completion.result.filterString); } } \ No newline at end of file diff --git a/tests/server/src/cases/display/issues/Issue10635.hx b/tests/server/src/cases/display/issues/Issue10635.hx new file mode 100644 index 00000000..1cd612e8 --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue10635.hx @@ -0,0 +1,83 @@ +package cases.display.issues; + +import haxe.display.JsonModuleTypes; +import haxe.Json; + +class Issue10635 extends DisplayTestCase { + /** + class C { + public function new() {} + } + + function main() { + Something.append([new C()], [new C()]); + } + **/ + function test(_) { + var args = ["-main", "Main", "--display", "Main.hx@0@diagnostics"]; + vfs.putContent("Something.hx", getTemplate("issues/Issue10635/Something.hx")); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args); + Assert.isTrue(lastResult.stderr.length == 2); // dumb, but we don't have a proper diagnostics structure in these tests + } + + /** + class C { + public function new() {} + } + + function main() { + Something.append([new C()], [new C()]); + } + **/ + function testGenericClassPerMethod(_) { + var args = ["-main", "Main", "--display", "Main.hx@0@diagnostics"]; + vfs.putContent("Something.hx", "@:genericClassPerMethod " + getTemplate("issues/Issue10635/Something.hx")); + runHaxe(args); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args); + Assert.isTrue(lastResult.stderr.length == 2); // dumb, but we don't have a proper diagnostics structure in these tests + } + + function testGenericAddition(_) { + var args = ["-main", "Main"]; + vfs.putContent("GenericMethod.hx", getTemplate("GenericMethod.hx")); + vfs.putContent("Main.hx", getTemplate("issues/Issue10635/MainBefore.hx")); + runHaxe(args); + vfs.putContent("Main.hx", getTemplate("issues/Issue10635/MainAfter.hx")); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + // Note: We only have to run this once to reproduce because ServerMethods.Type will call cl_restore anyway + runHaxe(args); + runHaxeJson(args, ServerMethods.Contexts, null); + var contexts:Array = Json.parse(lastResult.stderr).result.result; + utest.Assert.equals(1, contexts.length); + var sig = contexts[0].signature; + runHaxeJson(args, ServerMethods.Type, {signature: sig, modulePath: "GenericMethod", typeName: "GenericMethod"}); + var type:JsonModuleType = Json.parse(lastResult.stderr).result.result; + var statics = type.args.statics; + Assert.isTrue(statics.exists(cf -> cf.name == "f")); + Assert.isTrue(statics.exists(cf -> cf.name == "f_Class

")); + } + + function testGenericInstanceAddition(_) { + var args = ["-main", "Main"]; + vfs.putContent("GenericInstanceMethod.hx", getTemplate("GenericInstanceMethod.hx")); + vfs.putContent("Main.hx", getTemplate("issues/Issue10635/MainInstanceBefore.hx")); + runHaxe(args); + vfs.putContent("Main.hx", getTemplate("issues/Issue10635/MainInstanceAfter.hx")); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + // Note: We only have to run this once to reproduce because ServerMethods.Type will call cl_restore anyway + runHaxe(args); + var contexts = null; + runHaxeJsonCb(args, ServerMethods.Contexts, null, r -> contexts = r); + Assert.notNull(contexts); + utest.Assert.equals(1, contexts.length); + var sig = contexts[0].signature; + var type:JsonModuleType = null; + runHaxeJsonCb(args, ServerMethods.Type, {signature: sig, modulePath: "GenericInstanceMethod", typeName: "GenericInstanceMethod"}, r -> type = r); + var fields = type.args.fields; + Assert.isTrue(fields.exists(cf -> cf.name == "f")); + Assert.isTrue(fields.exists(cf -> cf.name == "f_Class
")); + } +} diff --git a/tests/server/src/cases/display/issues/Issue10638.hx b/tests/server/src/cases/display/issues/Issue10638.hx new file mode 100644 index 00000000..9a28bcb1 --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue10638.hx @@ -0,0 +1,21 @@ +package cases.display.issues; + +class Issue10638 extends DisplayTestCase { + /** + var x = {-1-}"f{-2-}oo"{-3-}; + + class Main { + static function main() {} + } + **/ + function test(_) { + runHaxe(["--main", "Main"]); + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(2) + }); + var result = parseHover(); + Assert.same(range(1, 3), result.result.range); + } + +} \ No newline at end of file diff --git a/tests/server/src/cases/display/issues/Issue10678.hx b/tests/server/src/cases/display/issues/Issue10678.hx new file mode 100644 index 00000000..20ed82bd --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue10678.hx @@ -0,0 +1,17 @@ +package cases.display.issues; + +import haxe.Json; + +class Issue10678 extends DisplayTestCase { + function test(_) { + vfs.putContent("Macro.hx", getTemplate("issues/Issue10678/Macro.hx")); + vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); + var args = ["--main HelloWorld", "--js", "js.js", "--macro", "Macro.init()"]; + // This is very facepalm + runHaxeJson(args, DisplayMethods.FindReferences, {file: new FsPath("HelloWorld.hx"), offset: 0}); + var results:Array = Json.parse(lastResult.stderr).result.result; + for (i => result in results) { + Assert.notContains(result, results.slice(i + 1)); + } + } +} diff --git a/tests/server/src/cases/display/issues/Issue10781.hx b/tests/server/src/cases/display/issues/Issue10781.hx new file mode 100644 index 00000000..ac34c97f --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue10781.hx @@ -0,0 +1,48 @@ +package cases.display.issues; + +class Issue10781 extends DisplayTestCase { + /** + interface Canvas { + var g2(get, null):Graphics; + } + + class Graphics { + public function begin() {} + } + + class Main { + static function main() { + onRender(null); + } + + static function onRender(canvas:Canvas) { + final {-1-}g = canvas.g2; + g.begin(); + } + } + **/ + function test(_) { + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(1) + }); + runHaxeJson([], ServerMethods.Invalidate, {file: file}); + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(1) + }); + var result = parseHover(); + Assert.same({ + kind: "TInst", + args: { + path: { + moduleName: "Main", + importStatus: 0, + pack: [], + typeName: "Graphics" + }, + params: [] + } + }, result.result.item.type); + } +} diff --git a/tests/server/src/cases/display/issues/Issue9140.hx b/tests/server/src/cases/display/issues/Issue9140.hx new file mode 100644 index 00000000..ff69011d --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue9140.hx @@ -0,0 +1,61 @@ +package cases.display.issues; + +class Issue9140 extends DisplayTestCase { + /** + class Main { + static function main() { + var c = new C(); + var f {-1-}= c.field{-2-}; + {-3-} c.field{-4-} = f; + } + } + + class C implements I { + public var field(g{-5-}et,s{-6-}et):Int; + public function new() {} + + function get{-7-}_field() return 10; + function set_f{-8-}ield(value:Int) return value; + } + + interface I { + var field(get,set):Int; + } + **/ + function test(_) { + // TODO: The starting positions are off by 4. This comes from patch_string_pos in + // statistics.ml subtracting the length of get_/set_, while the syntax has the plain + // field. This is not a new issue though. + runHaxeJson([], DisplayMethods.FindReferences, { + file: file, + kind: WithBaseAndDescendants, + offset: offset(5) + }); + var result = parseGotoDefinitionLocations(); + Assert.same([range(1, 2)], result.map(l -> l.range)); + + runHaxeJson([], DisplayMethods.FindReferences, { + file: file, + kind: WithBaseAndDescendants, + offset: offset(7) + }); + var result = parseGotoDefinitionLocations(); + Assert.same([range(1, 2)], result.map(l -> l.range)); + + runHaxeJson([], DisplayMethods.FindReferences, { + file: file, + kind: WithBaseAndDescendants, + offset: offset(6) + }); + var result = parseGotoDefinitionLocations(); + Assert.same([range(3, 4)], result.map(l -> l.range)); + + runHaxeJson([], DisplayMethods.FindReferences, { + file: file, + kind: WithBaseAndDescendants, + offset: offset(8) + }); + var result = parseGotoDefinitionLocations(); + Assert.same([range(3, 4)], result.map(l -> l.range)); + } +} diff --git a/tests/server/src/cases/display/issues/Issue9318.hx b/tests/server/src/cases/display/issues/Issue9318.hx new file mode 100644 index 00000000..56ac14e5 --- /dev/null +++ b/tests/server/src/cases/display/issues/Issue9318.hx @@ -0,0 +1,39 @@ +package cases.display.issues; + +class Issue9318 extends DisplayTestCase { + /** + typedef T = { + var {-1-}field{-2-}:Int; + } + + @:structInit + class C { + public function new({-3-}field{-4-}:Int) {} + } + + @:structInit + class D { + var {-5-}field{-6-}:Int; + } + + class Main { + static function main() { + var t:T = {fie{-7-}ld: 1}; // clicking on `field` takes to the field position: ideal + var c:C = {fie{-8-}ld: 1}; // clicking on `field` takes to the contstructor position: not bad, but ideally should take to the argument position (not sure if possible) + var d:D = {fiel{-9-}d: 1}; // clicking on `field` doesn't work :() + } + } + **/ + function test(_) { + var args = ["Main", "-js", "main.js"]; + function parseGotoDefintion():GotoDefinitionResult { + return haxe.Json.parse(lastResult.stderr).result.result.map(result -> result.range); + } + runHaxeJson(args, DisplayMethods.GotoDefinition, {file: file, offset: offset(7), contents: source}); + Assert.same([range(1, 2)], parseGotoDefintion()); + // runHaxeJson(args, DisplayMethods.GotoDefinition, {file: file, offset: offset(8), contents: source}); + // Assert.same([range(3, 4)], parseGotoDefintion()); + runHaxeJson(args, DisplayMethods.GotoDefinition, {file: file, offset: offset(9), contents: source}); + Assert.same([range(5, 6)], parseGotoDefintion()); + } +} diff --git a/tests/server/src/cases/display/issues/Issue9423.hx b/tests/server/src/cases/display/issues/Issue9423.hx index 2041ccea..a7deb94d 100644 --- a/tests/server/src/cases/display/issues/Issue9423.hx +++ b/tests/server/src/cases/display/issues/Issue9423.hx @@ -1,6 +1,7 @@ package cases.display.issues; import haxe.display.Protocol; +using Lambda; class Issue9423 extends DisplayTestCase { /** @@ -13,15 +14,16 @@ class Issue9423 extends DisplayTestCase { function test(_) { vfs.putContent("Mod9423.hx", getTemplate("issues/Issue9423/Mod9423.hx")); vfs.putContent("Mod9423.whatever.hx", getTemplate("issues/Issue9423/Mod9423.whatever.hx")); - runHaxeJson([], DisplayMethods.Completion, { + runHaxeJson(["-cp", "."], DisplayMethods.Completion, { file: file, offset: offset(1), wasAutoTriggered: false }); var result = parseCompletion().result; - // TODO: this test does not pass, but the same setup works fine in vscode - // TODO: Another test which does not work here, but works in vshaxe: #9423. - // Assert.equals(1, result.items.length); - Assert.pass(); + final l = result.items.filter(di -> switch [di.kind, di.args] { + case [Type, args]: args.path.typeName == "Mod9423"; + case _: false; + }); + Assert.equals(1, l.length); } } diff --git a/tests/server/src/cases/issues/Issue10646.hx b/tests/server/src/cases/issues/Issue10646.hx new file mode 100644 index 00000000..7882f646 --- /dev/null +++ b/tests/server/src/cases/issues/Issue10646.hx @@ -0,0 +1,21 @@ +package cases.issues; + +class Issue10646 extends TestCase { + function test(_) { + vfs.putContent("HelloWorld.hx", getTemplate("HelloWorld.hx")); + var args = ["-main", "HelloWorld", "--neko", "test.n"]; + runHaxe(args); + var nekoCtx = null; + runHaxeJsonCb([], ServerMethods.Contexts, null, function(ctxs) { + for (ctx in ctxs) { + if (ctx.desc == "after_init_macros") { + nekoCtx = ctx; + } + } + }); + Assert.notNull(nekoCtx); + runHaxeJsonCb([], ServerMethods.ContextMemory, {signature: nekoCtx.signature}, function(mem) { + Assert.isNull(mem.leaks); + }); + } +} diff --git a/tests/server/src/cases/issues/Issue10653.hx b/tests/server/src/cases/issues/Issue10653.hx new file mode 100644 index 00000000..e855ac0e --- /dev/null +++ b/tests/server/src/cases/issues/Issue10653.hx @@ -0,0 +1,16 @@ +package cases.issues; + +class Issue10653 extends TestCase { + function test(_) { + vfs.putContent("Test.hx", getTemplate("issues/Issue10653/Test.hx")); + vfs.putContent("Main.hx", getTemplate("issues/Issue10653/MainBefore.hx")); + var args = ["-main", "Main", "--js", "no.js", "--no-output"]; + runHaxe(args); + vfs.putContent("Main.hx", getTemplate("issues/Issue10653/MainAfter.hx")); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args.concat(["--display", "Main.hx@0@diagnostics"])); + runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args.concat(["--display", "Main.hx@0@diagnostics"])); + Assert.isTrue(lastResult.stderr.length == 2); + } +} diff --git a/tests/server/src/cases/issues/Issue10677.hx b/tests/server/src/cases/issues/Issue10677.hx new file mode 100644 index 00000000..48d9c3a1 --- /dev/null +++ b/tests/server/src/cases/issues/Issue10677.hx @@ -0,0 +1,15 @@ +package cases.issues; + +class Issue10677 extends TestCase { + function test(_) { + vfs.putContent("Main.hx", getTemplate("issues/Issue10677/Main.hx")); + vfs.putContent("Polygon.hx", getTemplate("issues/Issue10677/Polygon.hx")); + vfs.putContent("Polygons.hx", getTemplate("issues/Issue10677/Polygons.hx")); + vfs.putContent("PolygonCollider.hx", getTemplate("issues/Issue10677/PolygonCollider.hx")); + var args = ["--main", "Main.hx", "--no-output"]; + runHaxe(args); + runHaxeJson(args, ServerMethods.Invalidate, {file: new FsPath("Main.hx")}); + runHaxe(args); + Assert.isFalse(lastResult.hasError); + } +} diff --git a/tests/server/src/cases/issues/Issue8748.hx b/tests/server/src/cases/issues/Issue8748.hx index 900dff72..82d30d24 100644 --- a/tests/server/src/cases/issues/Issue8748.hx +++ b/tests/server/src/cases/issues/Issue8748.hx @@ -19,7 +19,7 @@ class Issue8748 extends TestCase { runHaxeJson([], ServerMethods.Invalidate, {file: new FsPath("res/dep.dep")}); runHaxeJson(args, DisplayMethods.Hover, {file: new FsPath("WithDependency.hx"), offset: 65}); // check messages manually because module file contains awkward absolute path - var r = ~/skipping Dependency\(.*dep.dep\)/; - Assert.isTrue(messages.exists(message -> r.match(message))); + // var r = ~/skipping Dependency \(.*dep.dep\)/; + // Assert.isTrue(messages.exists(message -> r.match(message))); } -} \ No newline at end of file +} diff --git a/tests/server/src/cases/issues/Issue9636.hx b/tests/server/src/cases/issues/Issue9636.hx new file mode 100644 index 00000000..da24e660 --- /dev/null +++ b/tests/server/src/cases/issues/Issue9636.hx @@ -0,0 +1,15 @@ +package cases.issues; + +class Issue9636 extends TestCase { + function test(_) { + vfs.putContent('Main.hx', getTemplate('issues/Issue9636/Main.hx')); + var args = ['--main', 'Main', '--macro', 'include("pack")', '--js', 'test.js']; + runHaxe(args); + assertSuccess(); + vfs.putContent('pack/Foo.hx', getTemplate('issues/Issue9636/Foo.hx')); + runHaxe(args); + assertSuccess(); + runHaxe(['--cmd', 'node test.js']); + Assert.isTrue(lastResult.stderr.contains('Exception from Foo')); + } +} diff --git a/tests/server/src/utils/macro/TestBuilder.macro.hx b/tests/server/src/utils/macro/TestBuilder.macro.hx index affc4563..1cfab11d 100644 --- a/tests/server/src/utils/macro/TestBuilder.macro.hx +++ b/tests/server/src/utils/macro/TestBuilder.macro.hx @@ -12,6 +12,9 @@ class TestBuilder { continue; } switch (field.kind) { + case FFun(f) if (field.meta.exists(m -> m.name == ":async")): + // Async is already manually handled, nothing to do + case FFun(f): var asyncName = switch f.args { case []: @@ -22,7 +25,7 @@ class TestBuilder { }); name; case [arg]: - if(arg.name == "_") { + if (arg.name == "_") { arg.name = "async"; arg.type = macro:utest.Async; } @@ -63,6 +66,10 @@ class TestBuilder { var e = transformHaxeCalls(asyncName, el); args.push(macro() -> ${failOnException(asyncName, e)}); macro runHaxeJson($a{args}); + case macro runHaxeJsonCb($a{args}): + var e = transformHaxeCalls(asyncName, el); + args.push(macro() -> ${failOnException(asyncName, e)}); + macro runHaxeJsonCb($a{args}); case macro complete($a{args}): var e = transformHaxeCalls(asyncName, el); args.push(macro function(response, markers) ${failOnException(asyncName, e)}); @@ -76,12 +83,13 @@ class TestBuilder { } static function failOnException(asyncName:String, e:Expr):Expr { - return macro @:pos(e.pos) try { - $e; - } catch(e) { - Assert.fail(e.details()); - $i{asyncName}.done(); - return; - } + return macro + @:pos(e.pos) try { + $e; + } catch (e) { + Assert.fail(e.details()); + $i{asyncName}.done(); + return; + } } } diff --git a/tests/server/test/templates/Dependency.hx b/tests/server/test/templates/Dependency.hx index 9429955f..f86f9215 100644 --- a/tests/server/test/templates/Dependency.hx +++ b/tests/server/test/templates/Dependency.hx @@ -2,4 +2,6 @@ class Dependency { static public function get() { return "Hello World"; } + + public function new() {} } diff --git a/tests/server/test/templates/GenericInstanceMethod.hx b/tests/server/test/templates/GenericInstanceMethod.hx new file mode 100644 index 00000000..d22d83f0 --- /dev/null +++ b/tests/server/test/templates/GenericInstanceMethod.hx @@ -0,0 +1,5 @@ +class GenericInstanceMethod { + public function new() {} + + @:generic public function f(t:T) {} +} diff --git a/tests/server/test/templates/GenericMethod.hx b/tests/server/test/templates/GenericMethod.hx new file mode 100644 index 00000000..52a1e70a --- /dev/null +++ b/tests/server/test/templates/GenericMethod.hx @@ -0,0 +1,3 @@ +class GenericMethod { + @:generic static public function f(t:T) {} +} \ No newline at end of file diff --git a/tests/server/test/templates/issues/Issue10635/MainAfter.hx b/tests/server/test/templates/issues/Issue10635/MainAfter.hx new file mode 100644 index 00000000..d3e5d367 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10635/MainAfter.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + GenericMethod; + GenericMethod.f(Main); + } +} \ No newline at end of file diff --git a/tests/server/test/templates/issues/Issue10635/MainBefore.hx b/tests/server/test/templates/issues/Issue10635/MainBefore.hx new file mode 100644 index 00000000..95fa43ba --- /dev/null +++ b/tests/server/test/templates/issues/Issue10635/MainBefore.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + GenericMethod; + } +} \ No newline at end of file diff --git a/tests/server/test/templates/issues/Issue10635/MainInstanceAfter.hx b/tests/server/test/templates/issues/Issue10635/MainInstanceAfter.hx new file mode 100644 index 00000000..f1df99f1 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10635/MainInstanceAfter.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + var c = new GenericInstanceMethod(); + c.f(Main); + } +} diff --git a/tests/server/test/templates/issues/Issue10635/MainInstanceBefore.hx b/tests/server/test/templates/issues/Issue10635/MainInstanceBefore.hx new file mode 100644 index 00000000..8f4c75c6 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10635/MainInstanceBefore.hx @@ -0,0 +1,5 @@ +class Main { + static function main() { + var c = new GenericInstanceMethod(); + } +} diff --git a/tests/server/test/templates/issues/Issue10635/Something.hx b/tests/server/test/templates/issues/Issue10635/Something.hx new file mode 100644 index 00000000..361f08d9 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10635/Something.hx @@ -0,0 +1,9 @@ +class Something { + @:generic + static public function append(a:Array, b:Array) { + for (e in b) { + a.push(e); + } + return a; + } +} diff --git a/tests/server/test/templates/issues/Issue10653/MainAfter.hx b/tests/server/test/templates/issues/Issue10653/MainAfter.hx new file mode 100644 index 00000000..58db9819 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10653/MainAfter.hx @@ -0,0 +1,8 @@ +class Main { + static function main() { + var x = Test.test(); + x = new Main(); + } + + function new() {} +} diff --git a/tests/server/test/templates/issues/Issue10653/MainBefore.hx b/tests/server/test/templates/issues/Issue10653/MainBefore.hx new file mode 100644 index 00000000..8785dedf --- /dev/null +++ b/tests/server/test/templates/issues/Issue10653/MainBefore.hx @@ -0,0 +1,7 @@ +class Main { + static function main() { + var x = Test.test(); + } + + function new() {} +} diff --git a/tests/server/test/templates/issues/Issue10653/Test.hx b/tests/server/test/templates/issues/Issue10653/Test.hx new file mode 100644 index 00000000..a95e75f7 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10653/Test.hx @@ -0,0 +1,5 @@ +class Test { + static public function test() { + return null; + } +} diff --git a/tests/server/test/templates/issues/Issue10677/Main.hx b/tests/server/test/templates/issues/Issue10677/Main.hx new file mode 100644 index 00000000..fa57b86a --- /dev/null +++ b/tests/server/test/templates/issues/Issue10677/Main.hx @@ -0,0 +1,6 @@ +class Main { + static function main() { + trace(Polygons); + Polygon.x = Main; + } +} diff --git a/tests/server/test/templates/issues/Issue10677/Polygon.hx b/tests/server/test/templates/issues/Issue10677/Polygon.hx new file mode 100644 index 00000000..62ba0069 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10677/Polygon.hx @@ -0,0 +1,3 @@ +class Polygon { + static public var x:Class
; +} diff --git a/tests/server/test/templates/issues/Issue10677/PolygonCollider.hx b/tests/server/test/templates/issues/Issue10677/PolygonCollider.hx new file mode 100644 index 00000000..a23636a0 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10677/PolygonCollider.hx @@ -0,0 +1,3 @@ +class PolygonCollider { + static public var p:Polygons; +} diff --git a/tests/server/test/templates/issues/Issue10677/Polygons.hx b/tests/server/test/templates/issues/Issue10677/Polygons.hx new file mode 100644 index 00000000..1dbb1caf --- /dev/null +++ b/tests/server/test/templates/issues/Issue10677/Polygons.hx @@ -0,0 +1,4 @@ +class Polygons { + static public var c:PolygonCollider; + static public var p:Polygon; +} diff --git a/tests/server/test/templates/issues/Issue10678/Macro.hx b/tests/server/test/templates/issues/Issue10678/Macro.hx new file mode 100644 index 00000000..d7a333ab --- /dev/null +++ b/tests/server/test/templates/issues/Issue10678/Macro.hx @@ -0,0 +1,14 @@ +import haxe.macro.Context; + +var lookups = []; + +function init() { + Context.onTypeNotFound(path -> { + lookups.push(path); + return null; + }); + Context.onAfterTyping(_ -> { + var json = haxe.Json.stringify(lookups); + @:privateAccess Context.load("send_json", 1)(json); + }); +} diff --git a/tests/server/test/templates/issues/Issue10986/Main.hx b/tests/server/test/templates/issues/Issue10986/Main.hx new file mode 100644 index 00000000..b76786c2 --- /dev/null +++ b/tests/server/test/templates/issues/Issue10986/Main.hx @@ -0,0 +1,7 @@ +import haxe.ds.Vector; + +class Main { + static public function main() { + final v = new Vector(2, 5); + } +} diff --git a/tests/server/test/templates/issues/Issue10986/Vector.hx b/tests/server/test/templates/issues/Issue10986/Vector.hx new file mode 100644 index 00000000..95bb7e3d --- /dev/null +++ b/tests/server/test/templates/issues/Issue10986/Vector.hx @@ -0,0 +1,43 @@ +package haxe.ds; + +private typedef VectorData = java.NativeArray + +abstract Vector(VectorData) { + public var length(get, never):Int; + + inline function get_length():Int { + return this.length; + } + + extern overload public inline function new(length:Int) { + this = new java.NativeArray(length); + } + + extern overload public inline function new(length:Int, defaultValue:T) { + this = new java.NativeArray(length); + // uncomment and rebuild to break compilation server + // server restart will fix errors until the next change in this file + + // trace("uncomment me"); + } + + @:op([]) public inline function get(index:Int):T { + return this[index]; + } + + @:op([]) public inline function set(index:Int, val:T):T { + return this[index] = val; + } + + public static function blit(src:Vector, srcPos:Int, dest:Vector, destPos:Int, len:Int):Void {} + + static public function fromData(data:VectorData):Vector + return cast data; + + public function copy():Vector { + return cast this; + } + + public function toData():VectorData + return cast this; +} diff --git a/tests/server/test/templates/issues/Issue10986/build.hxml b/tests/server/test/templates/issues/Issue10986/build.hxml new file mode 100644 index 00000000..08d3ae4d --- /dev/null +++ b/tests/server/test/templates/issues/Issue10986/build.hxml @@ -0,0 +1,3 @@ +--main Main +--jvm Main.jar +# --cmd java -jar bin/Main.jar diff --git a/tests/server/test/templates/issues/Issue8368/Main2.hx b/tests/server/test/templates/issues/Issue8368/Main2.hx new file mode 100644 index 00000000..b36642bf --- /dev/null +++ b/tests/server/test/templates/issues/Issue8368/Main2.hx @@ -0,0 +1,11 @@ +class Main { + public static function main() { + buildSimplest(); + } + + macro static function buildSimplest() { + var cb = new MyMacro(null); + cb.build(); + return macro {} + } +} diff --git a/tests/server/test/templates/issues/Issue8368/MyMacro2.macro.hx b/tests/server/test/templates/issues/Issue8368/MyMacro2.macro.hx new file mode 100644 index 00000000..324b32f6 --- /dev/null +++ b/tests/server/test/templates/issues/Issue8368/MyMacro2.macro.hx @@ -0,0 +1,16 @@ +class MyMacro { + var data:FunctionData; + + public function new(data:FunctionData) { + this.data = data; + } + + public function build():Void { + {-1-}data.args.{-2-} + } + +} + +@:structInit class FunctionData { + public final args:Array; +} diff --git a/tests/server/test/templates/issues/Issue9636/Foo.hx b/tests/server/test/templates/issues/Issue9636/Foo.hx new file mode 100644 index 00000000..3dde2ce9 --- /dev/null +++ b/tests/server/test/templates/issues/Issue9636/Foo.hx @@ -0,0 +1,6 @@ +package pack; + +class Foo { + static function __init__() throw "Exception from Foo"; + public static function bar() {} +} diff --git a/tests/server/test/templates/issues/Issue9636/Main.hx b/tests/server/test/templates/issues/Issue9636/Main.hx new file mode 100644 index 00000000..31c90d95 --- /dev/null +++ b/tests/server/test/templates/issues/Issue9636/Main.hx @@ -0,0 +1,6 @@ + +class Main { + static public function main() { + trace("hello world"); + } +} diff --git a/tests/server/test/templates/retyper/AbstractWithDependency.hx b/tests/server/test/templates/retyper/AbstractWithDependency.hx new file mode 100644 index 00000000..98672a38 --- /dev/null +++ b/tests/server/test/templates/retyper/AbstractWithDependency.hx @@ -0,0 +1,5 @@ +abstract AbstractWithDependency(String) { + public static function notMain() { + trace(Dependency.get()); + } +} diff --git a/tests/server/test/templates/retyper/AbstractWithSignatureDependency.hx b/tests/server/test/templates/retyper/AbstractWithSignatureDependency.hx new file mode 100644 index 00000000..44665fce --- /dev/null +++ b/tests/server/test/templates/retyper/AbstractWithSignatureDependency.hx @@ -0,0 +1,3 @@ +abstract AbstractWithSignatureDependency(String) { + public static function test(d:Dependency) {} +} diff --git a/tests/server/test/templates/retyper/DependentEnum.hx b/tests/server/test/templates/retyper/DependentEnum.hx new file mode 100644 index 00000000..d2ae45b2 --- /dev/null +++ b/tests/server/test/templates/retyper/DependentEnum.hx @@ -0,0 +1,3 @@ +enum DependentEnum { + Constructor(s:Dependency); +} diff --git a/tests/server/test/templates/retyper/DependentTypedef.hx b/tests/server/test/templates/retyper/DependentTypedef.hx new file mode 100644 index 00000000..430926ec --- /dev/null +++ b/tests/server/test/templates/retyper/DependentTypedef.hx @@ -0,0 +1 @@ +typedef DependentTypedef = Dependency; diff --git a/tests/server/test/templates/retyper/IndependentEnum.hx b/tests/server/test/templates/retyper/IndependentEnum.hx new file mode 100644 index 00000000..7bac0b72 --- /dev/null +++ b/tests/server/test/templates/retyper/IndependentEnum.hx @@ -0,0 +1,9 @@ +enum IndependentEnum { + Constructor(s:String); +} + +class MakeDependency { + static function f() { + Dependency.get(); + } +} diff --git a/tests/server/test/templates/retyper/IndependentTypedef.hx b/tests/server/test/templates/retyper/IndependentTypedef.hx new file mode 100644 index 00000000..f79493af --- /dev/null +++ b/tests/server/test/templates/retyper/IndependentTypedef.hx @@ -0,0 +1,7 @@ +typedef IndependentTypedef = String; + +class MakeDependency { + static function f() { + Dependency.get(); + } +} diff --git a/tests/server/test/templates/retyper/InterfaceDependency.hx b/tests/server/test/templates/retyper/InterfaceDependency.hx new file mode 100644 index 00000000..41f14634 --- /dev/null +++ b/tests/server/test/templates/retyper/InterfaceDependency.hx @@ -0,0 +1 @@ +interface InterfaceDependency {} diff --git a/tests/server/test/templates/retyper/MutualDependency.hx b/tests/server/test/templates/retyper/MutualDependency.hx new file mode 100644 index 00000000..31ec3dac --- /dev/null +++ b/tests/server/test/templates/retyper/MutualDependency.hx @@ -0,0 +1,5 @@ +class MutualDependency { + static public function get() { + return WithMutualDependency.value; + } +} diff --git a/tests/server/test/templates/retyper/WithInterfaceDependency.hx b/tests/server/test/templates/retyper/WithInterfaceDependency.hx new file mode 100644 index 00000000..6de75847 --- /dev/null +++ b/tests/server/test/templates/retyper/WithInterfaceDependency.hx @@ -0,0 +1 @@ +class WithInterfaceDependency implements InterfaceDependency {} diff --git a/tests/server/test/templates/retyper/WithMutualDependency.hx b/tests/server/test/templates/retyper/WithMutualDependency.hx new file mode 100644 index 00000000..8f42bbcf --- /dev/null +++ b/tests/server/test/templates/retyper/WithMutualDependency.hx @@ -0,0 +1,7 @@ +class WithMutualDependency { + static public var value = "Hello World"; + + public static function main() { + trace(MutualDependency.get()); + } +} diff --git a/tests/server/test/templates/retyper/WithParentDependency.hx b/tests/server/test/templates/retyper/WithParentDependency.hx new file mode 100644 index 00000000..07d6d8fd --- /dev/null +++ b/tests/server/test/templates/retyper/WithParentDependency.hx @@ -0,0 +1 @@ +class WithParentDependency extends Dependency {} diff --git a/tests/server/test/templates/retyper/WithSignatureDependency.hx b/tests/server/test/templates/retyper/WithSignatureDependency.hx new file mode 100644 index 00000000..4fab1e39 --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependency.hx @@ -0,0 +1,3 @@ +class WithSignatureDependency { + public static function test(d:Dependency) {} +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyInferredArg.hx b/tests/server/test/templates/retyper/WithSignatureDependencyInferredArg.hx new file mode 100644 index 00000000..0d71cf92 --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyInferredArg.hx @@ -0,0 +1,5 @@ +class WithSignatureDependency { + public static function test(d) { + d = new Dependency(); + } +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyInferredProperty.hx b/tests/server/test/templates/retyper/WithSignatureDependencyInferredProperty.hx new file mode 100644 index 00000000..ca47aa5f --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyInferredProperty.hx @@ -0,0 +1,3 @@ +class WithSignatureDependency { + public static var test(default, null) = new Dependency(); +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyInferredRet.hx b/tests/server/test/templates/retyper/WithSignatureDependencyInferredRet.hx new file mode 100644 index 00000000..1e1bbde3 --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyInferredRet.hx @@ -0,0 +1,5 @@ +class WithSignatureDependency { + public static function test() { + return new Dependency(); + } +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyInferredVariable.hx b/tests/server/test/templates/retyper/WithSignatureDependencyInferredVariable.hx new file mode 100644 index 00000000..8c3e7c8c --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyInferredVariable.hx @@ -0,0 +1,3 @@ +class WithSignatureDependency { + public static var test = new Dependency(); +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyProperty.hx b/tests/server/test/templates/retyper/WithSignatureDependencyProperty.hx new file mode 100644 index 00000000..49e17954 --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyProperty.hx @@ -0,0 +1,3 @@ +class WithSignatureDependency { + public static var test(default, null):Dependency; +} diff --git a/tests/server/test/templates/retyper/WithSignatureDependencyVariable.hx b/tests/server/test/templates/retyper/WithSignatureDependencyVariable.hx new file mode 100644 index 00000000..f2e5fa60 --- /dev/null +++ b/tests/server/test/templates/retyper/WithSignatureDependencyVariable.hx @@ -0,0 +1,3 @@ +class WithSignatureDependency { + public static var test:Dependency; +} diff --git a/tests/sys/compile-js.hxml b/tests/sys/compile-js.hxml new file mode 100644 index 00000000..23277af0 --- /dev/null +++ b/tests/sys/compile-js.hxml @@ -0,0 +1,22 @@ +compile-each.hxml +--main Main +-js bin/js/sys.js +-lib hxnodejs + +--next +compile-each.hxml +--main TestArguments +-js bin/js/TestArguments.js +-lib hxnodejs + +--next +compile-each.hxml +--main ExitCode +-js bin/js/ExitCode.js +-lib hxnodejs + +--next +compile-each.hxml +--main UtilityProcess +-js bin/js/UtilityProcess.js +-lib hxnodejs diff --git a/tests/sys/compile.hxml b/tests/sys/compile.hxml index b47f9564..35c14c48 100644 --- a/tests/sys/compile.hxml +++ b/tests/sys/compile.hxml @@ -8,3 +8,5 @@ --next compile-java.hxml --next compile-php.hxml --next compile-hl.hxml +--next compile-js.hxml +--next compile-lua.hxml diff --git a/tests/sys/genTestRes.py b/tests/sys/genTestRes.py deleted file mode 100755 index 46c82cdd..00000000 --- a/tests/sys/genTestRes.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python3 - -# Generates files and directories in test-res used for Unicode sys tests. -# The test vector printf'ed into data.bin, as well as the names in filenames() -# should correspond exactly to the sequences in UnicodeSequences.valid. - -# Run with: -# python3 genTestRes.py -# Or: -# python3 genTestRes.py TEST_INVALID_UNICODE_FS -# The latter will attempt to create filenames which contain invalid Unicode -# codepoints; this does not work on some filesystems, e.g. APFS. - -import os -import shutil -import sys - -MODE = " ".join(sys.argv[1:]) - -TESTDIR = "test-res" - -# delete previous, if any -if os.path.isdir(TESTDIR): - shutil.rmtree(TESTDIR) - -os.mkdir(TESTDIR) - -# Unicode test vectors -allUnicode = [ - [0x01], # will not work on NTFS - [0x7F], - [0xC2, 0x80], - [0xDF, 0xBF], - [0xE0, 0xA0, 0x80], - [0xED, 0x9F, 0xBF], # will not work on APFS - [0xEE, 0x80, 0x80], - [0xEF, 0xBF, 0xBD], - [0xF0, 0x90, 0x80, 0x80], - [0xF0, 0x9F, 0xBF, 0xBF], # will not work on APFS - [0xF3, 0xBF, 0xBF, 0xBF], # will not work on APFS - [0xF4, 0x80, 0x80, 0x80], - [0xF4, 0x8F, 0xBF, 0xBF], # will not work on APFS - [0xF0, 0x9F, 0x98, 0x82, 0xF0, 0x9F, 0x98, 0x84, 0xF0, 0x9F, 0x98, 0x99], - [0xC8, 0xA7], - [0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, 0xEF, 0xBC, 0x8C, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0x94] - ] - -allStrings = [ bytes(data).decode("utf-8") for data in allUnicode ] - -allFilenames = allStrings[:] -# Windows does not allow codepoints in the U+0000 - U+001F range -# see https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file -if os.name == "nt": - allFilenames.remove(bytes([0x01]).decode("utf-8")) - -# on APFS (macOS 10.13+), filenames must consist of valid Unicode codepoints -if MODE != "TEST_INVALID_UNICODE_FS": - allFilenames.remove(bytes([0xED, 0x9F, 0xBF]).decode("utf-8")) - allFilenames.remove(bytes([0xF0, 0x9F, 0xBF, 0xBF]).decode("utf-8")) - allFilenames.remove(bytes([0xF3, 0xBF, 0xBF, 0xBF]).decode("utf-8")) - allFilenames.remove(bytes([0xF4, 0x8F, 0xBF, 0xBF]).decode("utf-8")) - -allBinary = b"" -for data in allUnicode: - allBinary += bytes(data) + b"\n" - -# generate a file with Unicode data -with open(os.path.join(TESTDIR, "data.bin"), "wb") as f: - f.write(allBinary) - -# generate sub-directories with symlinks -os.mkdir(os.path.join(TESTDIR, "a")) -for data in allFilenames: - os.mkdir(os.path.join(TESTDIR, data)) - os.mkdir(os.path.join(TESTDIR, "a", data)) - if os.name != "nt": - for target, name in [ - ("../../bin/cpp/UtilityProcess-debug", "bin-cpp-debug"), - ("../../bin/cpp/UtilityProcess", "bin-cpp"), - ("../../bin/cs/bin/UtilityProcess-Debug.exe", "bin-cs-debug"), - ("../../bin/cs/bin/UtilityProcess.exe", "bin-cs"), - ("../../bin/hl/UtilityProcess.hl", "bin-hl"), - ("../../bin/lua/UtilityProcess.lua", "bin-lua"), - ("../../bin/java/UtilityProcess-Debug.jar", "bin-java-debug"), - ("../../bin/java/UtilityProcess.jar", "bin-java"), - ("../../bin/jvm/UtilityProcess.jar", "bin-jvm"), - ("../../bin/neko/UtilityProcess.n", "bin-neko"), - ("../../bin/php/UtilityProcess/index.php", "bin-php"), - ("../../bin/python/UtilityProcess.py", "bin-py"), - ("../../src/UtilityProcess.hx", "bin-eval") - ]: - os.symlink(target, os.path.join(TESTDIR, data, name), target_is_directory = False) - -# files -os.mkdir(os.path.join(TESTDIR, "b")) -for data in allFilenames: - with open(os.path.join(TESTDIR, "b", data), "wb") as f: - f.write(allBinary) diff --git a/tests/sys/gen_test_res.py b/tests/sys/gen_test_res.py new file mode 100755 index 00000000..0d50a597 --- /dev/null +++ b/tests/sys/gen_test_res.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 + +# Generates files and directories in test-res used for Unicode sys tests. +# The test vector printf'ed into data.bin, as well as the names in filenames() +# should correspond exactly to the sequences in UnicodeSequences.valid. + +# Run with: +# python3 gen_test_res.py +# Or: +# python3 gen_test_res.py TEST_INVALID_UNICODE_FS +# The latter will attempt to create filenames which contain invalid Unicode +# codepoints; this does not work on some filesystems, e.g. APFS. + +import os +import shutil +import sys + +MODE = " ".join(sys.argv[1:]) + +TESTDIR = "test-res" + +# delete previous, if any +if os.path.isdir(TESTDIR): + shutil.rmtree(TESTDIR) + +os.mkdir(TESTDIR) + +# Unicode test vectors +all_unicode = [ + [0x01], # will not work on NTFS + [0x7F], + [0xC2, 0x80], + [0xDF, 0xBF], + [0xE0, 0xA0, 0x80], + [0xED, 0x9F, 0xBF], # will not work on APFS + [0xEE, 0x80, 0x80], + [0xEF, 0xBF, 0xBD], + [0xF0, 0x90, 0x80, 0x80], + [0xF0, 0x9F, 0xBF, 0xBF], # will not work on APFS + [0xF3, 0xBF, 0xBF, 0xBF], # will not work on APFS + [0xF4, 0x80, 0x80, 0x80], + [0xF4, 0x8F, 0xBF, 0xBF], # will not work on APFS + [0xF0, 0x9F, 0x98, 0x82, 0xF0, 0x9F, 0x98, 0x84, 0xF0, 0x9F, 0x98, 0x99], + [0xC8, 0xA7], + [0xE4, 0xB8, 0xAD, 0xE6, 0x96, 0x87, 0xEF, 0xBC, 0x8C, 0xE3, 0x81, 0xAB, + 0xE3, 0x81, 0xBB, 0xE3, 0x82, 0x93, 0xE3, 0x81, 0x94] + ] + +all_strings = [bytes(data).decode("utf-8") for data in all_unicode] + +all_filenames = all_strings[:] +# Windows does not allow codepoints in the U+0000 - U+001F range +# see https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file +if os.name == "nt": + all_filenames.remove(bytes([0x01]).decode("utf-8")) + +# on APFS (macOS 10.13+), filenames must consist of valid Unicode codepoints +if MODE != "TEST_INVALID_UNICODE_FS": + all_filenames.remove(bytes([0xED, 0x9F, 0xBF]).decode("utf-8")) + all_filenames.remove(bytes([0xF0, 0x9F, 0xBF, 0xBF]).decode("utf-8")) + all_filenames.remove(bytes([0xF3, 0xBF, 0xBF, 0xBF]).decode("utf-8")) + all_filenames.remove(bytes([0xF4, 0x8F, 0xBF, 0xBF]).decode("utf-8")) + +all_binary = b"" +for data in all_unicode: + all_binary += bytes(data) + b"\n" + +# generate a file with Unicode data +with open(os.path.join(TESTDIR, "data.bin"), "wb") as f: + f.write(all_binary) + +# generate sub-directories with symlinks +os.mkdir(os.path.join(TESTDIR, "a")) +for data in all_filenames: + os.mkdir(os.path.join(TESTDIR, data)) + os.mkdir(os.path.join(TESTDIR, "a", data)) + if os.name == "nt": + continue + for target, name in [ + ("../../bin/cpp/UtilityProcess-debug", "bin-cpp-debug"), + ("../../bin/cpp/UtilityProcess", "bin-cpp"), + ("../../bin/cs/bin/UtilityProcess-Debug.exe", "bin-cs-debug"), + ("../../bin/cs/bin/UtilityProcess.exe", "bin-cs"), + ("../../bin/hl/UtilityProcess.hl", "bin-hl"), + ("../../bin/lua/UtilityProcess.lua", "bin-lua"), + ("../../bin/java/UtilityProcess-Debug.jar", "bin-java-debug"), + ("../../bin/java/UtilityProcess.jar", "bin-java"), + ("../../bin/jvm/UtilityProcess.jar", "bin-jvm"), + ("../../bin/neko/UtilityProcess.n", "bin-neko"), + ("../../bin/php/UtilityProcess/index.php", "bin-php"), + ("../../bin/python/UtilityProcess.py", "bin-py"), + ("../../src/UtilityProcess.hx", "bin-eval"), + ("../../bin/js/UtilityProcess.js", "bin-js") + ]: + os.symlink(target, os.path.join(TESTDIR, data, name), target_is_directory=False) + +# files +os.mkdir(os.path.join(TESTDIR, "b")) +for data in all_filenames: + with open(os.path.join(TESTDIR, "b", data), "wb") as f: + f.write(all_binary) diff --git a/tests/sys/run.hxml b/tests/sys/run.hxml index eea810f1..bbd1e0ee 100644 --- a/tests/sys/run.hxml +++ b/tests/sys/run.hxml @@ -3,26 +3,33 @@ # Compile everything first. compile.hxml +# Install Dependencies +# --next +# --cmd luarocks install haxe-deps 0.0.1-6 +# --cmd npm install deasync + # Mac/Linux --next --cmd echo Neko && neko bin/neko/sys.n --cmd echo Python && python3 bin/python/sys.py --cmd echo Cpp && bin/cpp/Main-debug --cmd echo CS && mono bin/cs/bin/Main-Debug.exe --cmd echo Java && java -jar bin/java/Main-Debug.jar --cmd echo Php && php bin/php/Main/index.php +--cmd echo Neko && export EXISTS=1 && neko bin/neko/sys.n +--cmd echo Python && export EXISTS=1 && python3 bin/python/sys.py +--cmd echo Cpp && export EXISTS=1 && bin/cpp/Main-debug +--cmd echo CS && export EXISTS=1 && mono bin/cs/bin/Main-Debug.exe +--cmd echo Java && export EXISTS=1 && java -jar bin/java/Main-Debug.jar +--cmd echo Php && export EXISTS=1 && php bin/php/Main/index.php +--cmd echo Hl && export EXISTS=1 && hl bin/hl/sys.hl +--cmd echo Js && export EXISTS=1 && node bin/js/sys.js +--cmd echo Lua && export EXISTS=1 && lua bin/sys.lua +--cmd echo Macro && export EXISTS=1 && haxe compile-macro.hxml # Windows # --next -# -cmd echo Neko && neko bin\neko\sys.n -# -cmd echo Python && python3 bin\python\sys.py -# -cmd echo Cpp && bin\cpp\Main-debug.exe -# -cmd echo CS && bin\cs\bin\Main-Debug.exe -# -cmd echo Java && java -jar bin\java\Main-Debug.jar -# -cmd echo Php && php bin\php\Main\index.php - -# Macro has to be placed at the end since it would exit the compilation process. ---next --cmd echo Macro ---next -compile-macro.hxml \ No newline at end of file +# --cmd echo Neko && set EXISTS=1 && neko bin\neko\sys.n +# --cmd echo Python && set EXISTS=1 && python3 bin\python\sys.py +# --cmd echo Cpp && set EXISTS=1 && bin\cpp\Main-debug.exe +# --cmd echo CS && set EXISTS=1 && bin\cs\bin\Main-Debug.exe +# --cmd echo Java && set EXISTS=1 && java -jar bin\java\Main-Debug.jar +# --cmd echo Php && set EXISTS=1 && php -c ..\PHP.ini bin\php\Main\index.php +# --cmd echo Hl && set EXISTS=1 && hl bin/hl/sys.hl +# --cmd echo Js && set EXISTS=1 && node bin/js/sys.js +# --cmd echo Lua && set EXISTS=1 && lua bin/lua/sys.lua +# --cmd echo Macro && set EXISTS=1 && haxe compile-macro.hxml diff --git a/tests/sys/src/ExitCode.hx b/tests/sys/src/ExitCode.hx index 7bce3e84..cddcf86f 100644 --- a/tests/sys/src/ExitCode.hx +++ b/tests/sys/src/ExitCode.hx @@ -39,6 +39,8 @@ class ExitCode { "bin/php/ExitCode/index.php"; #elseif lua "bin/lua/ExitCode.lua"; + #elseif js + "bin/js/ExitCode.js"; #else null; #end diff --git a/tests/sys/src/Main.hx b/tests/sys/src/Main.hx index fbeb3a5b..a9718643 100644 --- a/tests/sys/src/Main.hx +++ b/tests/sys/src/Main.hx @@ -10,26 +10,14 @@ class Main { runner.addCase(new TestFileSystem()); runner.addCase(new io.TestFile()); runner.addCase(new io.TestFileInput()); + #if !js runner.addCase(new io.TestProcess()); - #if !(java || cs || lua || python || eval) // Sqlite is not implemented for these targets - #if !hl // Idk how to resolve "FATAL ERROR : Failed to load library sqlite.hdll" - var testSqlite = #if php Sys.systemName() != 'Windows' #else true #end; //our CI doesn't have sqlite php module - if(testSqlite) { - runner.addCase(new db.TestSqliteConnection()); - runner.addCase(new db.TestSqliteResultSet()); - } #end + #if !(java || cs || lua || python || eval || js) // Sqlite is not implemented for these targets + runner.addCase(new db.TestSqliteConnection()); + runner.addCase(new db.TestSqliteResultSet()); #end - #if php - switch (Sys.systemName()) { - case "Windows": - // pass - case _: - runner.addCase(new net.TestSocket()); - } - #else - runner.addCase(new net.TestSocket()); - #end + runner.addCase(new net.TestSocket()); var report = Report.create(runner); report.displayHeader = AlwaysShowHeader; report.displaySuccessResults = NeverShowSuccessResults; diff --git a/tests/sys/src/TestArguments.hx b/tests/sys/src/TestArguments.hx index fd70f372..67a34068 100644 --- a/tests/sys/src/TestArguments.hx +++ b/tests/sys/src/TestArguments.hx @@ -99,6 +99,8 @@ class TestArguments extends utest.Test { "bin/php/TestArguments/index.php"; #elseif lua "bin/lua/TestArguments.lua"; + #elseif js + "bin/js/TestArguments.js"; #else null; #end diff --git a/tests/sys/src/TestCommandBase.hx b/tests/sys/src/TestCommandBase.hx index c903c85d..00c93c95 100644 --- a/tests/sys/src/TestCommandBase.hx +++ b/tests/sys/src/TestCommandBase.hx @@ -42,6 +42,8 @@ class TestCommandBase extends utest.Test { run(php.Global.defined('PHP_BINARY') ? php.Const.PHP_BINARY : 'php', [bin].concat(args)); #elseif lua run("lua", [bin].concat(args)); + #elseif js + run("node", [bin].concat(args)); #else -1; #end @@ -133,6 +135,8 @@ class TestCommandBase extends utest.Test { run(php.Global.defined('PHP_BINARY') ? php.Const.PHP_BINARY : 'php', [bin].concat(args)); #elseif lua run("lua", [bin].concat(args)); + #elseif js + run("node", [bin].concat(args)); #else -1; #end diff --git a/tests/sys/src/TestFileSystem.hx b/tests/sys/src/TestFileSystem.hx index fa411f85..385086b9 100644 --- a/tests/sys/src/TestFileSystem.hx +++ b/tests/sys/src/TestFileSystem.hx @@ -141,12 +141,11 @@ class TestFileSystem extends utest.Test { } static function normPath(p:String, properCase = false):String { - if (Sys.systemName() == "Windows") - { + if (Sys.systemName() == "Windows") { // on windows, haxe returns lowercase paths with backslashes, drive letter uppercased p = p.substr(0, 1).toUpperCase() + p.substr(1); p = p.replace("/", "\\"); } return p; } -} \ No newline at end of file +} diff --git a/tests/sys/src/TestSys.hx b/tests/sys/src/TestSys.hx index 9ad77b4c..68196f8e 100644 --- a/tests/sys/src/TestSys.hx +++ b/tests/sys/src/TestSys.hx @@ -5,18 +5,99 @@ class TestSys extends TestCommandBase { return Sys.command(cmd, args); } - function testEnv() { - #if !(java) - Sys.putEnv("foo", "value"); - Assert.equals("value", Sys.getEnv("foo")); + function testEnvironment() { + var env = Sys.environment(); + // EXISTS should be set manually via the command line + Assert.notNull(env.get("EXISTS")); + Assert.isNull(env.get("doesn't exist")); + + final nonExistent = "NON_EXISTENT"; + env.set(nonExistent, "1"); + // new copies should not be affected + Assert.isNull(Sys.environment()[nonExistent]); + + #if !java + // env should not update when environment updates + final toUpdate = "TO_UPDATE"; + + Sys.putEnv(toUpdate, "1"); + Assert.isNull(env.get(toUpdate)); + + // new copy should have the variable + Assert.equals("1", Sys.environment()[toUpdate]); + + // environment should not update if env updates + env.set(toUpdate, "2"); + Assert.equals("1", Sys.getEnv(toUpdate)); + + // variables set via target specific api should exist + #if (cs || python) + final toSetNatively = "SET_NATIVELY"; + #if cs + cs.system.Environment.SetEnvironmentVariable(toSetNatively, "1"); + #elseif python + python.lib.Os.environ.set(toSetNatively, "1"); #end - Assert.equals(null, Sys.getEnv("doesn't exist")); + Assert.equals("1", Sys.environment()[toSetNatively]); + #end + #end + } - #if !(java) - var env = Sys.environment(); - Assert.equals("value", env.get("foo")); + function existsInSubProcess(variable:String, value:String) { + #if js + return UtilityProcess.runUtilityAsCommand(["checkEnv", variable, value]) == 0; + #else + return UtilityProcess.runUtility(["checkEnv", variable, value]).exitCode == 0; + #end + } + + function testGetEnv() { + // EXISTS should be set manually via the command line + Assert.notNull(Sys.getEnv("EXISTS")); + + // on windows, Sys.getEnv should be case insensitive + if (Sys.systemName() == "Windows") + Assert.notNull(Sys.getEnv("exists")); + + Assert.isNull(Sys.getEnv("doesn't exist")); + } + + #if !java + function testPutEnv() { + Sys.putEnv("FOO", "value"); + Assert.equals("value", Sys.getEnv("FOO")); + + Assert.equals("value", Sys.environment().get("FOO")); + + Assert.isTrue(existsInSubProcess("FOO", "value")); + + #if python + // the variable should also be visible through python's api + Assert.equals("value", python.lib.Os.environ.get("FOO")); + #end + + // null + Sys.putEnv("FOO", null); + Assert.isNull(Sys.getEnv("FOO")); + + Assert.isFalse(Sys.environment().exists("FOO")); + + Assert.isFalse(existsInSubProcess("FOO", "value")); + + #if python + // the variable should also be gone when checking through python's api + Assert.isFalse(python.lib.Os.environ.hasKey("FOO")); #end + + Assert.isTrue(try { + Sys.putEnv("NON_EXISTENT", null); + true; + } catch (e) { + trace(e); + false; + }); } + #end function testProgramPath() { var p = Sys.programPath(); @@ -50,8 +131,14 @@ class TestSys extends TestCommandBase { #end } + function testGetCwd() { + final current = Sys.getCwd(); + // ensure it has a trailing slash + Assert.notEquals(current, haxe.io.Path.removeTrailingSlashes(current)); + } + #if !java - function testCwd() { + function testSetCwd() { var cur = Sys.getCwd(); Sys.setCwd("../"); var newCwd = haxe.io.Path.join([cur, "../"]); diff --git a/tests/sys/src/TestUnicode.hx b/tests/sys/src/TestUnicode.hx index a1b2bf3a..f6446213 100644 --- a/tests/sys/src/TestUnicode.hx +++ b/tests/sys/src/TestUnicode.hx @@ -42,6 +42,8 @@ class TestUnicode extends utest.Test { "bin-py"; #elseif eval "bin-eval"; +#elseif js + "bin-js"; #else null; #end @@ -129,9 +131,9 @@ class TestUnicode extends utest.Test { function setupClass() { FileSystem.createDirectory("temp-unicode"); #if TEST_INVALID_UNICODE_FS - Sys.command("python3", ["genTestRes.py", "TEST_INVALID_UNICODE_FS"]); + Sys.command("python3", ["gen_test_res.py", "TEST_INVALID_UNICODE_FS"]); #else - Sys.command("python3", ["genTestRes.py"]); + Sys.command("python3", ["gen_test_res.py"]); #end } @@ -146,8 +148,8 @@ class TestUnicode extends utest.Test { #if target.unicode function testFilesystem() { -#if !java -#if !(cpp || cs) // C++ disabled temporarily (#8400), C# disabled temporarily (#8247) +#if !java // java does not have this functionality +#if !cs // C# disabled temporarily (#8247) // setCwd + getCwd Sys.setCwd("test-res"); function enterLeave(dir:String, ?alt:String):Void { @@ -183,8 +185,8 @@ class TestUnicode extends utest.Test { ); }, "test-res"); -#if !java -#if !(cpp || cs) // C++ disabled temporarily (#8400), C# disabled temporarily (#8247) +#if !java // java does not have this functionality +#if !cs // C# disabled temporarily (#8247) assertNormalEither(path -> { if (!FileSystem.exists(path)) return false; // NFC/NFD differences Sys.setCwd(path); @@ -207,10 +209,8 @@ class TestUnicode extends utest.Test { #end // exists -#if !cpp // C++ disabled temporarily (#8400) assertNormalEither(FileSystem.exists, 'test-res/a', 'expected exists == true'); assertNormalEither(FileSystem.exists, 'test-res/b', 'expected exists == false'); -#end // fullPath #if !lua // Lua disabled temporarily (#8215) @@ -257,7 +257,6 @@ class TestUnicode extends utest.Test { }); // rename -#if !cpp // C++ disabled temporarily (#8400) File.copy("test-res/data.bin", "temp-unicode/rename-me"); pathBoth(str -> { FileSystem.rename('temp-unicode/rename-me', 'temp-unicode/$str'); @@ -265,9 +264,7 @@ class TestUnicode extends utest.Test { Assert.isTrue(FileSystem.exists('temp-unicode/$str')); FileSystem.rename('temp-unicode/$str', 'temp-unicode/rename-me'); }); -#end -#if !cpp // C++ disabled temporarily (#8400) pathBoth(str -> { // copy File.copy("test-res/data.bin", 'temp-unicode/$str'); @@ -287,7 +284,6 @@ class TestUnicode extends utest.Test { FileSystem.deleteDirectory('temp-unicode/$str'); Assert.isFalse(FileSystem.exists('temp-unicode/$str')); }); -#end } // Temporary disabled for local run because of https://github.com/HaxeFoundation/haxe/issues/8380 @@ -323,18 +319,16 @@ class TestUnicode extends utest.Test { // trace assertUEnds(runUtility(["trace", '$i', mode]).stdout, str + endLine); #if !java -#if (hl || cpp) if (Sys.systemName() != "Windows") { #end // HL and C++ temporarily disabled (#8379) // putEnv + getEnv assertUEquals(runUtility(["putEnv", "HAXE_TEST", '$i', mode, "getEnv", "HAXE_TEST"]).stdout, str + endLine); // putEnv + environment assertUEquals(runUtility(["putEnv", "HAXE_TEST", '$i', mode, "environment", "HAXE_TEST"]).stdout, str + endLine); -#if (hl || cpp) } #end // HL and C++ temporarily disabled (#8379) #end }); // args #if !cs // C# behaves like Windows here - if (#if (java || eval || hl || cpp) Sys.systemName() != "Windows" #else true #end) { + if (#if (java || eval || cpp) Sys.systemName() != "Windows" #else true #end) { // https://stackoverflow.com/questions/7660651/passing-command-line-unicode-argument-to-java-code UnicodeSequences.normalBoth(str -> { assertUEquals(runUtility(["args", str]).stdout, str + endLine); @@ -389,13 +383,18 @@ class TestUnicode extends utest.Test { assertBytesEqual(File.getBytes("temp-unicode/out.bin"), UnicodeSequences.validBytes); // append - var out = File.append("temp-unicode/out.bin"); - out.writeString(UnicodeSequences.validString); - out.close(); - var repeated = Bytes.alloc(UnicodeSequences.validBytes.length * 2); - repeated.blit(0, UnicodeSequences.validBytes, 0, UnicodeSequences.validBytes.length); - repeated.blit(UnicodeSequences.validBytes.length, UnicodeSequences.validBytes, 0, UnicodeSequences.validBytes.length); - assertBytesEqual(File.getBytes("temp-unicode/out.bin"), repeated); +#if js + if (Sys.systemName() != "Mac") // File.append() here is broken on mac +#end + { + var out = File.append("temp-unicode/out.bin"); + out.writeString(UnicodeSequences.validString); + out.close(); + var repeated = Bytes.alloc(UnicodeSequences.validBytes.length * 2); + repeated.blit(0, UnicodeSequences.validBytes, 0, UnicodeSequences.validBytes.length); + repeated.blit(UnicodeSequences.validBytes.length, UnicodeSequences.validBytes, 0, UnicodeSequences.validBytes.length); + assertBytesEqual(File.getBytes("temp-unicode/out.bin"), repeated); + } // readLine var data = File.read("test-res/data.bin"); diff --git a/tests/sys/src/UtilityProcess.hx b/tests/sys/src/UtilityProcess.hx index 2ebda1a2..fff1af0f 100644 --- a/tests/sys/src/UtilityProcess.hx +++ b/tests/sys/src/UtilityProcess.hx @@ -1,5 +1,5 @@ /** - Used by TestUnicode. + Used by TestUnicode and TestSys. Runs a given simple program based on the first argument. */ @@ -28,6 +28,8 @@ class UtilityProcess { Path.join(["bin", "python"]); #elseif eval Path.join(["src"]); +#elseif js + Path.join(["bin", "js"]); #else null; #end @@ -64,6 +66,8 @@ class UtilityProcess { "UtilityProcess.py"; #elseif eval "UtilityProcess.hx"; +#elseif js + "UtilityProcess.js"; #else null; #end @@ -118,22 +122,68 @@ class UtilityProcess { stderr: stderr }; } - + + /** Runs the utility program via Sys.command rather than as a separate process, + for compatiblity with hxnodejs. + + Returns the exit code of the command. + **/ + public static function runUtilityAsCommand(args:Array, ?options:{?stdin:String, ?execPath:String, ?execName:String}):Int { + if (options == null) options = {}; + if (options.execPath == null) options.execPath = BIN_PATH; + if (options.execName == null) options.execName = BIN_NAME; + final execFull = Path.join([options.execPath, options.execName]); + final exitCode = + #if (macro || interp) + Sys.command("haxe", ["compile-each.hxml", "-p", options.execPath, "--run", options.execName].concat(args)); + #elseif cpp + Sys.command(execFull, args); + #elseif cs + (switch (Sys.systemName()) { + case "Windows": + Sys.command(execFull, args); + case _: + Sys.command("mono", [execFull].concat(args)); + }); + #elseif java + Sys.command(Path.join([java.lang.System.getProperty("java.home"), "bin", "java"]), ["-jar", execFull].concat(args)); + #elseif python + Sys.command(python.lib.Sys.executable, [execFull].concat(args)); + #elseif neko + Sys.command("neko", [execFull].concat(args)); + #elseif hl + Sys.command("hl", [execFull].concat(args)); + #elseif php + Sys.command(php.Global.defined('PHP_BINARY') ? php.Const.PHP_BINARY : 'php', [execFull].concat(args)); + #elseif lua + Sys.command("lua", [execFull].concat(args)); + #elseif js + Sys.command("node", [execFull].concat(args)); + #else + 1; + #end + + return exitCode; + } + public static function main():Void { var args = Sys.args(); - function sequenceIndex(d:String, mode:String):String return (switch (UnicodeSequences.valid[Std.parseInt(d)]) { + function sequenceIndex(d:String, mode:String):String + return switch UnicodeSequences.valid[Std.parseInt(d)] { case Only(ref): UnicodeSequences.codepointsToString(ref); case Normal(nfc, nfd): UnicodeSequences.codepointsToString(mode == "nfc" ? nfc : nfd); - }); + }; switch (args) { case _.slice(0, 1) => ["putEnv"]: - // ["putEnv", var name, index, nfc mode, next args...] - Sys.putEnv(args[1], sequenceIndex(args[2], args[3])); - var out = runUtility(args.slice(4)); - Sys.print(out.stdout); - Sys.exit(out.exitCode); + // ["putEnv", var name, index, nfc mode, next args...] + Sys.putEnv(args[1], sequenceIndex(args[2], args[3])); + var out = runUtility(args.slice(4)); + Sys.print(out.stdout); + Sys.exit(out.exitCode); case ["getCwd"]: Sys.println(Sys.getCwd()); case ["getEnv", name]: Sys.println(Sys.getEnv(name)); + case ["checkEnv", name, value]: + Sys.exit(value == Sys.getEnv(name) ? 0 : 1); case ["environment", name]: Sys.println(Sys.environment().get(name)); case ["exitCode", Std.parseInt(_) => code]: Sys.exit(code); case ["args", data]: Sys.println(data); @@ -144,9 +194,9 @@ class UtilityProcess { case ["stdin.readString", Std.parseInt(_) => len]: Sys.println(Sys.stdin().readString(len, UTF8)); case ["stdin.readUntil", Std.parseInt(_) => end]: Sys.println(Sys.stdin().readUntil(end)); case ["stderr.writeString", d, mode]: - var stream = Sys.stderr(); stream.writeString(sequenceIndex(d, mode)); stream.flush(); + var stream = Sys.stderr(); stream.writeString(sequenceIndex(d, mode)); stream.flush(); case ["stdout.writeString", d, mode]: - var stream = Sys.stdout(); stream.writeString(sequenceIndex(d, mode)); stream.flush(); + var stream = Sys.stdout(); stream.writeString(sequenceIndex(d, mode)); stream.flush(); case ["programPath"]: Sys.println(Sys.programPath()); case _: // no-op } diff --git a/tests/sys/src/net/TestSocket.hx b/tests/sys/src/net/TestSocket.hx index edeba6cc..224d090f 100644 --- a/tests/sys/src/net/TestSocket.hx +++ b/tests/sys/src/net/TestSocket.hx @@ -19,9 +19,11 @@ class TestSocket extends utest.Test { registeredSockets = []; } + #if !js // bind is not implemented on nodejs public function testBind() { var socket = register(new Socket()); socket.bind(new Host('localhost'), 34567); Assert.pass(); } -} \ No newline at end of file + #end +} diff --git a/tests/threads/build.hxml b/tests/threads/build.hxml index 153c652c..b4d1f303 100644 --- a/tests/threads/build.hxml +++ b/tests/threads/build.hxml @@ -2,4 +2,6 @@ --main Main --library utest --dce full --D analyzer-optimize \ No newline at end of file +-D analyzer-optimize +-D UTEST_PRINT_TESTS +-D hl-ver=1.13.0 \ No newline at end of file diff --git a/tests/threads/src/cases/TestAtomics.hx b/tests/threads/src/cases/TestAtomics.hx new file mode 100644 index 00000000..bc720120 --- /dev/null +++ b/tests/threads/src/cases/TestAtomics.hx @@ -0,0 +1,23 @@ +package cases; + + +#if target.atomics +import haxe.atomic.AtomicInt; +#end + +@:timeout(2000) +class TestAtomics extends utest.Test { + #if target.atomics + function test(async:Async) { + var a = new AtomicInt(5); + final thread = Thread.create(() -> { + while(a.compareExchange(0, 2) != 0) {} + }); + isTrue(a.compareExchange(5, 0) == 5); + + while (a.compareExchange(2, 2) != 2) {} + + async.done(); + } + #end +} diff --git a/tests/threads/src/cases/TestCondition.hx b/tests/threads/src/cases/TestCondition.hx new file mode 100644 index 00000000..aa9f9f97 --- /dev/null +++ b/tests/threads/src/cases/TestCondition.hx @@ -0,0 +1,23 @@ +package cases; + +#if !neko +import sys.thread.Condition; +import sys.thread.Thread; +#end + +class TestCondition extends utest.Test { + #if !neko + function test() { + final cond = new Condition(); + cond.acquire(); + Thread.create(() -> { + cond.acquire(); + cond.signal(); + cond.release(); + }); + cond.wait(); + cond.release(); + utest.Assert.pass(); + } + #end +} diff --git a/tests/threads/src/cases/TestEvents.hx b/tests/threads/src/cases/TestEvents.hx index 357c96a8..c60c8846 100644 --- a/tests/threads/src/cases/TestEvents.hx +++ b/tests/threads/src/cases/TestEvents.hx @@ -14,9 +14,9 @@ class TestEvents extends utest.Test { events.cancel(e1); events.cancel(e2); events.cancel(e3); - }, 2); - e1 = events.repeat(() -> checks.push(1), 1); - e3 = events.repeat(() -> checks.push(3), 3); + }, 20); + e1 = events.repeat(() -> checks.push(1), 10); + e3 = events.repeat(() -> checks.push(3), 30); Sys.sleep(0.1); var checker = null; diff --git a/tests/threads/src/cases/TestSemaphore.hx b/tests/threads/src/cases/TestSemaphore.hx new file mode 100644 index 00000000..f18ce27d --- /dev/null +++ b/tests/threads/src/cases/TestSemaphore.hx @@ -0,0 +1,21 @@ +package cases; + +#if !neko +import sys.thread.Semaphore; +#end + +class TestSemaphore extends utest.Test { + #if !neko + function test() { + var m = new Semaphore(3); + m.acquire(); + m.acquire(); + isTrue(m.tryAcquire()); + isFalse(m.tryAcquire()); + isFalse(m.tryAcquire(0.1)); + m.release(); + m.release(); + m.release(); + } + #end +} diff --git a/tests/unit/.vscode/settings.json b/tests/unit/.vscode/settings.json index 8e66727a..1bd9f6fd 100644 --- a/tests/unit/.vscode/settings.json +++ b/tests/unit/.vscode/settings.json @@ -4,6 +4,7 @@ {"label": "JavaScript", "args": ["compile-js.hxml", "-cmd", "node -e \"require('./bin/unit.js').unit.TestMain.main()\""]}, {"label": "JVM", "args": ["compile-jvm-only.hxml", "-cmd", "java -jar bin/unit.jar"]}, {"label": "Interp", "args": ["compile-macro.hxml"]}, + {"label": "Lua", "args": ["compile-lua.hxml", "-cmd", "lua bin/unit.lua"]}, ], "[haxe]": { "editor.formatOnSave": true, @@ -16,4 +17,4 @@ "arguments": ["-v"] }, "haxe.enableCompilationServer": false -} \ No newline at end of file +} diff --git a/tests/unit/compile-each.hxml b/tests/unit/compile-each.hxml index e94d14c6..7995fca9 100644 --- a/tests/unit/compile-each.hxml +++ b/tests/unit/compile-each.hxml @@ -1,4 +1,4 @@ --D source-header='' +-D source-header= --debug -p src # -cp "C:\Program Files\The Haxe Effect\src/dev/null" @@ -7,6 +7,7 @@ --resource serializedValues.txt --macro Macro.init() --dce full --lib utest:git:https://github.com/haxe-utest/utest#559b24c9a36533281ba7a2eed8aab83ed6b872b4 +-lib utest -D analyzer-optimize -D analyzer-user-var-fusion +-D message-reporting=pretty \ No newline at end of file diff --git a/tests/unit/compile-flash9.hxml b/tests/unit/compile-flash.hxml similarity index 72% rename from tests/unit/compile-flash9.hxml rename to tests/unit/compile-flash.hxml index 5176f042..6396ab01 100644 --- a/tests/unit/compile-flash9.hxml +++ b/tests/unit/compile-flash.hxml @@ -4,7 +4,6 @@ compile-each.hxml --main unit.TestMain ---swf-version 11 --swf bin/unit9.swf +-swf bin/unit.swf -swf-lib native_swf/lib.swc --D network-sandbox \ No newline at end of file +-D network-sandbox diff --git a/tests/unit/compile-hl.hxml b/tests/unit/compile-hl.hxml index a1fff608..1b169b97 100644 --- a/tests/unit/compile-hl.hxml +++ b/tests/unit/compile-hl.hxml @@ -3,4 +3,4 @@ compile-each.hxml -hl bin/unit.hl #-D interp -D hl-check --D hl-ver=1.11.0 \ No newline at end of file +-D hl-ver=1.13.0 \ No newline at end of file diff --git a/tests/unit/compile-python.hxml b/tests/unit/compile-python.hxml index 3064476d..0c80eaf9 100644 --- a/tests/unit/compile-python.hxml +++ b/tests/unit/compile-python.hxml @@ -1,3 +1,12 @@ compile-each.hxml + +--each + --main unit.TestMain --python bin/unit.py \ No newline at end of file +-python bin/unit.py + +--next + +-D python-version=3.4 +--main unit.TestMain +-python bin/unit34.py diff --git a/tests/unit/compile.hxml b/tests/unit/compile.hxml index d661e19a..2c5775cb 100644 --- a/tests/unit/compile.hxml +++ b/tests/unit/compile.hxml @@ -11,7 +11,7 @@ compile-java-runner.hxml # targets ---next compile-flash9.hxml +--next compile-flash.hxml --next compile-js.hxml --next compile-hl.hxml --next compile-lua.hxml @@ -21,5 +21,5 @@ compile-java-runner.hxml --next compile-java.hxml --next compile-cs.hxml --next compile-python.hxml ---next compile-macro.hxml --next compile-cs-unsafe.hxml +--next compile-macro.hxml diff --git a/tests/unit/native_java/hxjava_build.txt b/tests/unit/native_java/hxjava_build.txt index 7eb4705c..83e2ee51 100644 --- a/tests/unit/native_java/hxjava_build.txt +++ b/tests/unit/native_java/hxjava_build.txt @@ -22,4 +22,8 @@ M haxe.UpperCasePackage.SomeClass C haxe.UpperCasePackage.SomeClass M haxe.UpperCasePackage.lowercase C haxe.UpperCasePackage.lowercase +M haxe.test.MyDefaultInterface +C haxe.test.MyDefaultInterface +M haxe.test.MyDefaultSubInterface +C haxe.test.MyDefaultSubInterface end modules diff --git a/tests/unit/native_java/src/haxe/test/MyDefaultInterface.java b/tests/unit/native_java/src/haxe/test/MyDefaultInterface.java new file mode 100644 index 00000000..f7175696 --- /dev/null +++ b/tests/unit/native_java/src/haxe/test/MyDefaultInterface.java @@ -0,0 +1,12 @@ +package haxe.test; +import java.lang.annotation.*; + +public interface MyDefaultInterface +{ + default public String test(String a) + { + return a.toUpperCase(); + } +} + +class MyDefaultClass implements MyDefaultInterface {} \ No newline at end of file diff --git a/tests/unit/native_java/src/haxe/test/MyDefaultSubInterface.java b/tests/unit/native_java/src/haxe/test/MyDefaultSubInterface.java new file mode 100644 index 00000000..469675f1 --- /dev/null +++ b/tests/unit/native_java/src/haxe/test/MyDefaultSubInterface.java @@ -0,0 +1,7 @@ +package haxe.test; +import java.lang.annotation.*; + +public interface MyDefaultSubInterface extends MyDefaultInterface +{ + +} \ No newline at end of file diff --git a/tests/unit/src/unit/HelperMacros.hx b/tests/unit/src/unit/HelperMacros.hx index c349a33b..b39cc709 100644 --- a/tests/unit/src/unit/HelperMacros.hx +++ b/tests/unit/src/unit/HelperMacros.hx @@ -48,11 +48,16 @@ class HelperMacros { return { pos: currentPos(), expr: haxe.macro.Expr.ExprDef.EConst(haxe.macro.Expr.Constant.CIdent(result)) }; } + @:access(haxe.macro.Error.childErrors) static public macro function typeErrorText(e:haxe.macro.Expr) { var result = try { typeof(e); null; - } catch (e:haxe.macro.Expr.Error) e.message; + } catch (e:haxe.macro.Expr.Error) { + var msg = e.message; + if (e.childErrors != null) for (c in e.childErrors) msg += "\n" + c.message; + msg; + } return { pos: currentPos(), expr: if (result == null) diff --git a/tests/unit/src/unit/TestArrowFunctions.hx b/tests/unit/src/unit/TestArrowFunctions.hx index 6b77367c..04b361a3 100644 --- a/tests/unit/src/unit/TestArrowFunctions.hx +++ b/tests/unit/src/unit/TestArrowFunctions.hx @@ -87,7 +87,7 @@ class TestArrowFunctions extends Test { f3_1 = (?a:Int, b:String) -> a + b.length; f3_2 = (a:Int, ?b:Int) -> a + b; - #if !(flash || hl) // Cannot skip not nullable argument + #if !flash // Cannot skip not nullable argument f3_1 = function (a=1, b:String) return a + b.length; eq(f3_1("--"),3); diff --git a/tests/unit/src/unit/TestBasetypes.hx b/tests/unit/src/unit/TestBasetypes.hx index f7be0942..c883bb11 100644 --- a/tests/unit/src/unit/TestBasetypes.hx +++ b/tests/unit/src/unit/TestBasetypes.hx @@ -274,6 +274,18 @@ class TestBasetypes extends Test { i[x++] += 4; eq(x, 2); eq(i[1], 4); + + eq(new Map().toString(), "[]"); + eq(new Map().toString(), "[]"); + eq(new Map().toString(), "[]"); + eq(new Map<{}, Int>().toString(), "[]"); + + eq([1 => 1].toString(), "[1 => 1]"); + eq(["foo" => 1].toString(), "[foo => 1]"); + eq([MyEnum.C(0, "foo") => 1].toString(), "[C(0,foo) => 1]"); + final objMapString = [{foo: 1} => 1].toString(); + eq(objMapString.charCodeAt(0), "[".code); + eq(objMapString.charCodeAt(objMapString.length - 1), "]".code); } function testObjectKeyword() { diff --git a/tests/unit/src/unit/TestDefaultTypeParameters.hx b/tests/unit/src/unit/TestDefaultTypeParameters.hx new file mode 100644 index 00000000..44fd5ad7 --- /dev/null +++ b/tests/unit/src/unit/TestDefaultTypeParameters.hx @@ -0,0 +1,44 @@ +package unit; + +import utest.Assert; + +using StringTools; + +private class DefaultTPClass_y {} +private class DefaultTPClass_yn {} +private class DefaultTPClass_ny {} +private class DefaultTPClass_yy {} + +class TestDefaultTypeParameters extends Test { + function test() { + t(HelperMacros.typeString((null : DefaultTPClass_y)).endsWith("DefaultTPClass_y")); + t(HelperMacros.typeString((null : DefaultTPClass_y)).endsWith("DefaultTPClass_y")); + t(HelperMacros.typeString((null : DefaultTPClass_yn)).endsWith("DefaultTPClass_yn")); + t(HelperMacros.typeString((null : DefaultTPClass_ny)).endsWith("DefaultTPClass_ny")); + t(HelperMacros.typeString((null : DefaultTPClass_ny)).endsWith("DefaultTPClass_ny")); + t(HelperMacros.typeString((null : DefaultTPClass_yy)).endsWith("DefaultTPClass_yy")); + t(HelperMacros.typeString((null : DefaultTPClass_yy)).endsWith("DefaultTPClass_yy")); + t(HelperMacros.typeString((null : DefaultTPClass_yy)).endsWith("DefaultTPClass_yy")); + } + + macro static function printThings() { + var pr = new haxe.macro.Printer(); + var tds = [ + macro class DefaultTPClass_y {}, + macro class DefaultTPClass_yn {}, + macro class DefaultTPClass_ny {}, + macro class DefaultTPClass_yy {}, + ]; + return macro $v{[for (td in tds) pr.printTypeDefinition(td).replace("\r", "").replace("\n", "")]}; + } + + function testPrinting() { + var expected = [ + "class DefaultTPClass_y {}", + "class DefaultTPClass_yn {}", + "class DefaultTPClass_ny {}", + "class DefaultTPClass_yy {}" + ]; + Assert.same(expected, printThings()); + } +} diff --git a/tests/unit/src/unit/TestExceptions.hx b/tests/unit/src/unit/TestExceptions.hx index 23ee1945..98fd5e13 100644 --- a/tests/unit/src/unit/TestExceptions.hx +++ b/tests/unit/src/unit/TestExceptions.hx @@ -242,6 +242,10 @@ class TestExceptions extends Test { var expected = null; var lineShift = 0; for(s in stacks) { + // TODO: fix hl vs other targets difference with callstacks + // See https://github.com/HaxeFoundation/haxe/issues/10926 + #if hl @:privateAccess s.asArray().shift(); #end + if(expected == null) { expected = stackItemData(s[0]); } else { diff --git a/tests/unit/src/unit/TestGADT.hx b/tests/unit/src/unit/TestGADT.hx index fb8948cc..d5a3741f 100644 --- a/tests/unit/src/unit/TestGADT.hx +++ b/tests/unit/src/unit/TestGADT.hx @@ -6,18 +6,18 @@ enum Constant { CFloat(s:String):Constant; } -enum Binop { - OpAdd:Binop; - OpEq:Binop; +enum Binop { + OpAdd:Binop; + OpEq:Binop; } enum Expr { EConst(c:Constant):Expr; - EBinop(op:Binop, e1:Expr, e2:Expr):Expr; + EBinop + (op : Binop, e1 : Expr, e2 : Expr) : Expr; } class TestGADT extends Test { - function testBasic() { var ti = 1.22; var tb = false; @@ -26,22 +26,23 @@ class TestGADT extends Test { var e2 = EConst(CFloat("8")); var e3 = EConst(CFloat("12")); - var eadd = EBinop(OpAdd,e1,e2); + var eadd = EBinop(OpAdd, e1, e2); var s = eval(eadd); HelperMacros.typedAs(s, ti); - eq(s,20); + eq(s, 20); - var eeq = EBinop(OpEq,e1,e2); + var eeq = EBinop(OpEq, e1, e2); var s = eval(eeq); HelperMacros.typedAs(s, tb); - eq(s,false); + eq(s, false); - var eeq = EBinop(OpEq,e1,e3); + var eeq = EBinop(OpEq, e1, e3); var s = eval(eeq); HelperMacros.typedAs(s, tb); - eq(s,true); + eq(s, true); } + @:haxe.warning("-WGenerator") static function evalConst(c:Constant):T { return switch (c) { case CString(s): s; @@ -50,17 +51,18 @@ class TestGADT extends Test { } } - static function evalBinop(op:Binop, e1:Expr, e2:Expr):T { - return switch(op) { + @:haxe.warning("-WGenerator") + static function evalBinop(op:Binop, e1:Expr, e2:Expr):T { + return switch (op) { case OpAdd: eval(e1) + eval(e2); case OpEq: eval(e1) == eval(e2); } } static function eval(e:Expr):T { - return switch(e) { + return switch (e) { case EConst(c): evalConst(c); - case EBinop(_op,_e1,_e2): evalBinop(_op,_e1,_e2); // TODO: this generates some unused variable warnings in macro context (issue #1675?) + case EBinop(_op, _e1, _e2): evalBinop(_op, _e1, _e2); // TODO: this generates some unused variable warnings in macro context (issue #1675?) } } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/TestGeneric.hx b/tests/unit/src/unit/TestGeneric.hx index 5c1f6bde..d6079ca1 100644 --- a/tests/unit/src/unit/TestGeneric.hx +++ b/tests/unit/src/unit/TestGeneric.hx @@ -8,6 +8,7 @@ private typedef MyAnon = { @:generic class MyGeneric { public var t:T; + public function new(t:T) { this.t = t; } @@ -15,25 +16,28 @@ class MyGeneric { class MyRandomClass { public var s:String; + public function new(s:String) { this.s = s; } } -class MyRandomEmptyClass { } +class MyRandomEmptyClass {} @:generic class RBNode> { - public var rbLeft : T; - public var rbRight : T; + public var rbLeft:T; + public var rbRight:T; } @:generic class RBTree> { - public var root : T; - public function new() { } + public var root:T; + + public function new() {} } class MyData extends RBNode { - var id : Int; + var id:Int; + public function new(id:Int) { this.id = id; } @@ -46,7 +50,7 @@ class TestGeneric extends Test { t((mg.t is Int)); var mg = new MyGeneric("12"); - eq(mg.t,"12"); + eq(mg.t, "12"); t((mg.t is String)); } @@ -64,6 +68,7 @@ class TestGeneric extends Test { eq(a.t.b, null); } + @:haxe.warning("-WGenerator") function testGenericFn() { var a = new MyGenericInt>(function(i:Int):Int return i * i); eq(4, a.t(2)); diff --git a/tests/unit/src/unit/TestHttp.hx b/tests/unit/src/unit/TestHttp.hx index 2a306eaf..cc6db171 100644 --- a/tests/unit/src/unit/TestHttp.hx +++ b/tests/unit/src/unit/TestHttp.hx @@ -27,7 +27,7 @@ class TestHttp extends Test { return; } test(); - #elseif (github && (hl || java || (flash && (Linux || Mac)) || (cs && Windows))) + #elseif (github && (hl || java || (flash && (Linux || Mac || Windows)) || (cs && Windows))) noAssert(); async.done(); return; diff --git a/tests/unit/src/unit/TestInt64.hx b/tests/unit/src/unit/TestInt64.hx index 8912739e..a49e1be4 100644 --- a/tests/unit/src/unit/TestInt64.hx +++ b/tests/unit/src/unit/TestInt64.hx @@ -1,8 +1,9 @@ package unit; -using haxe.Int64; import haxe.Int64.*; +using haxe.Int64; + class TestInt64 extends Test { public function testMake() { @@ -13,6 +14,14 @@ class TestInt64 extends Test { eq( a.high, 10 ); eq( a.low, 0xFFFFFFFF ); + a = 47244640255i64; + eq( a.high, 10 ); + eq( a.low, 0xFFFFFFFF ); + + a = 0x7FFFFFFFFFFFFFFFi64; + eq( a.high, 0x7FFFFFFF ); + eq( a.low, 0xFFFFFFFF ); + // Int casts a = 1; eq( a.toInt(), 1 ); @@ -53,20 +62,19 @@ class TestInt64 extends Test { // This fails because the first division fails: var ten = haxe.Int64.make(0, 10); - var modulus = haxe.Int64.divMod( z, ten ).modulus.low; - eq( modulus, 0 ); + var modulus = haxe.Int64.divMod(z, ten).modulus.low; + eq(modulus, 0); // The first division failed because of negate: - eq( (-z).low, -2147483648 ); - eq( (-z).high, 2147483647 ); + eq((-z).low, -2147483648); + eq((-z).high, 2147483647); } // Some tests of how it generates Int64 when used as Null or as type parameters - function testGen() - { + function testGen() { var arr:Array = []; arr.push(1); - arr.push(Int64.make(0xFFFFFFFF,0x80000000)); + arr.push(Int64.make(0xFFFFFFFF, 0x80000000)); eq(arr[0].high, 0); eq(arr[0].low, 1); eq(arr[1].high, 0xFFFFFFFF); @@ -81,59 +89,62 @@ class TestInt64 extends Test { eq(n.low, 0xefefefef); } - function tryOverflow( a : Int64 ) - { + function tryOverflow(a:Int64) { a.toInt(); } public function testIncrement() { - var a : Int64, b : Int64, c : Int64; + var a:Int64, b:Int64, c:Int64; // Int64 should act as a value type and be immutable. // Increment ops should swap `this` to a new Int64 object. - a = 0; b = a; + a = 0; + b = a; a++; f(a == b); - a = 0; b = a; + a = 0; + b = a; ++a; f(a == b); - a = 0; b = a; + a = 0; + b = a; a--; f(a == b); - a = 0; b = a; + a = 0; + b = a; --a; f(a == b); - a = Int64.make(0,0xFFFFFFFF); + a = Int64.make(0, 0xFFFFFFFF); b = a; - c = Int64.make(1,0); - int64eq( a++, b ); - int64eq( a--, c ); - int64eq( ++a, c ); - int64eq( --a, b ); + c = Int64.make(1, 0); + int64eq(a++, b); + int64eq(a--, c); + int64eq(++a, c); + int64eq(--a, b); } public function testToString() { - var a : Int64; + var a:Int64; // Issue #903 a = 0; - eq( Std.string( a ), "0" ); + eq(Std.string(a), "0"); a = 156; - eq( '$a', "156" ); + eq('$a', "156"); a = -1; - eq( '$a', "-1" ); + eq('$a', "-1"); - a = Int64.make(0xFFFFFFFE,0); - eq( a.toStr(), "-8589934592" ); + a = Int64.make(0xFFFFFFFE, 0); + eq(a.toStr(), "-8589934592"); - a = Int64.make(1,1); - eq( a.toStr(), "4294967297" ); + a = Int64.make(1, 1); + eq(a.toStr(), "4294967297"); // set a to 2^63 (overflows to the smallest negative number) a = Int64.ofInt(2); @@ -141,259 +152,273 @@ class TestInt64 extends Test { a = Int64.mul(a, 2); } - eq( Int64.add(a, -1).toStr(), "9223372036854775807" ); // largest positive - eq( Int64.add(a, 1).toStr(), "-9223372036854775807" ); // smallest negative - 1 - eq( a.toStr(), "-9223372036854775808" ); // smallest negative + eq(Int64.add(a, -1).toStr(), "9223372036854775807"); // largest positive + eq(Int64.add(a, 1).toStr(), "-9223372036854775807"); // smallest negative - 1 + eq(a.toStr(), "-9223372036854775808"); // smallest negative } public function testComparison() { - var a : Int64, b : Int64; - a = 1; b = 1; - t( a == b ); f( a != b ); - t( a <= b ); f( a < b ); - t( a >= b ); f( a > b ); - eq( a.compare(b), 0 ); - eq( a.ucompare(b), 0 ); + var a:Int64, b:Int64; + a = 1; + b = 1; + t(a == b); + f(a != b); + t(a <= b); + f(a < b); + t(a >= b); + f(a > b); + eq(a.compare(b), 0); + eq(a.ucompare(b), 0); a = -10; b = -15; - f( a == b ); t( a != b ); - f( a <= b ); f( a < b ); - t( a >= b ); t( a > b ); - t( a.compare(b) > 0 ); - t( a.ucompare(b) > 0 ); - - a = Int64.make(0,0); - b = Int64.make(1,0); - f( a == b ); t( a != b ); - t( a <= b ); t( a < b ); - f( a >= b ); f( a > b ); - t( a.compare(b) < 0 ); - t( a.ucompare(b) < 0 ); - - a = Int64.make(0x0FFFFFFF,0xFFFFFFFF); - b = Int64.make(0x80000000,0x80000000); - f( a == b ); t( a != b ); - f( a <= b ); f( a < b ); - t( a >= b ); t( a > b ); - t( a.compare(b) > 0 ); - t( a.ucompare(b) < 0 ); + f(a == b); + t(a != b); + f(a <= b); + f(a < b); + t(a >= b); + t(a > b); + t(a.compare(b) > 0); + t(a.ucompare(b) > 0); + + a = Int64.make(0, 0); + b = Int64.make(1, 0); + f(a == b); + t(a != b); + t(a <= b); + t(a < b); + f(a >= b); + f(a > b); + t(a.compare(b) < 0); + t(a.ucompare(b) < 0); + + a = Int64.make(0x0FFFFFFF, 0xFFFFFFFF); + b = Int64.make(0x80000000, 0x80000000); + f(a == b); + t(a != b); + f(a <= b); + f(a < b); + t(a >= b); + t(a > b); + t(a.compare(b) > 0); + t(a.ucompare(b) < 0); // Issue #2317 a = Int64.make(0xA, 0x828D97A8); b = 0; - t( a.compare(b) > 0 ); - t( a.ucompare(b) > 0 ); + t(a.compare(b) > 0); + t(a.ucompare(b) > 0); // Issue #2090 a = 0; b = Int64.make(320, -2138504556); - t( a.compare(b) < 0 ); - t( a.ucompare(b) < 0 ); - - + t(a.compare(b) < 0); + t(a.ucompare(b) < 0); } public function testAddition() { - var a : Int64, b : Int64; + var a:Int64, b:Int64; - a = Int64.make(0,0x9E370301); - b = Int64.make(0,0xB0590000); - int64eq( a+b, Int64.make(0x1,0x4E900301) ); + a = Int64.make(0, 0x9E370301); + b = Int64.make(0, 0xB0590000); + int64eq(a + b, Int64.make(0x1, 0x4E900301)); // negation b = -a; - int64eq( a+b, 0 ); + int64eq(a + b, 0); // Int64+Int - int64eq( a+12345678, Int64.make(0,0x9EF3644F) ); + int64eq(a + 12345678, Int64.make(0, 0x9EF3644F)); // Int+Int64 - int64eq( 0xF0002222+a, Int64.make(0,0x8E372523) ); + int64eq(0xF0002222 + a, Int64.make(0, 0x8E372523)); } public function testSubtraction() { - var a : Int64, b : Int64; + var a:Int64, b:Int64; - a = Int64.make(1,0); - b = Int64.make(0,1); - int64eq( a-b, Int64.make(0,0xFFFFFFFF) ); + a = Int64.make(1, 0); + b = Int64.make(0, 1); + int64eq(a - b, Int64.make(0, 0xFFFFFFFF)); b = a; - int64eq( a-b, 0 ); + int64eq(a - b, 0); - b = Int64.make(0xFFFFFFFE,1); - int64eq( a-b, Int64.make(2,0xFFFFFFFF) ); + b = Int64.make(0xFFFFFFFE, 1); + int64eq(a - b, Int64.make(2, 0xFFFFFFFF)); // Int64-Int - int64eq( a-12345678, Int64.make(0,0xFF439EB2) ); + int64eq(a - 12345678, Int64.make(0, 0xFF439EB2)); // Int-Int64 - int64eq( 12345678-a, Int64.make(0xFFFFFFFF,0x00BC614E) ); + int64eq(12345678 - a, Int64.make(0xFFFFFFFF, 0x00BC614E)); } public function testMultiplication() { - var a : Int64, b : Int64; + var a:Int64, b:Int64; a = Int64.make(0, 0x239B0E13); b = Int64.make(0, 0x39193D1B); - int64eq( a*b, Int64.make(0x07F108C6,0x4E900301) ); + int64eq(a * b, Int64.make(0x07F108C6, 0x4E900301)); a = Int64.make(0, 0xD3F9C9F4); b = Int64.make(0, 0xC865C765); - int64eq( a*b, Int64.make(0xA5EF6C6E,0x1ACD5944) ); + int64eq(a * b, Int64.make(0xA5EF6C6E, 0x1ACD5944)); var a = Int64.make(0xFFF21CDA, 0x972E8BA3); var b = Int64.make(0x0098C29B, 0x81000001); - int64eq( a*b, Int64.make(0xDDE8A2E8, 0xBA2E8BA3) ); + int64eq(a * b, Int64.make(0xDDE8A2E8, 0xBA2E8BA3)); var a = Int64.make(0x81230000, 0x81230000); var b = Int64.make(0x90909090, 0x90909090); - int64eq( a*b, Int64.make(0xF04C9C9C,0x53B00000) ); + int64eq(a * b, Int64.make(0xF04C9C9C, 0x53B00000)); var a = Int64.make(0x00001000, 0x0020020E); var b = Int64.make(0xBEDBEDED, 0xDEBDEBDE); - int64eq( a*b, Int64.make(0xC45C967D,0x25FAA224) ); + int64eq(a * b, Int64.make(0xC45C967D, 0x25FAA224)); // Issue #1532 a = Int64.make(0xFFF21CDA, 0x972E8BA3); b = Int64.make(0x0098C29B, 0x81000001); - int64eq( a*b, Int64.make(0xDDE8A2E8, 0xBA2E8BA3) ); + int64eq(a * b, Int64.make(0xDDE8A2E8, 0xBA2E8BA3)); // Int64*Int a = Int64.make(0x01000000, 0x11131111); - int64eq( a*7, Int64.make(0x07000000,0x77857777) ); + int64eq(a * 7, Int64.make(0x07000000, 0x77857777)); // Int*Int64 a = Int64.make(0x91111111, 0x11111111); - int64eq( 2*a, Int64.make(0x22222222,0x22222222) ); + int64eq(2 * a, Int64.make(0x22222222, 0x22222222)); } public function testDivision() { - var a : Int64, b : Int64; + var a:Int64, b:Int64; a = Int64.make(0x00002342, 0xDDEF3421); b = Int64.make(0x00000001, 0x00002000); - int64eq( a/b, 9026 ); - int64eq( a%b, Int64.make(0,0xD986F421) ); + int64eq(a / b, 9026); + int64eq(a % b, Int64.make(0, 0xD986F421)); var result = a.divMod(b); - int64eq( a/b, result.quotient ); - int64eq( a%b, result.modulus ); + int64eq(a / b, result.quotient); + int64eq(a % b, result.modulus); a = Int64.make(0xF0120AAA, 0xAAAAAAA0); b = Int64.make(0x00020001, 0x0FF02000); - int64eq( a/b, -2038 ); - int64eq( a%b, Int64.make(0xFFFE131F,0x8C496AA0) ); + int64eq(a / b, -2038); + int64eq(a % b, Int64.make(0xFFFE131F, 0x8C496AA0)); result = a.divMod(b); - int64eq( a/b, result.quotient ); - int64eq( a%b, result.modulus ); + int64eq(a / b, result.quotient); + int64eq(a % b, result.modulus); a = Int64.make(0, 2); b = Int64.make(0xFFFFFFFF, 0xFAFAFAFA); - int64eq( a/b, 0 ); - int64eq( a%b, 2 ); + int64eq(a / b, 0); + int64eq(a % b, 2); result = a.divMod(b); - int64eq( a/b, result.quotient ); - int64eq( a%b, result.modulus ); + int64eq(a / b, result.quotient); + int64eq(a % b, result.modulus); a = Int64.make(0x7ABADDAD, 0xDEADBEEF); b = Int64.make(0xFFFFFFF1, 0x1FFFFFFF); - int64eq( a/b, Int64.make(0xFFFFFFFF,0xF7BFCEAE) ); - int64eq( a%b, Int64.make(0x0000000A,0x166D8D9D) ); + int64eq(a / b, Int64.make(0xFFFFFFFF, 0xF7BFCEAE)); + int64eq(a % b, Int64.make(0x0000000A, 0x166D8D9D)); result = a.divMod(b); - int64eq( a/b, result.quotient ); - int64eq( a%b, result.modulus ); + int64eq(a / b, result.quotient); + int64eq(a % b, result.modulus); a = Int64.make(0x81234567, 0xFDECBA98); b = Int64.make(0xFFFFFEFF, 0xEEEEEEEE); - int64eq( a/b, 0x007ED446 ); - int64eq( a%b, Int64.make(0xFFFFFFF5,0x31964D84) ); + int64eq(a / b, 0x007ED446); + int64eq(a % b, Int64.make(0xFFFFFFF5, 0x31964D84)); result = a.divMod(b); - int64eq( a/b, result.quotient ); - int64eq( a%b, result.modulus ); + int64eq(a / b, result.quotient); + int64eq(a % b, result.modulus); // Int64/Int - int64eq( a/2, Int64.make(0xC091A2B3,0xFEF65D4C) ); - int64eq( a%100, -68 ); + int64eq(a / 2, Int64.make(0xC091A2B3, 0xFEF65D4C)); + int64eq(a % 100, -68); // Int/Int64 - int64eq( 10001/a, 0 ); - int64eq( 515151%a, 515151 ); + int64eq(10001 / a, 0); + int64eq(515151 % a, 515151); } public function testBinaryOps() { - var a : Int64, b : Int64; + var a:Int64, b:Int64; a = haxe.Int64.make(0x0FFFFFFF, 0x00000001); b = haxe.Int64.make(0, 0x8FFFFFFF); - int64eq( a&b, 1 ); - int64eq( a|b, Int64.make(0x0FFFFFFF, 0x8FFFFFFF) ); - int64eq( a^b, Int64.make(0x0FFFFFFF, 0x8FFFFFFE) ); - int64eq( ~a, Int64.make(0xF0000000, 0xFFFFFFFE) ); + int64eq(a & b, 1); + int64eq(a | b, Int64.make(0x0FFFFFFF, 0x8FFFFFFF)); + int64eq(a ^ b, Int64.make(0x0FFFFFFF, 0x8FFFFFFE)); + int64eq(~a, Int64.make(0xF0000000, 0xFFFFFFFE)); } public function testShifts() { - var a : Int64; + var a:Int64; a = 1 << 20; - int64eq( a, 0x100000 ); + int64eq(a, 0x100000); a <<= 20; - int64eq( a, Int64.make(0x100,0x00000000) ); + int64eq(a, Int64.make(0x100, 0x00000000)); a = -1; a >>= 4; - int64eq( a, -1 ); + int64eq(a, -1); a >>>= 4; - int64eq( a, Int64.make(0x0FFFFFFF,0xFFFFFFFF) ); + int64eq(a, Int64.make(0x0FFFFFFF, 0xFFFFFFFF)); // Ensure proper overflow behavior for shift operand - a = Int64.make(1,1); - int64eq( a << 0, a ); - int64eq( a >> 0, a ); - int64eq( a >>> 0, a ); - int64eq( a << 64, a ); - int64eq( a >> 64, a ); - int64eq( a >>> 64, a ); + a = Int64.make(1, 1); + int64eq(a << 0, a); + int64eq(a >> 0, a); + int64eq(a >>> 0, a); + int64eq(a << 64, a); + int64eq(a >> 64, a); + int64eq(a >>> 64, a); } /** Tests that we have all of the classic Int64 interface. */ public function testBackwardsCompat() { - var a : Int64 = 32.ofInt(); - var b : Int64 = (-4).ofInt(); - - f( a.eq(b) ); - t( a.neq(b) ); - int64eq( a.add(b), 28 ); - int64eq( a.sub(b), 36 ); - int64eq( a.div(b), -8 ); - int64eq( a.mod(b), 0 ); - int64eq( b.shl(1), -8 ); - int64eq( b.shr(1), -2 ); - int64eq( b.ushr(1), Int64.make(0x7FFFFFFF,0xFFFFFFFE) ); - int64eq( a.and(b), 32 ); - int64eq( a.or(b), -4 ); - int64eq( a.xor(b), -36 ); - int64eq( a.neg(), -32 ); - f( a.isNeg() ); t( b.isNeg() ); - f( a.isZero() ); f( b.isZero() ); - t( a.compare(b) > 0 ); - t( a.ucompare(b) < 0 ); - int64eq( a.toInt(), 32 ); - int64eq( b.toInt(), -4 ); + var a:Int64 = 32.ofInt(); + var b:Int64 = (-4).ofInt(); + + f(a.eq(b)); + t(a.neq(b)); + int64eq(a.add(b), 28); + int64eq(a.sub(b), 36); + int64eq(a.div(b), -8); + int64eq(a.mod(b), 0); + int64eq(b.shl(1), -8); + int64eq(b.shr(1), -2); + int64eq(b.ushr(1), Int64.make(0x7FFFFFFF, 0xFFFFFFFE)); + int64eq(a.and(b), 32); + int64eq(a.or(b), -4); + int64eq(a.xor(b), -36); + int64eq(a.neg(), -32); + f(a.isNeg()); + t(b.isNeg()); + f(a.isZero()); + f(b.isZero()); + t(a.compare(b) > 0); + t(a.ucompare(b) < 0); + int64eq(a.toInt(), 32); + int64eq(b.toInt(), -4); } - public function testCapture() - { - var a = Int64.make(0xFF00FF00,0xF0F0F0F0), - b = Int64.make(0xFF00FF00,0xF0F0F0F0); + public function testCapture() { + var a = Int64.make(0xFF00FF00, 0xF0F0F0F0), + b = Int64.make(0xFF00FF00, 0xF0F0F0F0); eq(a.compare(b), 0); eq(a.high, 0xFF00FF00); - function test() return Int64.compare(a,Int64.make(0xFF00FF00,0xF0F0F0F0)); - eq(test(),0); - function testSet(v:Int64) b = v; - testSet( make(0xFF00FF00, 0xFF0) ); - eq(b.compare(make(0xFF00FF00,0xFF0)),0); + function test() + return Int64.compare(a, Int64.make(0xFF00FF00, 0xF0F0F0F0)); + eq(test(), 0); + function testSet(v:Int64) + b = v; + testSet(make(0xFF00FF00, 0xFF0)); + eq(b.compare(make(0xFF00FF00, 0xFF0)), 0); eq(b.high, 0xFF00FF00); } @@ -401,29 +426,28 @@ class TestInt64 extends Test { var a = Int64.make(0, 0x239B0E13); var b = Int64.make(0, 0x39193D1B); var c = Int64.mul(a, b); - eq( c.toStr(), "572248275467371265" ); + eq(c.toStr(), "572248275467371265"); eq(Int64.toStr(c), "572248275467371265"); var a = Int64.make(0, 0xD3F9C9F4); var b = Int64.make(0, 0xC865C765); var c = Int64.mul(a, b); - eq( c.toStr(), "-6489849317865727676" ); + eq(c.toStr(), "-6489849317865727676"); var a = Int64.make(0, 0x9E370301); var b = Int64.make(0, 0xB0590000); var c = Int64.add(a, b); - eq( Int64.toStr(c), "5613028097" ); + eq(Int64.toStr(c), "5613028097"); var a = Int64.make(0xFFF21CDA, 0x972E8BA3); var b = Int64.make(0x0098C29B, 0x81000001); var c = Int64.mul(a, b); var expected = Int64.make(0xDDE8A2E8, 0xBA2E8BA3); - eq( expected.compare(c), 0 ); + eq(expected.compare(c), 0); } public function testCompare() { - var a = ofInt(2), - b = ofInt(3); + var a = ofInt(2), b = ofInt(3); t(a == a); t(b == b); eq(a.compare(a), 0); @@ -432,47 +456,43 @@ class TestInt64 extends Test { } public function testBits() { - var x = make(0xfedcba98,0x76543210); + var x = make(0xfedcba98, 0x76543210); var y = x.and((ofInt(0xffff))), z = x.or((ofInt(0xffff))), - w = x.xor((make(0xffffffff,0xffffffff))); + w = x.xor((make(0xffffffff, 0xffffffff))); eq(y.toStr(), '12816'); eq(z.toStr(), '-81985529216434177'); eq(w.toStr(), '81985529216486895'); eq(x.and(ofInt(0xffff)).toStr(), '12816'); eq((x.or(ofInt(0xffff))).toStr(), '-81985529216434177'); eq((x.xor(ofInt(0xffff))).toStr(), '-81985529216446993'); - eq((x.and(make(0x1,0xffffffff))).toStr(), '1985229328'); - eq((x.or(make(0x1,0xffffffff))).toStr(), '-81985522611781633'); + eq((x.and(make(0x1, 0xffffffff))).toStr(), '1985229328'); + eq((x.or(make(0x1, 0xffffffff))).toStr(), '-81985522611781633'); eq((x.xor(make(0x1, 0xffffffff))).toStr(), '-81985524597010961'); - var a = ofInt(7), - b = a.shl(1); + var a = ofInt(7), b = a.shl(1); eq(b.toStr(), '14'); } public function testAdd() { - var a = ofInt(3), - b = ofInt(2), - c = make(0xffffffff,0xfffffffe); - eq( (a.add(b)).compare(ofInt(5)), 0 ); - eq( (a.add(ofInt(4))).compare(ofInt(7)), 0 ); - eq( (c.add(ofInt(3))).compare(ofInt(1)), 0 ); + var a = ofInt(3), b = ofInt(2), c = make(0xffffffff, 0xfffffffe); + eq((a.add(b)).compare(ofInt(5)), 0); + eq((a.add(ofInt(4))).compare(ofInt(7)), 0); + eq((c.add(ofInt(3))).compare(ofInt(1)), 0); // numbers larger than int32 - eq( a.add(make(0x1, 0)).toStr(), '4294967299'); + eq(a.add(make(0x1, 0)).toStr(), '4294967299'); } public function testNeg() { - eq(Std.string(ofInt(-1)),Std.string(neg(ofInt(1)))); - eq(Std.string(ofInt(-100)),Std.string(neg(ofInt(100)))); + eq(Std.string(ofInt(-1)), Std.string(neg(ofInt(1)))); + eq(Std.string(ofInt(-100)), Std.string(neg(ofInt(100)))); eq(Std.string(make(-2147483648, 1)), Std.string(neg(make(2147483647, -1)))); // -9223372036854775807 == neg(9223372036854775807) } - function int64eq( v : Int64, v2 : Int64, ?pos ) { + function int64eq(v:Int64, v2:Int64, ?pos) { t(v == v2); } - public function testParseString() - { + public function testParseString() { for (v in ["0", "1", "-1", "9223372036854775807", "-9223372036854775807"]) { eq(Std.string(parseString(v)), v); } @@ -480,94 +500,131 @@ class TestInt64 extends Test { // trims the string: eq("-23", Std.string(parseString(" -23 "))); - // overflow and underflow raise exceptions: - try - { + try { parseString("9223372036854775808"); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } - try - { + try { parseString("-9223372036854775809"); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } - try - { + try { parseString("--1"); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } - try - { + try { parseString("asd1"); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } - try - { + try { parseString("1asdf"); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } } - public function testFromFloat() - { + public function testFromFloat() { for (v in [0.0, 1.0, -1.0, 9007199254740991, -9007199254740991]) { eq(Std.parseFloat(Std.string(fromFloat(v))), v); } - try - { + try { fromFloat(9007199254740992); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } - try - { + try { fromFloat(-9007199254740992); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } var nan = Math.NaN; - try - { + try { fromFloat(nan); f(true); - } - catch (e:Dynamic) - { + } catch (e:Dynamic) { // fine } } -} \ No newline at end of file + + static function toHex(v:haxe.Int64) { + return "0x" + (v.high == 0 ? StringTools.hex(v.low) : StringTools.hex(v.high) + StringTools.hex(v.low, 8)); + } + + function testNicolas() { + inline function check(a:haxe.Int64, str:String) { + eq(toHex(a), str); + } + + check(33, "0x21"); + check(-8, "0xFFFFFFFFFFFFFFF8"); + var x = haxe.Int64.make(0x87654321, 0xABCDEF99); + check(x, "0x87654321ABCDEF99"); + var y = haxe.Int64.make(0xABCDEF99, 0x87654321); + var small = haxe.Int64.make(0x3, 0x87654321); + check(x + x, "0xECA8643579BDF32"); + check(x * x, "0xF6889F3201490971"); + check(x + y, "0x333332BB333332BA"); + check(x - y, "0xDB9753882468AC78"); + check(x / small, "0xFFFFFFFFDDD2DC89"); + check(x % small, "0xFFFFFFFFA2DAA6F0"); + check(-x, "0x789ABCDE54321067"); + check(small / 255, "0x38AF033"); + check(small % 255, "0x54"); + check(x << 1, "0xECA8643579BDF32"); + check(x << 33, "0x579BDF3200000000"); + check(x >> 8, "0xFF87654321ABCDEF"); + check(x >> 40, "0xFFFFFFFFFF876543"); + check(x >>> 40, "0x876543"); + check(x & y, "0x8345430183454301"); + check(x | y, "0xAFEDEFB9AFEDEFB9"); + check(x ^ y, "0x2CA8ACB82CA8ACB8"); + x++; + check(x, "0x87654321ABCDEF9A"); + x--; + check(x, "0x87654321ABCDEF99"); + check(~x, "0x789ABCDE54321066"); + t(x == x); + f(x == y); + + t(y >= x); + f(x >= y); + t(x >= x); + t(x + 1 >= x); + f(x - 1 >= x); + t(y > x); + f(x > y); + t(x + 1 > x); + f(x - 1 > x); + f(x > x); + + f(y <= x); + t(x <= y); + t(x <= x); + f(x + 1 <= x); + t(x - 1 <= x); + f(y < x); + t(x < y); + f(x + 1 < x); + t(x - 1 < x); + f(x < x); + } +} diff --git a/tests/unit/src/unit/TestJava.hx b/tests/unit/src/unit/TestJava.hx index 207043b0..02a7d9cf 100644 --- a/tests/unit/src/unit/TestJava.hx +++ b/tests/unit/src/unit/TestJava.hx @@ -1,36 +1,40 @@ package unit; + import haxe.io.Bytes; -import haxe.test.Base; -import haxe.test.MyClass; import haxe.test.Base.Base_InnerClass; import haxe.test.Base.Base___InnerClass3__; import haxe.test.Base.Base___InnerClass3___InnerClass4__; -import haxe.test.TEnum; +import haxe.test.Base; import haxe.test.LowerCaseClass; +import haxe.test.MyClass; +import haxe.test.MyDefaultInterface; +import haxe.test.MyDefaultSubInterface; +import haxe.test.TEnum; import java.util.EnumSet; import java.vm.*; #if java -@:strict(haxe.test.MyClass.MyClass_MyAnnotation({ author:"John Doe", someEnum: TB })) +@:strict(haxe.test.MyClass.MyClass_MyAnnotation({author: "John Doe", someEnum: TB})) @:strict(MyClass_ParameterLessAnnotation) -class TestJava extends Test -{ - function testException() - { - var native = new NativeClass(); - var hx:NativeClass = new HxClass(); - - exc(function() try native.excTest() catch (e:Dynamic) throw e); - var dyn:Dynamic = native; - exc(dyn.excTest); - - try - hx.excTest() - catch(e:Dynamic) throw e; //shouldn't throw any exception - } - - public function testIssue2964() - { +class TestJava extends Test { + function testException() { + var native = new NativeClass(); + var hx:NativeClass = new HxClass(); + + exc(function() try + native.excTest() + catch (e:Dynamic) + throw e); + var dyn:Dynamic = native; + exc(dyn.excTest); + + try + hx.excTest() + catch (e:Dynamic) + throw e; // shouldn't throw any exception + } + + public function testIssue2964() { var cl = new MyClass(); var bbool:java.lang.Boolean = null; eq(cl.boolTest1(bbool), 100); @@ -46,42 +50,40 @@ class TestJava extends Test var i:java.lang.Integer = null; eq(cl.intTest1(i), 100); - eq(cl.intTest1(cast(-1, java.lang.Integer)),-1); - eq(cl.intTest1(cast(1000, java.lang.Integer)),1000); + eq(cl.intTest1(cast(-1, java.lang.Integer)), -1); + eq(cl.intTest1(cast(1000, java.lang.Integer)), 1000); i = -1; eq(cl.intTest1(i), -1); i = null; eq(cl.intTest2(i), 100); - eq(cl.intTest2(-1),-1); - eq(cl.intTest2(1000),1000); + eq(cl.intTest2(-1), -1); + eq(cl.intTest2(1000), 1000); i = -1; eq(cl.intTest2(i), -1); var i:java.lang.Long = null; eq(cl.longTest(i), 100); - eq(cl.longTest(haxe.Int64.ofInt(-1)),-1); - eq(cl.longTest(haxe.Int64.ofInt(1000)),1000); + eq(cl.longTest(haxe.Int64.ofInt(-1)), -1); + eq(cl.longTest(haxe.Int64.ofInt(1000)), 1000); i = 10; eq(cl.longTest(i), 10); } - public function testVarClash() - { + public function testVarClash() { var ic = new Base_InnerClass2(); eq(ic.varNameClash2(), 1); - eq(ic.varNameClash2(2),2.2); + eq(ic.varNameClash2(2), 2.2); var iface:Base_VarNameClash = ic; eq(iface.varNameClash2(), 1); - eq(iface.varNameClash2(2),2.2); + eq(iface.varNameClash2(2), 2.2); var base:Base = ic; - eq(base.varNameClash2,0); + eq(base.varNameClash2, 0); base.varNameClash2 = 2; - eq(base.varNameClash2,2); + eq(base.varNameClash2, 2); } - @:strict(MyClass_MyAnnotation({ author:"author", currentRevision: 2 })) - public function testAnnotations() - { + @:strict(MyClass_MyAnnotation({author: "author", currentRevision: 2})) + public function testAnnotations() { var cl = java.Lib.toNativeType(TestJava); var a = cl.getAnnotation(java.Lib.toNativeType(MyClass_MyAnnotation)); t(a != null); @@ -89,7 +91,7 @@ class TestJava extends Test eq(a.someEnum(), TB); eq(a.currentRevision(), 1); t(cl.getAnnotation(java.Lib.toNativeType(MyClass_ParameterLessAnnotation)) != null); - var m = cl.getMethod("testAnnotations", new java.NativeArray(0)); + var m = cl.getMethod("testAnnotations"); a = m.getAnnotation(java.Lib.toNativeType(MyClass_MyAnnotation)); t(a != null); eq(a.author(), "author"); @@ -97,20 +99,17 @@ class TestJava extends Test eq(a.currentRevision(), 2); } - function testLowerCase() - { + function testLowerCase() { var l = new LowerCaseClass(); t(l.works); } - function testNameCase() - { + function testNameCase() { t(haxe.uppercasepackage.SomeClass.SomeClassFound); t(haxe.uppercasepackage.Lowercase.lowercaseFound); } - function testEnumSet() - { + function testEnumSet() { var es1:EnumSet = EnumSet.noneOf(java.Lib.toNativeEnum(TEnum)); f(es1.contains(TA)); es1.add(TA); @@ -121,8 +120,7 @@ class TestJava extends Test // f(es2.contains(HC)); } - function testHaxeKeywords() - { + function testHaxeKeywords() { eq(Base._inline, 42); eq(Base.callback, 43); eq(Base._cast, 44); @@ -132,15 +130,12 @@ class TestJava extends Test eq(Base._in, 40); } - function testTypes() - { + function testTypes() { eq(Base.charTest(cast 10), cast 10); eq(Base.byteTest(cast 10), cast 10); } - - function testInnerClass() - { + function testInnerClass() { //-java-lib should be able to detect inner classes on import var i = new Base_InnerClass(); eq(i.nameClash(), 10); @@ -158,13 +153,12 @@ class TestJava extends Test eq("$$Inner$$Name8", haxe.test.MyClass.MyClass___Inner__Name7_____Inner__Name8.test()); } - function testGenerics() - { + function testGenerics() { var jcl:java.lang.Class = cast Base_InnerClass_InnerInnerClass; t(haxe.test.GenericHelper.staticTypedGeneric(jcl) != null); var helper = new haxe.test.GenericHelper(); - //TODO use typedAs + // TODO use typedAs eq(helper.getUntypedGeneric(), null); eq(helper.typedGeneric, null); @@ -172,21 +166,18 @@ class TestJava extends Test var val3 = new Base___InnerClass3__(); var g1 = new haxe.test.Generic1(val); g1.complexTypeParameterOfTypeParameter(new Base_InnerClass_InnerInnerClass()); - //if no compile-time error, we're fine! + // if no compile-time error, we're fine! t(true); } - function testNameClash() - { + function testNameClash() { eq(Base.nameClash(null), -1); eq(new Base().nameClash(), 1); eq(new Base().varNameClash(1), 1); eq(Base.varNameClash(10.4), 10.4); - } - function testOverloadOverride() - { + function testOverloadOverride() { var c = new TestMyClass(); c.normalOverload(true); t(c.boolCalled); @@ -214,8 +205,7 @@ class TestJava extends Test t(c.dynamicCalled); } - function testThrows1() - { + function testThrows1() { // test 1: no @:throws / no catch var b = new Base(); eq(Base.throwsTest(), 5); @@ -224,49 +214,37 @@ class TestJava extends Test eq(b.throwsMemberTest(true), 10); } - function testThrows2() - { + function testThrows2() { // test 2: catching only the IOException - try - { + try { var b = new Base(); eq(Base.throwsTest(), 5); eq(Base.throwsTest(42), 42); eq(b.throwsMemberTest(), 6); eq(b.throwsMemberTest(true), 10); - } - catch(e:java.io.IOException) - { - } + } catch (e:java.io.IOException) {} } - function testThrows3() - { + function testThrows3() { // test 3: catching all exceptions - try - { + try { var b = new Base(); eq(Base.throwsTest(), 5); eq(Base.throwsTest(42), 42); eq(b.throwsMemberTest(), 6); eq(b.throwsMemberTest(true), 10); - } - catch(e:java.lang.Throwable) - { - } + } catch (e:java.lang.Throwable) {} } // test 4: @:throws IOException and only use IOException - @:throws('java.io.IOException') function testThrows4() - { + @:throws('java.io.IOException') function testThrows4() { var b = new Base(); eq(Base.throwsTest(), 5); eq(b.throwsMemberTest(true), 10); } // test 5: @:throws IOException and use any - @:throws('java.io.IOException') function testThrows5() - { + @:throws('java.io.IOException') function testThrows5() { var b = new Base(); eq(Base.throwsTest(), 5); eq(Base.throwsTest(42), 42); @@ -274,39 +252,42 @@ class TestJava extends Test eq(b.throwsMemberTest(true), 10); } - function testJavaLibEnum() - { + function testJavaLibEnum() { var e = TEnum.TA; - switch(e) - { + switch (e) { case TA: t(true); case _: t(false); } - eq("TA",Type.enumConstructor(e)); + eq("TA", Type.enumConstructor(e)); } - function testMiscJavaLib() - { - //setting inline should be an error + function testMiscJavaLib() { + // setting inline should be an error t(HelperMacros.typeError(Base.inlineNumber = 4)); } - //TODO: - //overload with functions + variable types + // TODO: + // overload with functions + variable types + function testDefaultInterface() { + var c = new MyDefaultInterfaceClass(); + eq("FOO", c.test("foo")); + } + + function testDefaultSubInterface() { + var c = new MyDefaultSubInterfaceClass(); + eq("FOO", c.test("foo")); + } } -private class TestMyClass extends haxe.test.MyClass -{ - @:overload public function new() - { +private class TestMyClass extends haxe.test.MyClass { + @:overload public function new() { super(); } - @:overload public function new(str:String) - { + @:overload public function new(str:String) { super(); alternativeCtorCalled = true; } @@ -318,54 +299,39 @@ private class TestMyClass extends haxe.test.MyClass public var stringCalled:Bool; public var dynamicCalled:Bool; - @:overload override public function normalOverload(b:Bool):Void - { + @:overload override public function normalOverload(b:Bool):Void { this.boolCalled = true; } - @:overload override public function normalOverload(i:Int):Void - { + @:overload override public function normalOverload(i:Int):Void { this.intCalled = true; } - @:overload override public function normalOverload(i64:haxe.Int64):Void - { + @:overload override public function normalOverload(i64:haxe.Int64):Void { this.int64Called = true; } - @:overload override public function normalOverload(str:String):Void - { + @:overload override public function normalOverload(str:String):Void { this.stringCalled = true; } - @:overload override public function normalOverload(dyn:Dynamic):Void - { + @:overload override public function normalOverload(dyn:Dynamic):Void { this.dynamicCalled = true; } } -@:nativeGen private class NativeClass -{ - public function new() - { +@:nativeGen private class NativeClass { + public function new() {} - } - - @:throws("java.lang.Throwable") - public function excTest():Void - { - throw new java.lang.Throwable("test", null); - } + @:throws("java.lang.Throwable") + public function excTest():Void { + throw new java.lang.Throwable("test", null); + } } -private class HxClass extends NativeClass -{ - - @:throws("java.lang.Throwable") - override public function excTest():Void - { - - } +private class HxClass extends NativeClass { + @:throws("java.lang.Throwable") + override public function excTest():Void {} } enum HaxeEnum { @@ -374,4 +340,11 @@ enum HaxeEnum { HC; } +class MyDefaultInterfaceClass implements MyDefaultInterface { + public function new() {} +} + +class MyDefaultSubInterfaceClass implements MyDefaultSubInterface { + public function new() {} +} #end diff --git a/tests/unit/src/unit/TestLocalStatic.hx b/tests/unit/src/unit/TestLocalStatic.hx new file mode 100644 index 00000000..8da72b6c --- /dev/null +++ b/tests/unit/src/unit/TestLocalStatic.hx @@ -0,0 +1,20 @@ +package unit; + +class TestLocalStatic extends Test { + function basic() { + static var x = 1; + static final y = "final"; + x++; + return {x: x, y: y}; + } + + function testBasic() { + var obj = basic(); + eq(2, obj.x); + eq("final", obj.y); + + obj = basic(); + eq(3, obj.x); + eq("final", obj.y); + } +} diff --git a/tests/unit/src/unit/TestMacro.hx b/tests/unit/src/unit/TestMacro.hx index a1117a1d..7e58766c 100644 --- a/tests/unit/src/unit/TestMacro.hx +++ b/tests/unit/src/unit/TestMacro.hx @@ -70,6 +70,7 @@ class TestMacro extends Test { parseAndPrint('(a, b) -> c'); parseAndPrint('function(a) return b'); parseAndPrint('function named(a) return b'); + parseAndPrint('function named() {\n\tstatic final @Test a = 1, b = 2;\n\tstatic var c, d;\n\tfinal e = 2;\n\tvar f;\n}'); var p = new haxe.macro.Printer(); // special handling of single arguments (don't add parentheses) diff --git a/tests/unit/src/unit/TestMain.hx b/tests/unit/src/unit/TestMain.hx index 4fe21d61..6bc80f35 100644 --- a/tests/unit/src/unit/TestMain.hx +++ b/tests/unit/src/unit/TestMain.hx @@ -1,9 +1,9 @@ package unit; -import utest.ui.Report; -import utest.Runner; -import unit.Test.*; import haxe.ds.List; +import unit.Test.*; +import utest.Runner; +import utest.ui.Report; final asyncWaits = new Array(); final asyncCache = new Array<() -> Void>(); @@ -25,35 +25,38 @@ function main() { } #end - var verbose = #if ( cpp || neko || php ) Sys.args().indexOf("-v") >= 0 #else false #end; + var verbose = #if (cpp || neko || php) Sys.args().indexOf("-v") >= 0 #else false #end; - #if cs //"Turkey Test" - Issue #996 + #if cs // "Turkey Test" - Issue #996 cs.system.threading.Thread.CurrentThread.CurrentCulture = new cs.system.globalization.CultureInfo('tr-TR'); cs.Lib.applyCultureChanges(); #end #if neko - if( neko.Web.isModNeko ) - neko.Web.setHeader("Content-Type","text/plain"); + if (neko.Web.isModNeko) + neko.Web.setHeader("Content-Type", "text/plain"); #elseif php - if( php.Web.isModNeko ) - php.Web.setHeader("Content-Type","text/plain"); + if (php.Web.isModNeko) + php.Web.setHeader("Content-Type", "text/plain"); #end #if !macro trace("Generated at: " + HelperMacros.getCompilationDate()); #end trace("START"); #if flash - var tf : flash.text.TextField = untyped flash.Boot.getTrace(); + var tf:flash.text.TextField = untyped flash.Boot.getTrace(); tf.selectable = true; tf.mouseEnabled = true; #end var classes = [ new TestOps(), new TestBasetypes(), + new TestNumericSuffixes(), + new TestNumericSeparator(), new TestExceptions(), new TestBytes(), new TestIO(), new TestLocals(), + new TestLocalStatic(), new TestEReg(), new TestXML(), new TestMisc(), @@ -75,8 +78,7 @@ function main() { new TestNumericCasts(), new TestHashMap(), new TestRest(), - #if (!no_http && (!github || !(php && Windows))) - new TestHttp(), + #if !no_http new TestHttp(), #end #if !no_pattern_matching new TestMatch(), @@ -105,15 +107,15 @@ function main() { new TestOverloadsForEveryone(), new TestInterface(), new TestNaN(), - #if ((dce == "full") && !interp) - new TestDCE(), + #if ((dce == "full") && !interp) new TestDCE(), #end new TestMapComprehension(), new TestMacro(), new TestKeyValueIterator(), new TestFieldVariance(), - new TestConstrainedMonomorphs() - //new TestUnspecified(), + new TestConstrainedMonomorphs(), + new TestDefaultTypeParameters(), + // new TestUnspecified(), ]; for (specClass in unit.UnitBuilder.generateSpec("src/unitstd")) { @@ -131,12 +133,13 @@ function main() { report.displaySuccessResults = NeverShowSuccessResults; var success = true; runner.onProgress.add(function(e) { - for(a in e.result.assertations) { + for (a in e.result.assertations) { switch a { case Success(pos): case Warning(msg): case Ignore(reason): - case _: success = false; + case _: + success = false; } } #if js @@ -152,4 +155,8 @@ function main() { }); #end runner.run(); + + #if (flash && fdb) + flash.Lib.fscommand("quit"); + #end } diff --git a/tests/unit/src/unit/TestMatch.hx b/tests/unit/src/unit/TestMatch.hx index 28a211fc..155d5a08 100644 --- a/tests/unit/src/unit/TestMatch.hx +++ b/tests/unit/src/unit/TestMatch.hx @@ -136,7 +136,7 @@ class TestMatch extends Test { eq("foo", switchNormal(macro null.foo)); eq("22", switchNormal(macro null[22])); eq("22.5", switchNormal(macro null[22.5])); - eq("EConst(CInt(0))", switchNormal(macro 1 in 0)); + eq("EConst(CInt(0,null))", switchNormal(macro 1 in 0)); eq("not_found", switchNormal(macro null["22"])); t(null != switchCapture(macro "foobar")); @@ -146,8 +146,8 @@ class TestMatch extends Test { eq("[]", switchArray(macro [])); eq("_", switchArray(macro 2)); - eq("[EConst(CInt(22))]", switchArray(macro [22])); - eq("[EConst(CInt(22)),EConst(CString(foo,DoubleQuotes))]", switchArray(macro [22,"foo"])); + eq("[EConst(CInt(22,null))]", switchArray(macro [22])); + eq("[EConst(CInt(22,null)),EConst(CString(foo,DoubleQuotes))]", switchArray(macro [22,"foo"])); eq("_", switchArray(macro [22, "foo", "bar"])); eq("0", switchArray2(["a", "b"])); diff --git a/tests/unit/src/unit/TestMisc.hx b/tests/unit/src/unit/TestMisc.hx index afc6eced..d92e8d7e 100644 --- a/tests/unit/src/unit/TestMisc.hx +++ b/tests/unit/src/unit/TestMisc.hx @@ -328,6 +328,7 @@ class TestMisc extends Test { eq("Héllow", haxe.crypto.Base64.decode("SMOpbGxvdw", false).toString()); eq("Héllo", haxe.crypto.Base64.decode("SMOpbGxv").toString()); eq("Héll", haxe.crypto.Base64.decode("SMOpbGw=").toString()); + exc(() -> haxe.crypto.Base64.decode("invalid string")); // alternative base64 var b = new haxe.crypto.BaseCode(haxe.io.Bytes.ofString("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-")); diff --git a/tests/unit/src/unit/TestNullCoalescing.hx b/tests/unit/src/unit/TestNullCoalescing.hx new file mode 100644 index 00000000..0189e140 --- /dev/null +++ b/tests/unit/src/unit/TestNullCoalescing.hx @@ -0,0 +1,124 @@ +package unit; + +@:nullSafety(StrictThreaded) +class TestNullCoalescing extends Test { + final nullInt:Null = null; + final nullBool:Null = null; + final nullString:Null = null; + + var count = 0; + function call() { + count++; + return "_"; + } + + function test() { + var a = call() ?? "default"; + eq(count, 1); + + eq(nullInt ?? nullInt, null); + eq(nullBool ?? nullBool, null); + + final a:Dynamic = Std.random(0) + 1; + final b = Std.random(0) + 2; + eq(1 + a + 1 ?? 1 + b + 1, 3); + + final nullableBool:Null = false; + final testBool = nullBool ?? true; + final testNullBool = nullBool ?? nullableBool; + final s:Int = nullInt == null ? 2 : nullInt; + final s:Int = if (nullInt == null) 2; else nullInt; + final s = nullInt ?? 2; + + // $type(testBool); // Bool + // $type(testNullBool); // Null + // $type(s); // Int + final shouldBeBool:Bool = testBool; + if (testNullBool == null) {} + final shouldBeInt:Int = s; + + eq(testBool, true); + eq(testNullBool, false); + eq(s, 2); + + eq(nullInt == null ? 2 : nullInt, 2); + eq(nullInt ?? 2, 2); + eq(nullInt ?? (2 : Null) ?? 3 + 100, 2); + eq(nullInt ?? nullInt ?? 3, 3); + + final i:Null = 1; + final arr:Array = [i ?? 2]; + arr.push(i ?? 2); + arr.push((1 : Null) ?? 2); + eq(arr[0], 1); + eq(arr[1], 1); + eq(arr[2], 1); + + final arr = [ + nullInt ?? 2, + 2 + ]; + eq(arr[0], arr[1]); + + var a = [0 => nullInt ?? 0 + 100]; + eq(a[0], 100); + + final di:Null = null; + final di2:Null = null; + final di3:Null = 2; + eq(di ?? di2 ?? di3, 2); + + var a:Null = null; + a ??= 5; + eq(a, 5); + var a:Null = null; + eq(a ??= 5, 5); + eq(a, 5); + var a = "default"; + eq(a ??= "5", "default"); + + count = 0; + var a = call(); + eq(count, 1); + a ??= call(); + eq(count, 1); + + var a:Null = null; + final b = a ??= call(); + final c = a ??= call(); + eq(count, 2); + eq(a, "_"); + eq(b, "_"); + eq(c, "_"); + + final a:Null = ({} : Dynamic).x; + eq(a ?? 2, 2); + + final a = nullInt; + eq(a ?? 2, 2); + + final a = nullString; + eq(a ?? "2", "2"); + + eq(1 ?? 2, 1); + eq("1" ?? "2", "1"); + + final arr = []; + function item(n) { + arr.push(n); + return n; + } + eq(item(1) ?? item(2) ?? item(3), 1); + eq(arr.length, 1); + for (i => v in [1]) eq(arr[i], v); + + final arr = []; + function item(n) { + arr.push(n); + return null; + } + eq(item(1) ?? item(2) ?? item(3), null); + eq(arr.length, 3); + for (i => v in [1, 2, 3]) eq(arr[i], v); + } +} diff --git a/tests/unit/src/unit/TestNumericSeparator.hx b/tests/unit/src/unit/TestNumericSeparator.hx new file mode 100644 index 00000000..4cf3bf53 --- /dev/null +++ b/tests/unit/src/unit/TestNumericSeparator.hx @@ -0,0 +1,75 @@ +package unit; + +class TestNumericSeparator extends Test { + public function test() { + // normal int + eq(12_0, 120); + eq(1_2_0, 120); + + // hex int + eq(0x12_0, 0x120); + eq(0x1_2_0, 0x120); + + // normal float + feq(12.3_4, 12.34); + feq(1_2.34, 12.34); + feq(1_2.3_4, 12.34); + + // dot float + feq(.3_4, .34); + feq(.3_4_5, .345); + + // science float + feq(1_2e3_4, 12e34); + feq(1_2.3e4_5, 12.3e45); + + // int but actually float + feq(1_2f64, 12f64); + } + + public function testWithSuffix() { + // normal int + eq(12_0i32, 120i32); + eq(1_2_0i32, 120i32); + + // hex int + eq(0x12_0i32, 0x120i32); + eq(0x1_2_0i32, 0x120i32); + + // normal float + feq(12.3_4f64, 12.34f64); + feq(1_2.34f64, 12.34f64); + feq(1_2.3_4f64, 12.34f64); + + // dot float + feq(.3_4f64, .34f64); + feq(.3_4_5f64, .345f64); + + // science float + feq(1_2e3_4f64, 12e34f64); + feq(1_2.3e4_5f64, 12.3e45f64); + } + + public function testJustBeforeSuffix() { + // normal int + eq(12_0_i32, 120i32); + eq(1_2_0_i32, 120i32); + + // hex int + eq(0x12_0_i32, 0x120i32); + eq(0x1_2_0_i32, 0x120i32); + + // normal float + feq(12.3_4_f64, 12.34f64); + feq(1_2.34_f64, 12.34f64); + feq(1_2.3_4_f64, 12.34f64); + + // dot float + feq(.3_4_f64, .34f64); + feq(.3_4_5_f64, .345f64); + + // science float + feq(1_2e3_4_f64, 12e34f64); + feq(1_2.3e4_5_f64, 12.3e45f64); + } +} diff --git a/tests/unit/src/unit/TestNumericSuffixes.hx b/tests/unit/src/unit/TestNumericSuffixes.hx new file mode 100644 index 00000000..ef651805 --- /dev/null +++ b/tests/unit/src/unit/TestNumericSuffixes.hx @@ -0,0 +1,29 @@ +package unit; + +class TestNumericSuffixes extends Test { + + public function testIntSuffixes() { + eq(7i32, 7); + eq(-7i32, -7); + eq(-1u32, (-1 : UInt)); + eq(3000000000000i64 + "", "3000000000000"); + eq(9223372036854775807i64 + "", "9223372036854775807"); + } + + public function testFloatSuffixes() { + eq(7.0f64, 7.0); + eq(-7.0f64, -7.0); + eq(1f64 is Float, true); + eq(.0f64, 0.0); + eq(7e+0f64, 7e+0); + eq(7.0e+0f64, 7.0e+0); + } + + public function testHexSuffixes() { + eq(0xFFFFFFFFi32, -1); + eq(0xFFFFFFFFu32, (0xFFFFFFFF : UInt)); + eq(0xFFFFFFFFi64 + "", "4294967295"); + eq(0xFFFFFFFFFFFFFFFFi64 + "", "-1"); + eq(0x7FFFFFFFFFFFFFFFi64 + "", "9223372036854775807"); + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/TestRest.hx b/tests/unit/src/unit/TestRest.hx index 5fe3f373..fd739142 100644 --- a/tests/unit/src/unit/TestRest.hx +++ b/tests/unit/src/unit/TestRest.hx @@ -20,7 +20,7 @@ class TestRest extends Test { function testToArray() { function rest(...r:Int):Array { var a = r.toArray(); - a[0] = 999; //make sure array modification doesn't affect rest arguments object + a[0] = 999; // make sure array modification doesn't affect rest arguments object return r.toArray(); } aeq([1, 2, 3, 4], rest(1, 2, 3, 4)); @@ -36,7 +36,7 @@ class TestRest extends Test { function testIterator() { function rest(...r:Int):Array { - return [for(i in r) i]; + return [for (i in r) i]; } aeq([3, 2, 1], rest(3, 2, 1)); } @@ -45,11 +45,11 @@ class TestRest extends Test { function rest(...r:Int):{keys:Array, values:Array} { var keys = []; var values = []; - for(k => v in r) { + for (k => v in r) { keys.push(k); values.push(v); } - return {keys:keys, values:values} + return {keys: keys, values: values} } var r = rest(3, 2, 1, 0); aeq([0, 1, 2, 3], r.keys); @@ -57,16 +57,28 @@ class TestRest extends Test { } @:depends(testToArray) + @:haxe.warning("-WGenerator") function testAppend() { function rest(...r:Int) { var appended = r.append(9); - return {initial:r.toArray(), appended:appended.toArray()} + return {initial: r.toArray(), appended: appended.toArray()} } var result = rest(1, 2); aeq([1, 2], result.initial); aeq([1, 2, 9], result.appended); } + @:depends(testToArray) + function testPrepend() { + function rest(...r:Int) { + var prepended = r.prepend(9); + return {initial: r.toArray(), prepended: prepended.toArray()} + } + var result = rest(1, 2); + aeq([1, 2], result.initial); + aeq([9, 1, 2], result.prepended); + } + @:depends(testToArray) function testSpread() { function rest(...r:Int) { @@ -80,7 +92,7 @@ class TestRest extends Test { aeq([1, 2, 3], new Parent(...[1, 2, 3]).ctorArgs.toArray()); } - @:depends(testToArray, testRestReturn) + @:depends(testToArray, testRestReturn, testAppend) function testInheritance() { var p = new Parent(1, 2, 3); var c = new Child(4, 5, 6); @@ -101,7 +113,7 @@ class TestRest extends Test { @:depends(testToArray) function testClosure() { - var fn:(...r:Int)->Array; + var fn:(...r:Int) -> Array; fn = staticRest; aeq([1, 2, 3], fn(1, 2, 3)); aeq([1, 2, 3], fn(...[1, 2, 3])); @@ -109,9 +121,11 @@ class TestRest extends Test { aeq([3, 2, 1], fn(3, 2, 1)); aeq([3, 2, 1], fn(...[3, 2, 1])); } + function instanceRest(...r:Int):Array { return r.toArray(); } + static function staticRest(...r:Int):Array { return r.toArray(); } @@ -129,14 +143,14 @@ class TestRest extends Test { function rest(...r:{f:Int}) { return r.toArray(); } - aeq([{f:2}, {f:1}], rest({f:2}, {f:1})); + aeq([{f: 2}, {f: 1}], rest({f: 2}, {f: 1})); } function testInferred() { function rest(...r) { (r[0] : Int); } - HelperMacros.typedAs(rest, (null : (r : Rest)->Void)); + HelperMacros.typedAs(rest, (null : (r:Rest) -> Void)); } function testToString() { @@ -167,4 +181,4 @@ private class Child extends Parent { override public function methodWithRest(rest:Rest):Rest { return super.methodWithRest(...rest.append(999)); } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/UnitBuilder.hx b/tests/unit/src/unit/UnitBuilder.hx index 94a38c30..43c6e396 100644 --- a/tests/unit/src/unit/UnitBuilder.hx +++ b/tests/unit/src/unit/UnitBuilder.hx @@ -25,10 +25,10 @@ package unit; import haxe.macro.Context; import haxe.macro.Expr; import haxe.macro.Type; + using StringTools; class UnitBuilder { - static public macro function generateSpec(basePath:String) { var ret = []; var numFiles = 0; @@ -46,34 +46,43 @@ class UnitBuilder { params: [], expr: read(filePath) } - var p = Context.makePosition( { min:0, max:0, file:filePath + file } ); + var p = Context.makePosition({min: 0, max: 0, file: filePath + file}); var field = { name: "test", kind: FFun(func), pos: p, access: [APublic], doc: null, - meta: [] + meta: file.endsWith("Utf8.unit.hx") ? [{name: ":haxe.warning", params: [macro "-WDeprecated"], pos: p}] : [] }; var pack = ["unit", "spec"].concat(pack); var typeName = "Test" + file.substr(0, file.indexOf(".")); - Context.defineModule(pack.join(".") + "." + typeName, [{ - pack: pack, - name: typeName, - pos: p, - kind: TDClass({ - pack: ["unit"], - name: "Test" - }), - fields: [field] - }], [{ - path: [{pos: p, name: "unit"}, {pos: p, name: "spec"}, {pos: p, name: "TestSpecification"}], - mode: INormal - }, { - // TODO: import.hx doesn't work for this? - path: [{pos: p, name: "haxe"}, {pos: p, name: "macro"}, {pos: p, name: "Expr"}], - mode: INormal - }]); + Context.defineModule(pack.join(".") + "." + typeName, [ + { + pack: pack, + name: typeName, + pos: p, + kind: TDClass({ + pack: ["unit"], + name: "Test" + }), + fields: [field] + } + ], [ + { + path: [ + {pos: p, name: "unit"}, + {pos: p, name: "spec"}, + {pos: p, name: "TestSpecification"} + ], + mode: INormal + }, + { + // TODO: import.hx doesn't work for this? + path: [{pos: p, name: "haxe"}, {pos: p, name: "macro"}, {pos: p, name: "Expr"}], + mode: INormal + } + ]); var tp:TypePath = { pack: pack, name: typeName @@ -81,7 +90,7 @@ class UnitBuilder { ret.push(macro new $tp()); } else if (sys.FileSystem.isDirectory(filePath)) { readDir(filePath, pack.concat([file])); - } else if(filePath.endsWith('.hx')) { + } else if (filePath.endsWith('.hx')) { Context.error('$filePath: specification tests filenames should end with ".unit.hx"', Context.currentPos()); } } @@ -93,28 +102,32 @@ class UnitBuilder { #if macro static function collapseToOrExpr(el:Array) { - return switch(el) { + return switch (el) { case []: throw ""; case [e]: e; - case _: - var e = el.pop(); - { expr: EBinop(OpBoolOr, e, collapseToOrExpr(el)), pos: e.pos } + case _: + var e = el.pop(); + {expr: EBinop(OpBoolOr, e, collapseToOrExpr(el)), pos: e.pos} } } static function mkEq(e1, e2, p:Position) { function isFloat(e) { - try return switch(Context.follow(Context.typeof(e))) { - case TAbstract(tr, _): - tr.get().name == "Float"; - case _: - false; - } catch (e:Dynamic) { + try + return switch (Context.follow(Context.typeof(e))) { + case TAbstract(tr, _): + tr.get().name == "Float"; + case _: + false; + } catch (e:Dynamic) { return false; } } var e = switch [isFloat(e1) || isFloat(e2), e2.expr] { - case [_, EField( { expr:EConst(CIdent("Math" | "math")) }, "POSITIVE_INFINITY" | "NEGATIVE_INFINITY")] if (Context.defined("cpp") || Context.defined("php")): + case [ + _, + EField({expr: EConst(CIdent("Math" | "math"))}, "POSITIVE_INFINITY" | "NEGATIVE_INFINITY") + ] if (Context.defined("cpp") || Context.defined("php")): macro t($e1 == $e2); case [true, _]: macro feq($e1, $e2); @@ -126,12 +139,13 @@ class UnitBuilder { pos: p } } + static public function read(path:String) { - var p = Context.makePosition( { min:0, max:0, file:path } ); + var p = Context.makePosition({min: 0, max: 0, file: path}); var file = sys.io.File.getContent(path); var code = Context.parseInlineString("{" + file + "\n}", p); function mkBlock(e:Expr) { - return switch(e.expr) { + return switch (e.expr) { case EBlock(b): b; case _: [e]; } @@ -139,30 +153,24 @@ class UnitBuilder { function bl(block:Array):Array { var ret = []; for (e in block) { - var e = switch(e.expr) { - case EBinop(OpEq, e1, { expr: EConst(CIdent("false")) } ) - | EBinop(OpEq, { expr: EConst(CIdent("false")) }, e1): + var e = switch (e.expr) { + case EBinop(OpEq, e1, {expr: EConst(CIdent("false"))}) | EBinop(OpEq, {expr: EConst(CIdent("false"))}, e1): { expr: (macro f($e1)).expr, pos: e.pos } - case EBinop(OpEq, e1, { expr: EConst(CIdent("true")) } ) - | EBinop(OpEq, { expr: EConst(CIdent("true")) }, e1): + case EBinop(OpEq, e1, {expr: EConst(CIdent("true"))}) | EBinop(OpEq, {expr: EConst(CIdent("true"))}, e1): { expr: (macro t($e1)).expr, pos: e.pos } - case EBinop(OpEq, e1, { expr: EArrayDecl(el) } ) - | EBinop(OpEq, { expr: EArrayDecl(el) }, e1 ): + case EBinop(OpEq, e1, {expr: EArrayDecl(el)}) | EBinop(OpEq, {expr: EArrayDecl(el)}, e1): var el2 = []; for (i in 0...el.length) { var e2 = el[i]; el2.push(mkEq((macro $e1[$v{i}]), e2, e.pos)); } - if (el2.length == 0) - mkEq((macro @:pos(e1.pos) $e1.length), (macro 0), e.pos); - else - macro { $a{el2}; }; + if (el2.length == 0) mkEq((macro @:pos(e1.pos) $e1.length), (macro 0), e.pos); else macro {$a{el2};}; case EBinop(OpEq, e1, e2): mkEq(e1, e2, e.pos); case EBinop(OpNotEq, e1, e2): @@ -174,18 +182,18 @@ class UnitBuilder { } case EThrow(e): macro exc(function() $e); - case EBinop(OpIn, e1, {expr:EArrayDecl(el) }): + case EBinop(OpIn, e1, {expr: EArrayDecl(el)}): var el2 = []; for (e in el) el2.push(macro $e1 == $e); - macro @:pos(e.pos) t(${ collapseToOrExpr(el2) } ); + macro @:pos(e.pos) t(${collapseToOrExpr(el2)}); case EVars(vl): for (v in vl) if (v.name == "t" || v.name == "f" || v.name == "eq" || v.name == "neq") Context.error('${v.name} is reserved for unit testing', e.pos); - e; + e; case EFor(it, {expr: EBlock(el), pos: p}): - { expr: EFor(it, {expr:EBlock(bl(el)), pos: p}), pos: e.pos }; + {expr: EFor(it, {expr: EBlock(bl(el)), pos: p}), pos: e.pos}; case _: e; } @@ -197,4 +205,4 @@ class UnitBuilder { return macro $b{bl(block)}; } #end -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue10066.hx b/tests/unit/src/unit/issues/Issue10066.hx new file mode 100644 index 00000000..8f665f8a --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10066.hx @@ -0,0 +1,24 @@ +package unit.issues; + +private abstract Foo(Array) from Array { + @:op(_ += _) inline function add(string:String) + this.push(string); +} + +class Issue10066 extends unit.Test { + function testExpr() { + var a = []; + var foo:Foo = a; + foo += (1 == 2 ? 'two' : 'three'); + eq("three", a[0]); + } + + static var staticFoo:Foo; + + function testField() { + var a = []; + staticFoo = a; + staticFoo += (1 == 2 ? 'two' : 'three'); + eq("three", a[0]); + } +} diff --git a/tests/unit/src/unit/issues/Issue10073.hx b/tests/unit/src/unit/issues/Issue10073.hx index 9b3392d8..a5f94c74 100644 --- a/tests/unit/src/unit/issues/Issue10073.hx +++ b/tests/unit/src/unit/issues/Issue10073.hx @@ -1,41 +1,42 @@ package unit.issues; private abstract Foo(Array) from Array { - @:op([]) - function get(index: Int): Int; + @:op([]) + function get(index:Int):Int; - @:op([]) - function set(index: Int, value: Int): Void; + @:op([]) + function set(index:Int, value:Int):Void; } #if eval abstract Bar(Int) from Int { - @:op(_ + _) - extern function add(other: Int): Int; + @:op(_ + _) + extern function add(other:Int):Int; - @:op([]) - extern function get(index: Int): Int; + @:op([]) + extern function get(index:Int):Int; - @:native('add') - function doAdd(other: Int): Int - return 39; + @:native('add') + function doAdd(other:Int):Int + return 39; - @:native('get') - function doGet(index: Int) - return (this + index) * 2; + @:native('get') + function doGet(index:Int) + return (this + index) * 2; } #end +@:haxe.warning("-WGenerator") class Issue10073 extends Test { - function test() { - var foo: Foo = []; - foo[0] = 3; - eq(3, foo[0]); - - #if eval - var bar: Bar = 71; - eq(39, bar + 1); - eq(144, bar[1]); - #end - } + function test() { + var foo:Foo = []; + foo[0] = 3; + eq(3, foo[0]); + + #if eval + var bar:Bar = 71; + eq(39, bar + 1); + eq(144, bar[1]); + #end + } } diff --git a/tests/unit/src/unit/issues/Issue10124.hx b/tests/unit/src/unit/issues/Issue10124.hx index 2e0e5bde..bca95176 100644 --- a/tests/unit/src/unit/issues/Issue10124.hx +++ b/tests/unit/src/unit/issues/Issue10124.hx @@ -5,12 +5,12 @@ import utest.Assert; using unit.issues.Issue10124.Util; private class Util { - public static inline function add(arr:Array,...values:T) { + public static inline function add(arr:Array, ...values:T) { for (v in values) arr.push(v); } - public static inline function addFloat(arr:Array,...values:Float) { + public static inline function addFloat(arr:Array, ...values:Float) { for (v in values) arr.push(v); } diff --git a/tests/unit/src/unit/issues/Issue10127.hx b/tests/unit/src/unit/issues/Issue10127.hx new file mode 100644 index 00000000..6659cb97 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10127.hx @@ -0,0 +1,15 @@ +package unit.issues; + +@:allow(unit.issues.Issue10127) +class Issue10127 extends Test { + static final privateField = true; + + function test() { + accessible(); + noAssert(); + } +} + +function accessible() { + Issue10127.privateField; +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10135.hx b/tests/unit/src/unit/issues/Issue10135.hx new file mode 100644 index 00000000..2770ce65 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10135.hx @@ -0,0 +1,18 @@ +package unit.issues; + +class Issue10135 extends Test { + function doEncode() { + var w = 0; + + var i = 0; + while (Math.random() > 0.5) { + while (i < 80) { + w = 1; + } + } + } + + function test() { + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10139.hx b/tests/unit/src/unit/issues/Issue10139.hx new file mode 100644 index 00000000..e8058430 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10139.hx @@ -0,0 +1,19 @@ +package unit.issues; + +import utest.Assert; + +private abstract class A { // This class has uninitialized final vars, which requires a constructor + public final x:Int; // Example of an uninitialized final var +} + +private class B extends A { + public function new() { + x = 10; // Cannot access field or identifier x for writing + } +} + +class Issue10139 extends Test { + function test() { + eq(10, new B().x); + } +} diff --git a/tests/unit/src/unit/issues/Issue10148.hx b/tests/unit/src/unit/issues/Issue10148.hx new file mode 100644 index 00000000..86de69a7 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10148.hx @@ -0,0 +1,17 @@ +package unit.issues; + +private enum FooBar { + Foo; + Bar(value: Any); +} + +class Issue10148 extends Test { + function test() { + var bar: FooBar = Bar(0); + var matched = switch (bar) { + case Bar(0): true; + case _: false; + } + eq(true, matched); + } +} diff --git a/tests/unit/src/unit/issues/Issue10205.hx b/tests/unit/src/unit/issues/Issue10205.hx new file mode 100644 index 00000000..0f737d70 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10205.hx @@ -0,0 +1,16 @@ +package unit.issues; + +class Issue10205 extends unit.Test { + function test() { + eq("no rest foo", over("foo")); + eq("rest foo bar", over("foo", "bar")); + } + + overload extern static inline function over(s:String) { + return "no rest " + s; + } + + overload extern static inline function over(s:String, ...r:String) { + return "rest " + s + " " + r.toArray().join(" "); + } +} diff --git a/tests/unit/src/unit/issues/Issue10209.hx b/tests/unit/src/unit/issues/Issue10209.hx new file mode 100644 index 00000000..ffe2c490 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10209.hx @@ -0,0 +1,13 @@ +package unit.issues; + +class Issue10209 extends Test { + function test() { + var s:AStr = 'hello'; + s += ', world'; + eq('hello, world', s); + } +} + +private abstract AStr(String) from String to String { + @:op(A += B) @:commutative static function assignAdd(a:AStr, b:String):AStr; +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10291.hx b/tests/unit/src/unit/issues/Issue10291.hx new file mode 100644 index 00000000..a10f777c --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10291.hx @@ -0,0 +1,25 @@ +package unit.issues; + +import haxe.ds.Option; + +class Issue10291 extends Test { + function test() { + eq("Case 2: b=null", match()); + } + + function match() { + var ret = ""; + var o = Some({b: null}); + switch (o) { + case Some({b: b}) if (b != null): + ret = "Case 1: b=" + b; + case Some({b: null}): + ret = "Case 2: b=null"; + case Some(_): + ret = "Case 3: Some(_)"; + case None: + ret = ("Default"); + } + return ret; + } +} diff --git a/tests/unit/src/unit/issues/Issue10304.hx b/tests/unit/src/unit/issues/Issue10304.hx new file mode 100644 index 00000000..a77cb717 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10304.hx @@ -0,0 +1,35 @@ +package unit.issues; + +import utest.Assert; + +private class PairIter { + var data:Array; + var x = 0; + var prev:T; + var cur:T; + + public inline function new(data:Array) { + this.data = data; + } + + public inline function hasNext() { + return x < data.length; + } + + public inline function next() { + prev = cur; + cur = data[x++]; + return {prev: prev, cur: cur}; + } +} + +class Issue10304 extends Test { + @:haxe.warning("-WConstructorInliningCancelled") + function test() { + var buf = new StringBuf(); + for (p in new PairIter(["1", "2", "3"])) { + buf.add('${p.prev} ${p.cur} '); + } + eq("null 1 1 2 2 3 ", buf.toString()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10336.hx b/tests/unit/src/unit/issues/Issue10336.hx new file mode 100644 index 00000000..878d2f12 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10336.hx @@ -0,0 +1,54 @@ +package unit.issues; + +private abstract JurajFunc(T->Unit) { + @:from + public static function fromFuncReturningSameType(f:T->T):JurajFunc { + throw 'irrelevant'; + } +} + +private abstract KevinFunc(T->Unit) from T->Unit to T->Unit { + @:from + public static function fromFuncReturningSameType(f:T->T):KevinFunc { + throw 'irrelevant'; + } +} + +private class Unit {} + +private abstract Callback(T->Void) from(T -> Void) { + @:from static function ofSingle(cb:A->Void):Callback + return null; + + @:from static function fromMany(callbacks:Array>):Callback + throw 0; +} + +private abstract Foo(Dynamic) { + public var value(get, never):Int; + + inline function get_value() + return 1; +} + +class Issue10336 extends Test { + function testJuraj() { + var value = 'foo'; + exc(() -> { + var f:JurajFunc = function(v:String) return value = v; + }); + } + + function testKevin() { + var value = 'foo'; + exc(() -> { + var f:KevinFunc = function(v:String) return value = v; + }); + } + + function testJurajReloaded() { + var cb:Callback = foo -> trace(foo.length); + var cb:Callback = foo -> trace(foo.value); + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10383.hx b/tests/unit/src/unit/issues/Issue10383.hx new file mode 100644 index 00000000..6d7bd4f0 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10383.hx @@ -0,0 +1,15 @@ +package unit.issues; + +class Issue10383 extends Test { + function test() { + exec(); + utest.Assert.pass(); + } + + static inline function exec():{a:Int, ?b:String, ?c:String} { + if (Math.random() > 0.5) { + return {a: 0, b: ""}; + } + return {a: 0, c: ""}; + } +} diff --git a/tests/unit/src/unit/issues/Issue10397.hx b/tests/unit/src/unit/issues/Issue10397.hx new file mode 100644 index 00000000..640c0864 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10397.hx @@ -0,0 +1,159 @@ +package unit.issues; + +import utest.Assert.*; + +#if jvm +@:meta(java.lang.annotation.Documented()) +private class NotMain { + static public function gather() { + final main = new NotMain(); + + final cls = java.Lib.getNativeType(main); + + final classAnnotations = []; + final methods = new Map(); + final fieldAnnotations = []; + + for (an in cls.getAnnotations()) + classAnnotations.push(an.toString()); + + for (f in cls.getMethods()) { + final methodAnnotations = []; + final methodArgAnnotations = new Map(); + for (an in f.getAnnotations()) + methodAnnotations.push(an.toString()); + + for (arg in f.getParameters()) { + var annots = []; + for (an in arg.getAnnotations()) { + annots.push(an.toString()); + } + methodArgAnnotations[arg.getName()] = annots; + } + methods[f.getName()] = {methodAnnotations: methodAnnotations, methodArgAnnotations: methodArgAnnotations}; + } + + for (f in cls.getConstructors()) { + final methodAnnotations = []; + final methodArgAnnotations = new Map(); + for (an in f.getAnnotations()) + methodAnnotations.push(an.toString()); + + for (arg in f.getParameters()) { + var annots = []; + for (an in arg.getAnnotations()) { + annots.push(an.toString()); + } + methodArgAnnotations[arg.getName()] = annots; + } + methods[f.toString()] = {methodAnnotations: methodAnnotations, methodArgAnnotations: methodArgAnnotations}; + } + + for (f in cls.getFields()) + if (f.getName() == 'member') { + for (an in f.getAnnotations()) + fieldAnnotations.push(an.toString()); + } + + return { + classAnnotations: classAnnotations, + methods: methods, + fieldAnnotations: fieldAnnotations + } + } + + @:meta(java.lang.annotation.Documented()) + @:keep + public overload function new(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String) {} + + overload function new() {} + + @:meta(java.lang.annotation.Documented()) + @:keep var member:String; + + @:meta(java.lang.annotation.Documented()) + @:keep + public function on0(@:meta(java.lang.annotation.Documented()) arg0:String):String { + return 'foo'; + } + + @:keep + public function on1(arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String { + return 'foo'; + } + + @:meta(java.lang.annotation.Documented()) + @:keep + public function onBoth(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String { + return 'foo'; + } + + @:meta(java.lang.annotation.Documented()) + @:keep + public function on0Static(@:meta(java.lang.annotation.Documented()) arg0:String):String { + return 'foo'; + } + + @:keep + public function on1Static(arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String { + return 'foo'; + } + + @:meta(java.lang.annotation.Documented()) + @:keep + public function onBothStatic(@:meta(java.lang.annotation.Documented()) arg0:String, @:meta(java.lang.annotation.Documented()) arg1:String):String { + return 'foo'; + } +} +#end + +class Issue10397 extends unit.Test { + #if jvm + function test() { + var annots = NotMain.gather(); + same([ + "@java.lang.annotation.Documented()", + "@haxe.jvm.annotation.ClassReflectionInformation(hasSuperClass=false)" + ], annots.classAnnotations); + same({ + methodAnnotations: ["@java.lang.annotation.Documented()"], + methodArgAnnotations: ["arg0" => ["@java.lang.annotation.Documented()"]], + }, annots.methods["on0"]); + same({ + methodAnnotations: [], + methodArgAnnotations: ["arg0" => [], "arg1" => ["@java.lang.annotation.Documented()"]], + }, annots.methods["on1"]); + same({ + methodAnnotations: ["@java.lang.annotation.Documented()"], + methodArgAnnotations: [ + "arg0" => ["@java.lang.annotation.Documented()"], + "arg1" => ["@java.lang.annotation.Documented()"] + ], + }, annots.methods["onBoth"]); + same({ + methodAnnotations: ["@java.lang.annotation.Documented()"], + methodArgAnnotations: ["arg0" => ["@java.lang.annotation.Documented()"]], + }, annots.methods["on0Static"]); + same({ + methodAnnotations: [], + methodArgAnnotations: ["arg0" => [], "arg1" => ["@java.lang.annotation.Documented()"]], + }, annots.methods["on1Static"]); + same({ + methodAnnotations: ["@java.lang.annotation.Documented()"], + methodArgAnnotations: [ + "arg0" => ["@java.lang.annotation.Documented()"], + "arg1" => ["@java.lang.annotation.Documented()"] + ], + }, annots.methods["onBothStatic"]); + same({ + methodAnnotations: ["@java.lang.annotation.Documented()"], + methodArgAnnotations: [ + "arg0" => ["@java.lang.annotation.Documented()"], + "arg1" => ["@java.lang.annotation.Documented()"] + ], + }, + annots.methods["public unit.issues.Issue10397$NotMain(java.lang.String,java.lang.String)"]); + same(["@java.lang.annotation.Documented()"], annots.fieldAnnotations); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10410.hx b/tests/unit/src/unit/issues/Issue10410.hx new file mode 100644 index 00000000..2fe844ff --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10410.hx @@ -0,0 +1,12 @@ +package unit.issues; + +class Issue10410 extends Test { +#if js + function test() { + var n = cast("5", String); // including this to add the code due to DCE, otherwise remove this if no DCE + var fields = []; + js.Syntax.code("for (var s in {0}) {1}.push(s)", n, fields); + f(fields.contains('__class__')); + } +#end +} diff --git a/tests/unit/src/unit/issues/Issue10455.hx b/tests/unit/src/unit/issues/Issue10455.hx new file mode 100644 index 00000000..88867969 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10455.hx @@ -0,0 +1,8 @@ +package unit.issues; + +class Issue10455 extends Test { + function test() { + var a = [0 => true ? 1 : 0]; + eq(1, a[0]); + } +} diff --git a/tests/unit/src/unit/issues/Issue10468.hx b/tests/unit/src/unit/issues/Issue10468.hx new file mode 100644 index 00000000..e86a448c --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10468.hx @@ -0,0 +1,10 @@ +package unit.issues; + +import haxe.SysTools; + +class Issue10468 extends Test { + function test() { + eq('"some/path"', SysTools.quoteWinArg('some/path', false)); + eq('/S', SysTools.quoteWinArg('/S', false)); + } +} diff --git a/tests/unit/src/unit/issues/Issue10482.hx b/tests/unit/src/unit/issues/Issue10482.hx new file mode 100644 index 00000000..d4ad2892 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10482.hx @@ -0,0 +1,86 @@ +package unit.issues; + +class Issue10482 extends Test { + function test() { + final ab = new MyAbstract(1); + final arr = ab.foo(); + eq('plus 2', arr[0]); + eq("call", arr[1]); + eq("hi", arr[2]); + + eq(1, ab.arr()[0].arr()[0].value()); + eq("plus 3", ab + 3); + eq("call", ab()); + eq("hi", ab.arr()[0].hi()); + + final ab = new MyInlineAbstract(1); + final arr = ab.foo(); + eq('plus 2', arr[0]); + eq("call", arr[1]); + eq("hi", arr[2]); + + eq(1, ab.arr()[0].arr()[0].value()); + eq("plus 3", ab + 3); + eq("call", ab()); + eq("hi", ab.arr()[0].hi()); + + final ab = new AbGeneric(1.5); + eq(1.5, ab.foo()); + } +} + +abstract MyAbstract(Int) { + public function new(a):Void this = a; + + public function foo():Array { + return [ + abstract + 2, + abstract(), + abstract.hi() + ]; + } + + public function hi() return "hi"; + + public function arr() return [abstract]; + + @:op(a()) function call() return "call"; + + @:op(a + b) function plus(b) return 'plus $b'; + + public function value():Int return this; +} + +abstract MyInlineAbstract(Int) { + public inline function new(a):Void this = a; + + public inline function foo():Array { + return [ + abstract + 2, + abstract(), + abstract.hi() + ]; + } + + public function hi() return "hi"; + + public function arr() return [abstract]; + + @:op(a()) function call() return "call"; + + @:op(a + b) function plus(b) return 'plus $b'; + + public function value():Int return this; +} + +abstract AbGeneric(T) { + public function new(a:T):Void { + this = a; + } + public function foo() { + return abstract.bar(); + } + public function bar() { + return this; + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10508.hx b/tests/unit/src/unit/issues/Issue10508.hx new file mode 100644 index 00000000..ed8a5b74 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10508.hx @@ -0,0 +1,10 @@ +package unit.issues; + +class Issue10508 extends Test { + #if java + function test() { + t(java.math.RoundingMode.getConstructors().length > 0); + t(java.math.RoundingMode.createAll().length > 0); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10535.hx b/tests/unit/src/unit/issues/Issue10535.hx new file mode 100644 index 00000000..2f4022cb --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10535.hx @@ -0,0 +1,13 @@ +package unit.issues; + +class Issue10535 extends Test { + #if hl + function foo(a:Array) {} + + function test() { + var x:hl.F32 = 10.0; + foo([x]); + utest.Assert.pass(); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10537.hx b/tests/unit/src/unit/issues/Issue10537.hx new file mode 100644 index 00000000..1e0866f9 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10537.hx @@ -0,0 +1,40 @@ +package unit.issues; + +import haxe.macro.Context; + +class Issue10537 extends Test { + macro static function m() { + var imports = [ + "haxe.ds.StringMap", + "StringTools.endsWith", + "StringTools.*", + "StringTools.startsWith as sw" + ]; + var usings = ["StringTools"]; + Context.withImports(imports, usings, () -> { + Context.typeExpr(macro { + StringMap; + endsWith("foo", "o"); + startsWith("foo", "o"); + sw("foo", "f"); + "foo".endsWith("o"); + }); + }); + + // nested + Context.withImports(["haxe.ds.StringMap"], [], () -> { + Context.withImports(["StringTools.endsWith"], [], () -> { + Context.typeExpr(macro { + StringMap; + endsWith("foo", "o"); + }); + }); + }); + return macro null; + } + + function test() { + m(); + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10541.hx b/tests/unit/src/unit/issues/Issue10541.hx new file mode 100644 index 00000000..e1a468c3 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10541.hx @@ -0,0 +1,14 @@ +package unit.issues; + +@:publicFields +private abstract Foo(Int) from Int { + static function make(f:Float) { + return Std.int(f); + } +} + +class Issue10541 extends Test { + function test() { + eq(1, Foo.make(1.2)); + } +} diff --git a/tests/unit/src/unit/issues/Issue10550.hx b/tests/unit/src/unit/issues/Issue10550.hx new file mode 100644 index 00000000..17ade2cd --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10550.hx @@ -0,0 +1,32 @@ +package unit.issues; + +@:structInit @:publicFields +private class Tuple2 { + final _1:T; + final _2:U; + + inline function new(_1:T, _2:U) { + this._1 = _1; + this._2 = _2; + } +} + +@:generic +@:structInit @:publicFields +private class Tuple2Generic { + final _1:T; + final _2:U; + + inline function new(_1:T, _2:U) { + this._1 = _1; + this._2 = _2; + } +} + +class Issue10550 extends Test { + function test() { + final foo:Tuple2 = {_1: 1, _2: "abc"}; // works + final bar:Tuple2Generic = {_1: 1, _2: "abc"}; // doesn't work + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10561.hx b/tests/unit/src/unit/issues/Issue10561.hx new file mode 100644 index 00000000..f6dcb325 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10561.hx @@ -0,0 +1,11 @@ +package unit.issues; + +class Issue10561 extends Test { + function test() { + eq(null, getNull() ?.length); + } + + function getNull():String { + return null; + } +} diff --git a/tests/unit/src/unit/issues/Issue10565.hx b/tests/unit/src/unit/issues/Issue10565.hx new file mode 100644 index 00000000..b9f1a396 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10565.hx @@ -0,0 +1,55 @@ +package unit.issues; + +private abstract MapProxy(Dynamic) { + @:from static function fromMap(map:Map):MapProxy { + return null; + } +} + +private class Foo {} +private abstract ReactTypeOf(Foo) to Foo {} + +@:forward +private abstract ReactContext(IReactContext) from IReactContext to IReactContext { + public function new() { + this = new Context(); + } + + @:to + public function toReactTypeString}>():ReactTypeOf { + return cast this; + } +} + +private interface IReactContext { + var Consumer:ReactContext; +} + +private class Context implements IReactContext { + public var Consumer = null; + + public function new() {} +} + +private typedef Bar = { + children:{foo:String}->String +} + +class Issue10565 extends Test { + function test() { + var m:MapProxy = new Map(); + utest.Assert.pass(); + } + + public static var Consumer:ReactTypeOf; + + function testReactMess() { + var context = createContext(); + Consumer = context.Consumer; + utest.Assert.pass(); + } + + public static function createContext():ReactContext { + return new ReactContext(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10568.hx b/tests/unit/src/unit/issues/Issue10568.hx new file mode 100644 index 00000000..12dfa783 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10568.hx @@ -0,0 +1,56 @@ +package unit.issues; + +private abstract A(String) { + var selected(get, set):Dynamic; + + public function get_selected() { + return null; + } + + public function set_selected(v) { + return null; + } + + public function new() { + this = ""; + } +} + +private class C { + var selected(get, set):Dynamic; + + public function get_selected() { + return null; + } + + public function set_selected(v) { + return null; + } + + public function new() {} +} + +class Issue10568 extends Test { + static var selected(get, set):Dynamic; + + static public function get_selected() { + return null; + } + + static public function set_selected(v) { + return null; + } + + function test() { + HelperMacros.typedAs(get_selected, (null : () -> Dynamic)); + HelperMacros.typedAs(set_selected, (null : (v:Dynamic) -> Dynamic)); + + var c = new C(); + HelperMacros.typedAs(c.get_selected, (null : () -> Dynamic)); + HelperMacros.typedAs(c.set_selected, (null : (v:Dynamic) -> Dynamic)); + + var a = new A(); + HelperMacros.typedAs(a.get_selected, (null : () -> Dynamic)); + HelperMacros.typedAs(a.set_selected, (null : (v:Dynamic) -> Dynamic)); + } +} diff --git a/tests/unit/src/unit/issues/Issue10571.hx b/tests/unit/src/unit/issues/Issue10571.hx new file mode 100644 index 00000000..f6853d40 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10571.hx @@ -0,0 +1,24 @@ +package unit.issues; + +class Issue10571 extends Test { + #if java + function test() { + eq("test()", foo()); + eq("test(I)", foo(1)); + } + + overload static function foo() { + function x() { + return "test()"; + } + return x(); + } + + overload static function foo(i:Int) { + function x() { + return "test(I)"; + } + return x(); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10600.hx b/tests/unit/src/unit/issues/Issue10600.hx new file mode 100644 index 00000000..a18ec8c9 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10600.hx @@ -0,0 +1,12 @@ +package unit.issues; + +class Issue10600 extends Test { + function test() { + var x = { + "checkstyle.checks.Checker": ["CheckBase", "UnusedImportCheck"] + }; + var a = Reflect.field(x, "checkstyle.checks.Checker"); + eq("CheckBase", a[0]); + eq("UnusedImportCheck", a[1]); + } +} diff --git a/tests/unit/src/unit/issues/Issue10604.hx b/tests/unit/src/unit/issues/Issue10604.hx new file mode 100644 index 00000000..aa1c97b6 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10604.hx @@ -0,0 +1,18 @@ +package unit.issues; + +private abstract Promise(Dynamic) { + @:from static inline function ofData(d:T):Promise + throw 0; +} + +@:callable +private abstract Next(In->Promise) from In->Promise to In->Promise { + @:from static function ofSafeSync(f:In->Out):Next + return x -> f(x); // error: Recursive implicit cast +} + +class Issue10604 extends Test { + function test() { + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10618.hx b/tests/unit/src/unit/issues/Issue10618.hx new file mode 100644 index 00000000..39f31797 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10618.hx @@ -0,0 +1,20 @@ +package unit.issues; + +#if java +@:keep +private class NotMain implements java.util.Iterator { + static function main() {} + + public function hasNext():Bool + throw 0; + + public function next():String + throw 0; +} +#end + +class Issue10618 extends unit.Test { + public function test() { + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10624.hx b/tests/unit/src/unit/issues/Issue10624.hx new file mode 100644 index 00000000..724c22ba --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10624.hx @@ -0,0 +1,17 @@ +package unit.issues; + +class Issue10624 extends unit.Test { + static final RANDOM:Int = 33; + + public function test() { + var v = switch (33) { + case 1: + "no"; + case RANDOM: + "yes"; + default: + "noooo"; + } + eq("yes", v); + } +} diff --git a/tests/unit/src/unit/issues/Issue10626.hx b/tests/unit/src/unit/issues/Issue10626.hx new file mode 100644 index 00000000..b83636d2 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10626.hx @@ -0,0 +1,17 @@ +package unit.issues; + +macro function foo() { + var s = try { + haxe.macro.Context.typeExpr(macro var x:Void); + "no exception"; + } catch (e) { + "an exception"; + } + return macro $v{s}; +} + +class Issue10626 extends unit.Test { + function test() { + eq("an exception", foo()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10641.hx b/tests/unit/src/unit/issues/Issue10641.hx new file mode 100644 index 00000000..43a7d3f4 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10641.hx @@ -0,0 +1,40 @@ +package unit.issues; + +private abstract A(Int) from Int { + @:op(A++) + function preInc():Void; + + @:op(++A) + function postInc():Void; +} + +macro function check() { + #if macro + final type = haxe.macro.ComplexTypeTools.toType(TPath({ + pack: ["unit", "issues"], + name: "Issue10641", + sub: "A" + })); + var acc = []; + function add(s:String) { + acc.push(s); + } + switch type { + case TAbstract(_.get() => t, params): + final unop = t.unops[0]; + for (unop in t.unops) { + add("" + unop.op); + add("" + unop.postFix); + add(unop.field.name); + } + case _: + } + return macro $v{acc.join(" ")}; + #end +} + +class Issue10641 extends unit.Test { + public function test() { + eq("OpIncrement true preInc OpIncrement false postInc", check()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10684.hx b/tests/unit/src/unit/issues/Issue10684.hx new file mode 100644 index 00000000..f16aef35 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10684.hx @@ -0,0 +1,11 @@ +package unit.issues; + +class Issue10684 extends unit.Test { + function test() { + eq("1", makeMapF()([1 => "1", 2 => "2"])); + } + + function makeMapF(?f:Map->String) { + return f ?? v -> v[1]; + } +} diff --git a/tests/unit/src/unit/issues/Issue10685.hx b/tests/unit/src/unit/issues/Issue10685.hx new file mode 100644 index 00000000..4394b450 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10685.hx @@ -0,0 +1,19 @@ +package unit.issues; + +@:structInit +private abstract class A { + public final x:Int; +} + +@:structInit +private class B extends A { + public var y:Int; +} + +class Issue10685 extends Test { + function test() { + var b:B = {x: 1, y: 2}; + eq(1, b.x); + eq(2, b.y); + } +} diff --git a/tests/unit/src/unit/issues/Issue10709.hx b/tests/unit/src/unit/issues/Issue10709.hx new file mode 100644 index 00000000..4b0f7bfe --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10709.hx @@ -0,0 +1,11 @@ +package unit.issues; + +class Issue10709 extends unit.Test { + function test() { + eq("null", Std.string(foo())); + } + + static function foo():Null> { + return null; + } +} diff --git a/tests/unit/src/unit/issues/Issue10712.hx b/tests/unit/src/unit/issues/Issue10712.hx new file mode 100644 index 00000000..0e7ba333 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10712.hx @@ -0,0 +1,47 @@ +package unit.issues; + +import haxe.extern.EitherType; + +private typedef PrepareRenameResult = EitherType>; + +private typedef Range = { + var start:Position; + var end:Position; +} + +private typedef RangePlaceholder = { + var range:Range; + var placeholder:String; +} + +private typedef DefaultBehaviour = { + var defaultBehavior:Bool; +} + +private typedef Position = { + var line:Int; + var character:Int; +} + +class Issue10712 extends unit.Test { + function test() { + var pos:Position = {line: 1, character: 10}; + var range:Range = {start: pos, end: pos}; + + var result:PrepareRenameResult = {range: range, placeholder: "Haxe"}; + result = range; + result = {defaultBehavior: true}; + + test2({range: range, placeholder: "Haxe"}); + test2(range); + test2({defaultBehavior: true}); + + test2(cast {range: range, placeholder: "Haxe"}); + test2(range); + test2(cast {defaultBehavior: true}); + + utest.Assert.pass(); + } + + static function test2(param:PrepareRenameResult) {} +} diff --git a/tests/unit/src/unit/issues/Issue10716.hx b/tests/unit/src/unit/issues/Issue10716.hx new file mode 100644 index 00000000..d83bec9a --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10716.hx @@ -0,0 +1,21 @@ +package unit.issues; + +using unit.issues.Issue10716.Extension; + +private class Extension { + macro public static function test(x:ExprOf) + return macro $x + $v{1}; +} + +class Issue10716 extends unit.Test { + function test() { + #if !macro + eq(11, 10.test()); + #end + + utest.Assert.pass(); + } +} + +function test() + return 1; diff --git a/tests/unit/src/unit/issues/Issue10720.hx b/tests/unit/src/unit/issues/Issue10720.hx new file mode 100644 index 00000000..235f9af6 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10720.hx @@ -0,0 +1,19 @@ +package unit.issues; + +class Issue10720 extends unit.Test { + function test() { + eq(1, switcheroo("😀 😀")); + eq(2, switcheroo("名 字")); + } + + function switcheroo(x:String) { + return switch (x) { + case '😀 😀': + 1; + case '名 字': + 2; + case _: + 0; + } + } +} diff --git a/tests/unit/src/unit/issues/Issue10728.hx b/tests/unit/src/unit/issues/Issue10728.hx new file mode 100644 index 00000000..615ddfc0 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10728.hx @@ -0,0 +1,21 @@ +package unit.issues; + +class Issue10728 extends Test { + function test(){ + eq("String", m()); + } + + static macro function m() { + var mono = haxe.macro.Context.makeMonomorph(); + var td = switch (haxe.macro.Context.getType("B")) { + case TType(td, _): td; + case _: throw "assert"; + }; + haxe.macro.Context.unify(haxe.macro.Context.getType("A"), TType(td, [mono])); + return macro $v{haxe.macro.TypeTools.toString(mono)}; + } +} + +private abstract A(Int) to B {} + +private typedef B = Int; diff --git a/tests/unit/src/unit/issues/Issue10730.hx b/tests/unit/src/unit/issues/Issue10730.hx new file mode 100644 index 00000000..fc08920d --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10730.hx @@ -0,0 +1,18 @@ +package unit.issues; + +import haxe.extern.EitherType; + +private abstract A(String) {} +private typedef ET = EitherType; + +private typedef DocumentFilter = { + final ?language:String; + final ?scheme:String; +} + +class Issue10730 extends Test { + function testSimn() { + final et:ET = {language: "haxe"}; + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10735.hx b/tests/unit/src/unit/issues/Issue10735.hx new file mode 100644 index 00000000..a5a7d5dc --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10735.hx @@ -0,0 +1,24 @@ +package unit.issues; + +@:generic +private abstract class A { + var i:T; + + public function new(i:T) { + this.i = i; + } + + abstract function foo():T; +} + +private class B extends A { + public function foo() { + return i; + } +} + +class Issue10735 extends Test { + function test() { + eq(12, new B(12).foo()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10738.hx b/tests/unit/src/unit/issues/Issue10738.hx new file mode 100644 index 00000000..6d0ef800 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10738.hx @@ -0,0 +1,19 @@ +package unit.issues; + +class Issue10738 extends Test { + function test() { + var c = new C(); + func = c.process.bind(42); + c = null; + eq(42, func()); + } + + var func:()->Int; +} + +private class C { + public function new() {} + public function process(value:Int):Int { + return value; + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10740.hx b/tests/unit/src/unit/issues/Issue10740.hx new file mode 100644 index 00000000..85dc44a2 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10740.hx @@ -0,0 +1,77 @@ +package unit.issues; + +import unit.HelperMacros.typeString; + +class Issue10740 extends Test { + // not local vars to prevent optimizations + var o:Null = null; + var o2:Null Int}> = { + var sideEffectCount = 0; + { + field1: 42, + field2: "hallo", + field3: {field4: true}, + field5: function() { + sideEffectCount++; + return [{field6: 28}]; + }, + sideEffectCount: () -> sideEffectCount + }; + }; + + function testTyping() { + // simple field access + eq("Null", typeString(o?.field1)); + eq("Null", typeString(o?.field2)); + + // field chain + eq("Null", typeString(o?.field3.field4)); + + // complex chain + eq("Null", typeString(o?.field5()[0].field6)); + + // nested chain + eq("Null", typeString(o?.field5()[0]?.field6)); + } + + function testNull() { + // simple field access + eq(null, o?.field1); + eq(null, o?.field2); + + // field chain + eq(null, o?.field3.field4); + + // complex chain + eq(null, o?.field5()[0].field6); + + // nested chain + eq(null, o?.field5()[0]?.field6); + } + + function testNonNull() { + // simple field access + eq(42, o2?.field1); + eq("hallo", o2?.field2); + + // field chain + eq(true, o2?.field3.field4); + + // complex chain + eq(28, o2?.field5()[0].field6); + eq(1, o2.sideEffectCount()); + + // nested chain + eq(28, o2?.field5()[0]?.field6); + eq(2, o2.sideEffectCount()); + eq(null, o2?.field5()[1]?.field6); + eq(3, o2.sideEffectCount()); + } +} + +private typedef S = { + var field1:Int; + var field2:String; + var field3:{field4:Bool}; + var field5:() -> Array<{field6:Null}>; +} diff --git a/tests/unit/src/unit/issues/Issue10743.hx b/tests/unit/src/unit/issues/Issue10743.hx new file mode 100644 index 00000000..eb2f87d3 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10743.hx @@ -0,0 +1,14 @@ +package unit.issues; + +@:keep +private class C { + var o = () -> { + return 28; + }; +} + +class Issue10743 extends Test { + function test() { + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10744.hx b/tests/unit/src/unit/issues/Issue10744.hx new file mode 100644 index 00000000..37abbea9 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10744.hx @@ -0,0 +1,179 @@ +package unit.issues; + +import unit.HelperMacros.typeString; + +class Issue10744 extends Test { + function test() { + var v:Null = 10; + eq("Int", typeString(v ?? return)); + eq("Int", typeString(v ?? throw true)); + for (i in 0...1) { + eq("Int", typeString(v ?? break)); + eq("Int", typeString(v ?? continue)); + } + eq("Int", typeString(v ?? { + (throw "nope"); + })); + eq("Null", typeString(v ?? { + if (Std.random(0) == 0) + return; + else + v; + })); + eq("Null", typeString(v ?? { + function foo() + return; + v; + })); + eq("Int", typeString(v ?? { + if (Std.random(0) == 0) + return; + else + throw "nope"; + })); + eq("Int", typeString(v ?? { + Std.parseInt(return); + })); + eq("Int", typeString(v ?? { + (return)(); + })); + eq("Int", typeString(v ?? { + v + return; + v; + })); + eq("Null", typeString(v ?? { + false && return ; + v; + })); + eq("Int", typeString(v ?? { + true && return ; + v; + })); + eq("Int", typeString(v ?? { + false || return ; + v; + })); + eq("Int", typeString(v ?? { + final a = return; + v; + })); + eq("Int", typeString(v ?? { + [0, return, 2]; + v; + })); + eq("Null", typeString(v ?? { + for (i in 0...Std.random(1)) { + return; + } + v; + })); + eq("Int", typeString(v ?? { + for (i in [0, return, 2]) { + break; + } + v; + })); + eq("Int", typeString(v ?? { + switch (null) { + case _: return; + } + v; + })); + eq("Int", typeString(v ?? { + switch (return) { + case _: null; + } + v; + })); + eq("Int", typeString(v ?? { + final arr = []; + arr[return]; + v; + })); + eq("Int", typeString(v ?? { + new EReg("", return); + v; + })); + eq("Null", typeString(v ?? { + do { + break; + return; + } while (true); + v; + })); + eq("Int", typeString(v ?? { + do { + break; // die + return; + } while (true); // resurrect + return; // die again + v; + })); + eq("Null", typeString(v ?? { + try { + throw null; + } catch (e) {} + v; + })); + eq("Int", typeString(v ?? { + try { + throw null; + } catch (e) { + return; + } + v; + })); + eq("Null", typeString(v ?? { + try { + throw null; + } catch (e:String) { + // fall through + } catch (e) { + return; + } + v; + })); + eq("Null", typeString(v ?? { + try { + return; + } catch (e:String) { + // fall through + } catch (e) { + return; + } + v; + })); + eq("Int", typeString(v ?? { + try { + return; + } catch (e:String) { + return; + } catch (e) { + return; + } + v; + })); + eq("Null", typeString(v ?? { + try { + // something here COULD throw and end up in the fall through case + } catch (e:String) { + // fall through + } catch (e) { + return; + } + v; + })); + eq("Int", typeString(v ?? { + try { + return; + } + v; + })); + eq("Null", typeString(v ?? { + try { + // fall through + } + v; + })); + } +} diff --git a/tests/unit/src/unit/issues/Issue10746.hx b/tests/unit/src/unit/issues/Issue10746.hx new file mode 100644 index 00000000..fbc1f9c1 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10746.hx @@ -0,0 +1,13 @@ +package unit.issues; + +class Issue10746 extends Test { +#if js + @:keep var _ = @:privateAccess js.Boot.getClass; + + function test() { + js.Syntax.code("String.prototype.__class__ = String"); + t(true); + } +#end +} + diff --git a/tests/unit/src/unit/issues/Issue10748.hx b/tests/unit/src/unit/issues/Issue10748.hx new file mode 100644 index 00000000..b0f42081 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10748.hx @@ -0,0 +1,21 @@ +package unit.issues; + +private interface I { + function foo(v:T):Void; +} + +private abstract class Base implements I { + public function new() {} +} + +private class Real extends Base { + public function foo(v:Int) {} +} + +class Issue10748 extends Test { + function test() { + var r = new Real(); + r.foo(33); + utest.Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10752.hx b/tests/unit/src/unit/issues/Issue10752.hx new file mode 100644 index 00000000..61d25773 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10752.hx @@ -0,0 +1,16 @@ +package unit.issues; + +class Issue10752 extends Test { + function shl(x, y) + return x << y; + + #if (!php && !python && !lua) + function test() { + eq(2, shl(1, 33)); + eq(2, 1 << 33); + + eq(1 >> 1, 1 >> 33); + eq(1 >>> 1, 1 >>> 33); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10754.hx b/tests/unit/src/unit/issues/Issue10754.hx new file mode 100644 index 00000000..dc81975b --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10754.hx @@ -0,0 +1,9 @@ +package unit.issues; + +class Issue10754 extends Test { + function test() { + var a = [0, 1, 2]; + var boundPush = a.push.bind(); + eq(boundPush(3), 4); + } +} diff --git a/tests/unit/src/unit/issues/Issue10761.hx b/tests/unit/src/unit/issues/Issue10761.hx new file mode 100644 index 00000000..b5bebbd4 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10761.hx @@ -0,0 +1,17 @@ +package unit.issues; + +import haxe.Rest; + +class Issue10761 extends Test { + function rest(args:Rest) { + return args; + } + + #if !erase_generics + function test() { + aeq([0, 1], rest(0, 1)); // works + aeq([0, 1], rest(...[0, 1])); // works + aeq([0, 1], rest(...[for (i in 0...2) i])); // errors + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10776.hx b/tests/unit/src/unit/issues/Issue10776.hx new file mode 100644 index 00000000..943a76af --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10776.hx @@ -0,0 +1,35 @@ +package unit.issues; + +import utest.Assert; + +@:forward +private abstract DialogConfig(DialogConfigData) from DialogConfigData { + @:from static function fromRenderResult(r:RenderResult):DialogConfig + return {priority: 0, ui: r}; +} + +private typedef DialogConfigData = { + final priority:Int; + final ui:DialogUi; +} + +@:callable +private abstract DialogUi((close:() -> Void)->RenderResult) from (close:() -> Void)->RenderResult { + inline function new(f) + this = f; + + @:from static function fromRenderResult(r:RenderResult) + return new DialogUi(_ -> r); +} + +private abstract RenderResult(String) to String from String {} + +class Issue10776 extends Test { + function test() { + var cfg:DialogConfig = { + priority: 0, + ui: (null : RenderResult), + }; + Assert.pass(); + } +} diff --git a/tests/unit/src/unit/issues/Issue10799.hx b/tests/unit/src/unit/issues/Issue10799.hx new file mode 100644 index 00000000..9ac2e9b5 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10799.hx @@ -0,0 +1,79 @@ +package unit.issues; + +class Issue10799 extends Test { + #if lua + var myField:(Int) -> Int; + + private static function foo(x:Int):Int + return x * 3; + + private dynamic function bar(x:Int):Int { + eq("table", untyped __lua__("_G.type(self)")); + eq("number", untyped __lua__("_G.type(x)")); + return x * 4; + } + + private dynamic function baz(x:Int):Int { + throw "not implemented"; + } + + private function returnsField():(Int) -> Int { + return this.myField; + } + + private function returnsMethod():(Int) -> Int { + return this.bar; + } + + private function returnsClosure():(Int) -> Int { + final obj = lua.Lua.assert({num: 7}); + final fn:(Int) -> Int = x -> x * obj.num; + lua.Lua.assert(fn); + return fn; + } + + public function test() { + this.myField = x -> x * 2; + eq(6, untyped __lua__("self.myField(3)")); + this.myField = Issue10799.foo; + eq(9, untyped __lua__("self.myField(3)")); + eq(9, untyped __lua__("__unit_issues_Issue10799.foo(3)")); + this.myField = this.bar; + eq(12, untyped __lua__("self.myField(3)")); + exc(() -> untyped __lua__("self.bar(3)")); + this.myField = x -> x * 2; + this.bar = this.myField; + eq(6, untyped __lua__("self:bar(3)")); + this.baz = this.bar; + eq(6, untyped __lua__("self:baz(3)")); + + var localVar = this.myField; + lua.Lua.assert(localVar); + eq(6, untyped __lua__("localVar(3)")); + + localVar = this.bar; + lua.Lua.assert(localVar); + eq(6, untyped __lua__("localVar(3)")); + + final field = this.returnsField(); + eq(6, untyped __lua__("field(3)")); + final method = this.returnsMethod(); + eq(6, untyped __lua__("method(3)")); + final closure = this.returnsClosure(); + eq(21, untyped __lua__("closure(3)")); + + final anon = lua.Lua.assert({ + fromField: this.myField, + fromStatic: Issue10799.foo, + fromMethod: this.bar, + }); + + exc(() -> untyped __lua__("anon.fromField(3)")); + eq(6, untyped __lua__("anon:fromField(3)")); + exc(() -> untyped __lua__("anon.fromStatic(3)")); + eq(9, untyped __lua__("anon:fromStatic(3)")); + exc(() -> untyped __lua__("anon.fromMethod(3)")); + eq(6, untyped __lua__("anon:fromMethod(3)")); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10821.hx.disabled b/tests/unit/src/unit/issues/Issue10821.hx.disabled new file mode 100644 index 00000000..a6945b68 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10821.hx.disabled @@ -0,0 +1,15 @@ +package unit.issues; + +class Issue10821 extends Test { + function test() { + eq('foo', Std.string(new Dummy())); + } +} + +private class Dummy { + var toString:()->String; + + public function new() { + toString = () -> 'foo'; + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10830.hx b/tests/unit/src/unit/issues/Issue10830.hx new file mode 100644 index 00000000..33d6bf23 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10830.hx @@ -0,0 +1,103 @@ +package unit.issues; + +import utest.Assert; + +#if (cpp && !cppia) + +@:native('customnamespace::MyExternEnum') +extern enum MyExternEnum { + First; + Second(v : String); +} + +@:headerCode(' +HX_DECLARE_CLASS1(customnamespace, MyExternEnum) + +namespace customnamespace { + class MyExternEnum_obj : public hx::EnumBase_obj { + public: + typedef MyExternEnum_obj OBJ_; + + enum Type { + TFirst, + TSecond + }; + + MyExternEnum_obj() = default; + + HX_DO_ENUM_RTTI; + + static bool __GetStatic(const String&, Dynamic&, hx::PropertyAccess); + + String GetEnumName() const { return HX_CSTRING("MyExternEnum"); } + String __ToString() const { return HX_CSTRING("MyExternEnum.") + _hx_tag; } + + static MyExternEnum First(); + static Dynamic First_dyn(); + + static MyExternEnum Second(String); + static Dynamic Second_dyn(); + }; +} +') +@:cppFileCode(' +namespace customnamespace { + MyExternEnum MyExternEnum_obj::First() { + return hx::CreateEnum(HX_CSTRING("First"), Type::TFirst, 0); + } + + MyExternEnum MyExternEnum_obj::Second(String v) { + return hx::CreateEnum(HX_CSTRING("Second"), Type::TSecond, 1)->_hx_init(0, v); + } + + bool MyExternEnum_obj::__GetStatic(const String& _inName, Dynamic& _outValue, hx::PropertyAccess _propAccess) { + if (_inName == HX_CSTRING("First")) { _outValue = MyExternEnum_obj::First_dyn(); return true; } + if (_inName == HX_CSTRING("Second")) { _outValue = MyExternEnum_obj::Second_dyn(); return true; } + return hx::EnumBase_obj::__GetStatic(_inName, _outValue, _propAccess); + } + + hx::Val MyExternEnum_obj::__Field(const String& _inName, hx::PropertyAccess _propAccess) { + if (_inName == HX_CSTRING("First")) { return MyExternEnum_obj::First_dyn(); } + if (_inName == HX_CSTRING("Second")) { return MyExternEnum_obj::Second_dyn(); } + return hx::EnumBase_obj::__Field(_inName, _propAccess); + } + + int MyExternEnum_obj::__FindIndex(String _inName) { + if (_inName == HX_CSTRING("First")) { return Type::TFirst; } + if (_inName == HX_CSTRING("Second")) { return Type::TSecond; } + return hx::EnumBase_obj::__FindIndex(_inName); + } + + int MyExternEnum_obj::__FindArgCount(String _inName) { + if (_inName == HX_CSTRING("First")) { return 0; } + if (_inName == HX_CSTRING("Second")) { return 1; } + return hx::EnumBase_obj::__FindArgCount(_inName); + } + + HX_DEFINE_CREATE_ENUM(MyExternEnum_obj) + + STATIC_HX_DEFINE_DYNAMIC_FUNC0(MyExternEnum_obj, First, return) + + STATIC_HX_DEFINE_DYNAMIC_FUNC1(MyExternEnum_obj, Second, return) + + hx::Class MyExternEnum_obj::__mClass; +} +') +#end +class Issue10830 extends Test { + + #if (cpp && !cppia) + function test() { + switch create() { + case First: + Assert.fail(); + case Second(v): + Assert.equals('Hello, World!', v); + } + } + + function create() : MyExternEnum { + return untyped __cpp__('::customnamespace::MyExternEnum_obj::Second(HX_CSTRING("Hello, World!"))'); + } + #end +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10831.hx b/tests/unit/src/unit/issues/Issue10831.hx new file mode 100644 index 00000000..413fc67f --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10831.hx @@ -0,0 +1,25 @@ +package unit.issues; + +import utest.Assert; + +#if cpp +private enum MyEnum { + EnumTest(p:cpp.Pointer); +} +#end + +class Issue10831 extends Test { + #if cpp + function test() { + switch (get()) { + case EnumTest(p): + trace(p); + } + Assert.pass(); + } + + function get() { + return EnumTest(null); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10834.hx b/tests/unit/src/unit/issues/Issue10834.hx new file mode 100644 index 00000000..2279b14e --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10834.hx @@ -0,0 +1,12 @@ +package unit.issues; + +class Issue10834 extends Test { + var tmp:Any; + var a:Dynamic = []; + + function test() { + tmp = a[0]; + eq(0, a.length); + eq(#if (js || flash) '' #else '[]' #end, a.toString()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10836.hx b/tests/unit/src/unit/issues/Issue10836.hx new file mode 100644 index 00000000..b9cc0313 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10836.hx @@ -0,0 +1,21 @@ +package unit.issues; + +class Issue10836 extends Test { + function test() { + var foo = new Foo(); + foo.bar = () -> 'Foo::new bar'; + eq('Foo::bar', foo.run()); + } +} + +private class Foo { + public var run:Void->String; + + public function new() { + run = bar; + } + + public dynamic function bar() { + return 'Foo::bar'; + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10845.hx b/tests/unit/src/unit/issues/Issue10845.hx new file mode 100644 index 00000000..c605f12a --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10845.hx @@ -0,0 +1,47 @@ +package unit.issues; + +import utest.Assert; + +private var count = 0; + +private abstract MyInt(Int) from Int to Int { + @:op(A += B) + public inline function addEq(x:Int):MyInt { + count++; + return this; + } + + @:op(A++) + public inline function increment():MyInt { + count++; + return this; + } +} + +class Issue10845 extends Test { + final field:MyInt = 1; + + function test():Void { + final local:MyInt = 1; + + field++; + eq(1, count); + local++; + eq(2, count); + + field += 1; + eq(3, count); + local += 1; + eq(4, count); + + field.addEq(1); + eq(5, count); + local.addEq(1); + eq(6, count); + + field.increment(); + eq(7, count); + local.increment(); + eq(8, count); + } +} diff --git a/tests/unit/src/unit/issues/Issue10867.hx b/tests/unit/src/unit/issues/Issue10867.hx new file mode 100644 index 00000000..f2d51579 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10867.hx @@ -0,0 +1,19 @@ +package unit.issues; + +class Issue10867 extends Test { + function test() { + var r = ~/\/\*(((?!\*\/).)*)$/s; + var a = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; + + var test = "/*"; + var res = new StringBuf(); + for (i in 0...10) { + test += a; + res.add('iter $i: ${r.match(test)} '); + } + + res.add("Finished"); + eq("iter 0: true iter 1: true iter 2: true iter 3: true iter 4: true iter 5: true iter 6: true iter 7: true iter 8: true iter 9: true Finished", + res.toString()); + } +} diff --git a/tests/unit/src/unit/issues/Issue10876.hx b/tests/unit/src/unit/issues/Issue10876.hx new file mode 100644 index 00000000..a67d7a30 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10876.hx @@ -0,0 +1,41 @@ +package unit.issues; + +import utest.Assert; + +#if (cpp && !cppia) +@:native('MY_DEFINE') +extern class MyDefine {} + +@:unreflective +@:structAccess +@:native('my_extern') +extern class MyExtern { + function new(); + + function create() : T; +} + +@:headerCode(' + +typedef int MY_DEFINE; + +template +class my_extern { +public: + T create() { + return T(); + } +}; + +') +#end +class Issue10876 extends Test { + #if (cpp && !cppia) + function test() { + final vec = cpp.Pointer.fromStar(new MyExtern()); + final num = vec.ptr.create(); + + Assert.equals(0, num); + } + #end +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue10906.hx b/tests/unit/src/unit/issues/Issue10906.hx new file mode 100644 index 00000000..7653a5eb --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10906.hx @@ -0,0 +1,20 @@ +package unit.issues; + +import haxe.Rest; +import utest.Assert; + +class Issue10906 extends Test { + #if !erase_generics + function test() { + var a:Array = new Array(); + a.push(1); + a.push(2); + a.push(3); + Assert.same([1, 2, 3], a); + eq(3, a.length); + var r = Rest.of(a); + eq(1, r[0]); + eq(3, r.length); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10960.hx b/tests/unit/src/unit/issues/Issue10960.hx new file mode 100644 index 00000000..b9e2ae47 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10960.hx @@ -0,0 +1,12 @@ +package unit.issues; + +class Issue10960 extends Test { + function test() { + final obj:Dynamic = 0; + eq("", mm(obj)); + } + + static macro function mm(any:Any) { + return macro ""; + } +} diff --git a/tests/unit/src/unit/issues/Issue10965.hx b/tests/unit/src/unit/issues/Issue10965.hx new file mode 100644 index 00000000..0ab14b2a --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10965.hx @@ -0,0 +1,39 @@ +package unit.issues; + +class Issue10965 extends Test { + #if jvm + function testNeg() { + var a:Single = 1; + var c = -a; + eq((-1 : Single), c); + } + + function testIncPre() { + var a:Single = 1; + var c = ++a; + eq((2 : Single), a); + eq((2 : Single), c); + } + + function testIncPost() { + var a:Single = 1; + var c = a++; + eq((2 : Single), a); + eq((1 : Single), c); + } + + function testDecPre() { + var a:Single = 1; + var c = --a; + eq((0 : Single), a); + eq((0 : Single), c); + } + + function testDecPost() { + var a:Single = 1; + var c = a--; + eq((0 : Single), a); + eq((1 : Single), c); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue10993.hx b/tests/unit/src/unit/issues/Issue10993.hx new file mode 100644 index 00000000..4be324ca --- /dev/null +++ b/tests/unit/src/unit/issues/Issue10993.hx @@ -0,0 +1,17 @@ +package unit.issues; + +enum Issue10993_TestEnum { + FOO; +} + +class Issue10993 extends Test { + function testHasFieldWithEnum() { + final foo = Issue10993_TestEnum.FOO; + eq(false, Reflect.hasField(foo, "bar")); + } + + function testHasFieldWithFunction() { + final foo = () -> null; + eq(false, Reflect.hasField(foo, "bar")); + } +} diff --git a/tests/unit/src/unit/issues/Issue11007.hx b/tests/unit/src/unit/issues/Issue11007.hx new file mode 100644 index 00000000..7c43eb65 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11007.hx @@ -0,0 +1,8 @@ +package unit.issues; + +class Issue11007 extends Test { + function test() { + var a:Dynamic = {day: "numeric"}; + eq("numeric", a.day); + } +} diff --git a/tests/unit/src/unit/issues/Issue11040.hx b/tests/unit/src/unit/issues/Issue11040.hx new file mode 100644 index 00000000..05dc4ff1 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue11040.hx @@ -0,0 +1,50 @@ +package unit.issues; + +class Issue11040 extends Test { + function run() { + var tagName = "Array"; + var text:String = ">"; + var startTag = '<$tagName'; + var endTag = ''; + + var depth = 0; + var index = 0; + var buf = new StringBuf(); + function append(s:String) { + buf.add(s); + buf.add("\n"); + } + append("enter loop"); + while (true) { + append("looping"); + var indexStartTag = text.indexOf(startTag, index); + var indexEndTag = text.indexOf(endTag, index); + if ((indexStartTag == -1) && (indexEndTag == -1)) { + append(">>> should exit here >>>"); + return buf.toString(); + } + index++; + switch (text.charAt(index)) { + case " " | "/" | ">": + default: + append("default -> continue loop"); + continue; + } + if (depth <= 0) { + break; + } + } + text = text.substr(0, index); + append("exit loop"); + return buf.toString(); + } + + function test() { + eq("enter loop +looping +default -> continue loop +looping +>>> should exit here >>> +", run()); + } +} diff --git a/tests/unit/src/unit/issues/Issue2776.hx b/tests/unit/src/unit/issues/Issue2776.hx index 93057399..5520b5e5 100644 --- a/tests/unit/src/unit/issues/Issue2776.hx +++ b/tests/unit/src/unit/issues/Issue2776.hx @@ -1,34 +1,35 @@ package unit.issues; + import unit.Test; class Issue2776 extends Test { function test() { var type = null; - switch(type) { + switch (type) { case String: case Int: } unit.HelperMacros.typedAs(type, getClassT()); var type = null; - switch(type) { + switch (type) { case haxe.macro.Expr.ExprDef: case haxe.macro.Expr.Constant: } unit.HelperMacros.typedAs(type, getEnumT()); var type = null; - t(unit.HelperMacros.typeError(switch(type) { + t(unit.HelperMacros.typeError(switch (type) { case String: case haxe.macro.Expr.ExprDef: })); } - static function getClassT():Class { + static function getClassT():#if cs Class #else Null> #end { return (null : Class); } - static function getEnumT():Enum { + static function getEnumT():#if cs Enum #else Null> #end { return (null : Enum); } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue2778.hx b/tests/unit/src/unit/issues/Issue2778.hx index cd03d23c..d680141e 100644 --- a/tests/unit/src/unit/issues/Issue2778.hx +++ b/tests/unit/src/unit/issues/Issue2778.hx @@ -6,7 +6,6 @@ enum E { } class Issue2778 extends Test { - function test() { eq(true, sameType(BoolLit(true), BoolLit(true))); eq(false, sameType(BoolLit(false), BoolLit(true))); @@ -17,10 +16,11 @@ class Issue2778 extends Test { t(unit.HelperMacros.typeError(sameType(BoolLit(true), IntLit(1)))); } + @:haxe.warning("-WGenerator") static function sameType(o1:E, o2:E):S { return switch [o1, o2] { case [BoolLit(b1), BoolLit(b2)]: b1 && b2; case [IntLit(i1), IntLit(i2)]: i1 + i2; } } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue3396.hx b/tests/unit/src/unit/issues/Issue3396.hx index 847ed8ef..c6f91d8b 100644 --- a/tests/unit/src/unit/issues/Issue3396.hx +++ b/tests/unit/src/unit/issues/Issue3396.hx @@ -8,7 +8,11 @@ class Issue3396 extends Test { Reflect.makeVarArgs(f); Reflect.makeVarArgs(f2); var s:String = f([]); - unit.HelperMacros.typedAs(r, (r:String)); - unit.HelperMacros.typedAs(f2([]), (null:Void)); + #if cs + unit.HelperMacros.typedAs(r, (r : String)); + #else + unit.HelperMacros.typedAs(r, (r : Null)); + #end + unit.HelperMacros.typedAs(f2([]), (null : Void)); } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue3861.hx b/tests/unit/src/unit/issues/Issue3861.hx index 6dc72022..06f34c6e 100644 --- a/tests/unit/src/unit/issues/Issue3861.hx +++ b/tests/unit/src/unit/issues/Issue3861.hx @@ -1,10 +1,11 @@ package unit.issues; class Issue3861 extends unit.Test { + @:haxe.warning("-WVarInit") function test() { var a; var b = function() return a; a = 2; eq(2, b()); } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue4327.hx b/tests/unit/src/unit/issues/Issue4327.hx index 5f06c8ac..910be448 100644 --- a/tests/unit/src/unit/issues/Issue4327.hx +++ b/tests/unit/src/unit/issues/Issue4327.hx @@ -3,6 +3,7 @@ package unit.issues; @:callable abstract Example(()->String) { public function new() { + this = null; this = fun; } diff --git a/tests/unit/src/unit/issues/Issue4578.hx b/tests/unit/src/unit/issues/Issue4578.hx index f9d6c9d8..120ce683 100644 --- a/tests/unit/src/unit/issues/Issue4578.hx +++ b/tests/unit/src/unit/issues/Issue4578.hx @@ -1,33 +1,35 @@ package unit.issues; private class TList {} -private class TCons extends TList {} +private class TCons extends TList {} private class TNil extends TList {} private enum Stack { - Nil: Stack; - Cons(x: X, xs: Stack): Stack>; + Nil:Stack; + Cons + (x : X, xs : Stack) : Stack>; } -private interface Instr { - function denote(s: Stack): Stack; +private interface Instr { + function denote(s:Stack):Stack; } -private class IUnOp implements Instr,TCons> { - var f: X->Y; +private class IUnOp implements Instr, TCons> { + var f:X->Y; public function new(f) { this.f = f; } - public function denote(s: Stack>): Stack> { - return switch(s) { + public function denote(s:Stack>):Stack> { + return switch (s) { case Cons(x, s): Cons(f(x), s); } } } +@:haxe.warning("-WGenerator") class Issue4578 extends Test { function test() { var i = new IUnOp(function(x) return x * 2); @@ -35,9 +37,9 @@ class Issue4578 extends Test { eq(20, getHead(v)); } - static function getHead(s:Stack>):S { + static function getHead(s:Stack>):S { return switch (s) { case Cons(x, _): x; } } -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue5078.hx b/tests/unit/src/unit/issues/Issue5078.hx index 88c719b0..573ce9dd 100644 --- a/tests/unit/src/unit/issues/Issue5078.hx +++ b/tests/unit/src/unit/issues/Issue5078.hx @@ -1,12 +1,12 @@ package unit.issues; class Issue5078 extends Test { - #if !(js || php || lua) + #if !(js || lua) static function getToLower() return "ABC".toLowerCase; #end function test() { - #if !(js || php || lua) + #if !(js || lua) eq(getToLower()(), "abc"); #else noAssert(); diff --git a/tests/unit/src/unit/issues/Issue5890.hx b/tests/unit/src/unit/issues/Issue5890.hx new file mode 100644 index 00000000..6075ccb9 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue5890.hx @@ -0,0 +1,53 @@ +package unit.issues; + +class Issue5890 extends unit.Test { + function test() { + new ReporterImplSuc2(); + noAssert(); + } +} + +interface Reporter { + public function shoot(ctx:T):Void; +} + +class ReporterImpl implements Reporter { + public function shoot(ctx:T):Void {} +} + +class Impl2 implements Reporter { + public function shoot(a:T) {} +} + +class ReporterImplSuc extends ReporterImpl implements Reporter {} + +class ReporterImplSuc2 extends ReporterImpl implements Reporter { + public function new() {} +} + +class HierarchyImpl implements ISuc { + public function shoot(a:A) { + trace(a + 5); + } +} + +class HierarchyImpl2 implements ISuc { + public function shoot(a:A) {} +} + +interface IBase2 { + function shoot(a:A):Void; +} + +interface OtherBase {} +interface ISuc extends IBase2 extends OtherBase {} +class StatBase {} +class ShotContext> {} + +interface WeaponReporter { + public function shoot>(ctx:ShotContext):Void; +} + +class StatWeaponReporterImpl implements WeaponReporter { + public function shoot>(ctx:ShotContext):Void {} +} diff --git a/tests/unit/src/unit/issues/Issue6017.hx b/tests/unit/src/unit/issues/Issue6017.hx new file mode 100644 index 00000000..8494fe5e --- /dev/null +++ b/tests/unit/src/unit/issues/Issue6017.hx @@ -0,0 +1,70 @@ +package unit.issues; + +import utest.Assert; +import haxe.PosInfos; + +class Issue6017 extends Test { + function checkPos(i:Any, ?p:PosInfos) { + Assert.same(p, Abstr.p); + } + + function test() { + checkPos((123:Abstr)); + var i:Abstr = 123; + checkPos(i + 100); + checkPos((i:String)); + checkPos(i[0]); + checkPos(i[0] = 1); + checkPos(i()); + checkPos(i.hello); + checkPos(i.hello = 'world'); + } +} + +private abstract Abstr(Int) { + static public var p:PosInfos; + + @:from static public function fromInt(i:Int, ?p:PosInfos) { + Abstr.p = p; + return new Abstr(i); + } + + @:to public function toString(?p:PosInfos):String { + Abstr.p = p; + return 'Abstr $this'; + } + + @:op(A + B) public function addInt(i:Int, ?p:PosInfos) { + Abstr.p = p; + return new Abstr(i + this); + } + + @:op([]) public function arrayGet(i:Int, ?p:PosInfos) { + Abstr.p = p; + return i; + } + + @:op([]) public function arraySet(i:Int, v:Int, ?p:PosInfos) { + Abstr.p = p; + return v; + } + + @:op(a()) public function call(?p:PosInfos) { + Abstr.p = p; + return Std.random(10); + } + + @:op(a.b) public function resolveGet(f:String, ?p:PosInfos) { + Abstr.p = p; + return f; + } + + @:op(a.b) public function resolveSet(f:String, v:String, ?p:PosInfos) { + Abstr.p = p; + return v; + } + + function new(i:Int) { + this = i; + } +} \ No newline at end of file diff --git a/tests/unit/src/unit/issues/Issue6063.hx b/tests/unit/src/unit/issues/Issue6063.hx new file mode 100644 index 00000000..30bf2c01 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue6063.hx @@ -0,0 +1,35 @@ +package unit.issues; + +import haxe.macro.Context; +import haxe.macro.Expr; +import unit.HelperMacros.*; + +class Issue6063 extends Test { + macro static function convert(e:Expr) { + var t = Context.typeof(e); + var ct = Context.toComplexType(t); + return macro @:pos(e.pos) (null : $ct); + } + + function testFinal() { + #if !macro + var x:{final s:String;} = null; + var y = convert(x); + x = y; + t(typeError(y.s = "foo")); + #end + } + + function testAccess() { + var o:{var x(default, null):Int;} = null; + var o2 = convert(o); + o = o2; + t(typeError(o2.x = 12)); + } + + function testFunction() { + var f:(?a:String) -> Void = null; + var e = convert(f); + eq("(?a : String) -> Void", typeString(e)); + } +} diff --git a/tests/unit/src/unit/issues/Issue6457.hx b/tests/unit/src/unit/issues/Issue6457.hx index 77384ad1..b66cf1f6 100644 --- a/tests/unit/src/unit/issues/Issue6457.hx +++ b/tests/unit/src/unit/issues/Issue6457.hx @@ -12,9 +12,7 @@ class Issue6457 extends Test { var vals = [ for (k in m) k]; eq(vals.length, 1); eq(vals[0], 10); -#if !cpp - eq(m.toString(), '{0 => 10}'); -#end + eq(m.toString(), '[0 => 10]'); var m = new Map(); m.set("c", 1); @@ -27,8 +25,6 @@ class Issue6457 extends Test { var vals = [ for (k in m) k]; eq(vals.length, 1); eq(vals[0], 10); -#if !cpp - eq(m.toString(), '{z => 10}'); -#end + eq(m.toString(), '[z => 10]'); } } diff --git a/tests/unit/src/unit/issues/Issue6561.hx b/tests/unit/src/unit/issues/Issue6561.hx index d630be9b..756c66b0 100644 --- a/tests/unit/src/unit/issues/Issue6561.hx +++ b/tests/unit/src/unit/issues/Issue6561.hx @@ -4,16 +4,16 @@ private enum Log { NotLog(msg:String):Log; } - class Issue6561 extends unit.Test { function test() { eq("hello", apply(NotLog("hello"))); } - static function apply(f:Log):A { + @:haxe.warning("-WGenerator") + static function apply(f:Log):A { return switch f { case NotLog(msg): - msg; + msg; } - } -} \ No newline at end of file + } +} diff --git a/tests/unit/src/unit/issues/Issue7736.hx b/tests/unit/src/unit/issues/Issue7736.hx new file mode 100644 index 00000000..457082be --- /dev/null +++ b/tests/unit/src/unit/issues/Issue7736.hx @@ -0,0 +1,21 @@ +package unit.issues; + +class Issue7736 extends Test { + #if !cs + @:nullSafety + function test() { + var found = null; + for (i in 0...10) { + if (i == 5) { + found = i; + break; + } + } + unit.HelperMacros.typedAs(found, (null : Null)); + if (found != null) { + var x:Int = found; + eq(5, x); + } + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue8218.hx b/tests/unit/src/unit/issues/Issue8218.hx index b3dd6f66..6fc1130e 100644 --- a/tests/unit/src/unit/issues/Issue8218.hx +++ b/tests/unit/src/unit/issues/Issue8218.hx @@ -26,17 +26,6 @@ private class Child extends Lib2 { test.eq(f(), "hello"); test.eq(call(f), "hello"); - // super. protected access - test.eq(super.x, 50); - super.x = 55; - test.eq(super.x, 55); - - test.eq(super.i, 10); - super.i = 15; - test.eq(super.i, 15); - - test.eq(super.f(), "hello"); - // static protected access test.eq(Lib.sx, 42); Lib.sx = 50; diff --git a/tests/unit/src/unit/issues/Issue8401.hx b/tests/unit/src/unit/issues/Issue8401.hx new file mode 100644 index 00000000..739cf624 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue8401.hx @@ -0,0 +1,50 @@ +package unit.issues; + +class Issue8401 extends unit.Test { +#if python + function testNew() { + var sock = new python.net.SslSocket(); + // With Issue8401, construction fails immediately; if we get this far, it's a pass + utest.Assert.pass(); + } + + @:access(python.net.SslSocket.__s) + @:access(python.net.SslSocket.wrapSocketWithSslContext) + function testTimeout() { + var sock = new python.net.SslSocket(); + eq(null, sock.__s.gettimeout()); + sock.setTimeout(500); + feq(500, sock.__s.gettimeout()); + // This will change __s. Make sure we set the timeout properly. + sock.wrapSocketWithSslContext("127.0.0.1"); + feq(500, sock.__s.gettimeout()); + } + + #if (python_verion >= 3.7) + @:access(python.net.SslSocket.__s) + @:access(python.net.SslSocket.wrapSocketWithSslContext) + function testBlocking() { + var sock = new python.net.SslSocket(); + t(sock.__s.getblocking()); + sock.setBlocking(false); + f(sock.__s.getblocking()); + // This will change __s. Make sure we set the blocking flag properly. + sock.wrapSocketWithSslContext("127.0.0.1"); + f(sock.__s.getblocking()); + } + #end + + @:access(python.net.SslSocket.__s) + @:access(python.net.SslSocket.wrapSocketWithSslContext) + function testFastSend() { + var sock = new python.net.SslSocket(); + eq(0, sock.__s.getsockopt(python.lib.Socket.SOL_TCP, python.lib.Socket.TCP_NODELAY)); + sock.setFastSend(true); + // NOTE: this number can vary per platform; non-zero means true/enabled + utest.Assert.notEquals(0, sock.__s.getsockopt(python.lib.Socket.SOL_TCP, python.lib.Socket.TCP_NODELAY)); + // This will change __s. Make sure we set the sock opt properly. + sock.wrapSocketWithSslContext("127.0.0.1"); + utest.Assert.notEquals(0, sock.__s.getsockopt(python.lib.Socket.SOL_TCP, python.lib.Socket.TCP_NODELAY)); + } +#end +} diff --git a/tests/unit/src/unit/issues/Issue8869.hx b/tests/unit/src/unit/issues/Issue8869.hx index 0173cc6a..c0a190ac 100644 --- a/tests/unit/src/unit/issues/Issue8869.hx +++ b/tests/unit/src/unit/issues/Issue8869.hx @@ -5,9 +5,11 @@ class Issue8869 extends Test { noAssert(); } - function checkIntersectionConstraintInParenthesesVoid> & Dummy)>(cl:Class) {} + function checkIntersectionConstraintInParentheses Void> & Dummy)>(cl:Class) {} + + function checkFunctionTypeConstraint Void>() {} } private class Dummy { public function new() {} -} \ No newline at end of file +} diff --git a/tests/unit/src/unit/issues/Issue9034.hx b/tests/unit/src/unit/issues/Issue9034.hx index 4102b54b..f4704d14 100644 --- a/tests/unit/src/unit/issues/Issue9034.hx +++ b/tests/unit/src/unit/issues/Issue9034.hx @@ -5,7 +5,7 @@ import unit.Test; class Issue9034 extends Test{ #if java function test() { - bar(java.nio.file.Paths.get('build.hxml', new java.NativeArray(0))); + bar(java.nio.file.Paths.get('build.hxml')); utest.Assert.pass(); } diff --git a/tests/unit/src/unit/issues/Issue9198.hx b/tests/unit/src/unit/issues/Issue9198.hx new file mode 100644 index 00000000..64ed0b64 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue9198.hx @@ -0,0 +1,9 @@ +package unit.issues; + +import unit.Test; + +class Issue9198 extends Test { + public function test() { + eq(null, Std.parseInt("axolotl")); + } +} diff --git a/tests/unit/src/unit/issues/Issue9543.hx b/tests/unit/src/unit/issues/Issue9543.hx new file mode 100644 index 00000000..b2aa24cc --- /dev/null +++ b/tests/unit/src/unit/issues/Issue9543.hx @@ -0,0 +1,36 @@ +package unit.issues; + +private abstract A(String) { + public inline function new(s:String) { + this = s; + } + + public function f(index:Int) { + return StringTools.fastCodeAt(this, index); + } + + public inline function g(index:Int) { + return StringTools.fastCodeAt(this, index); + } +} + +class Issue9543 extends unit.Test { + #if (cpp && !cppia) + function testVar() { + var foo:Int = 0; + var bar:cpp.Reference = foo; + foo += 1; + eq(1, foo); + eq(1, bar); + } + + function testAssign() { + var foo:Int = 0; + var bar:cpp.Reference; + bar = foo; + foo += 1; + eq(1, foo); + eq(1, bar); + } + #end +} diff --git a/tests/unit/src/unit/issues/Issue9563.hx b/tests/unit/src/unit/issues/Issue9563.hx new file mode 100644 index 00000000..dc8340e9 --- /dev/null +++ b/tests/unit/src/unit/issues/Issue9563.hx @@ -0,0 +1,17 @@ +package unit.issues; + +import unit.Test; +import utest.Assert; + +enum abstract Noise(Null) { + final Noise = null; +} + +class Issue9563 extends Test { + function test() { + switch Noise { // Unmatched patterns: _ + case Noise: + } + Assert.pass(); + } +} diff --git a/tests/unit/src/unitstd/EReg.unit.hx b/tests/unit/src/unitstd/EReg.unit.hx index 01881d54..8b635aa0 100644 --- a/tests/unit/src/unitstd/EReg.unit.hx +++ b/tests/unit/src/unitstd/EReg.unit.hx @@ -87,7 +87,7 @@ pos.len == 2; ~/a/g.replace("bab", "z") == "bzb"; ~/a/g.replace("baba", "z") == "bzbz"; -#if !(hl && interp) // not allowed in local interpreter, still allowed in hl runtime +#if !(interp) // not allowed in local interpreter, still allowed in hl runtime // replace + $ ~/href="(.*?)"/.replace('lead href="foo" trail',"$1") == "lead foo trail"; //~/href="(.*?)"/.replace('lead href="foo" trail',"$2") == "lead $2 trail"; diff --git a/tests/unit/src/unitstd/Std.unit.hx b/tests/unit/src/unitstd/Std.unit.hx index f72a27f2..ede4bca4 100644 --- a/tests/unit/src/unitstd/Std.unit.hx +++ b/tests/unit/src/unitstd/Std.unit.hx @@ -71,53 +71,120 @@ Std.int(0.7) == 0; Std.int(0.2) == 0; // parseInt + +// general Std.parseInt("0") == 0; Std.parseInt("-1") == -1; -Std.parseInt(" 5") == 5; +// preceeding zeroes Std.parseInt("0001") == 1; Std.parseInt("0010") == 10; -Std.parseInt("100") == 100; -Std.parseInt("-100") == -100; +// trailing text Std.parseInt("100x123") == 100; Std.parseInt("12foo13") == 12; -Std.parseInt("") == null; -Std.parseInt("abcd") == null; -Std.parseInt("a10") == null; -Std.parseInt(null) == null; -Std.parseInt("0xFF") == 255; +Std.parseInt("23e2") == 23; +Std.parseInt("0x10z") == 16; +Std.parseInt("0x10x123") == 16; +Std.parseInt("0xff\n") == 255; +// hexadecimals +Std.parseInt("0xff") == 255; Std.parseInt("0x123") == 291; Std.parseInt("0XFF") == 255; Std.parseInt("0X123") == 291; Std.parseInt("0X01") == 1; Std.parseInt("0x01") == 1; -#if !neko //sorry, neko -#if !hl //see https://github.com/HaxeFoundation/hashlink/issues/330 -#if !cpp //see https://github.com/HaxeFoundation/hxcpp/issues/869 +// signs +Std.parseInt("123") == 123; +Std.parseInt("+123") == 123; +Std.parseInt("-123") == -123; +Std.parseInt("0xa0") == 160; +Std.parseInt("+0xa0") == 160; +Std.parseInt("-0xa0") == -160; +// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return +Std.parseInt(" 5") == 5; +Std.parseInt(" \t\n\x0b\x0c\r16") == 16; +Std.parseInt(" \t\n\x0b\x0c\r0xa") == 10; +// whitespace and signs +Std.parseInt(' 16') == 16; +Std.parseInt(' -16') == -16; +Std.parseInt(' +16') == 16; +Std.parseInt(' 0x10') == 16; Std.parseInt(' -0x10') == -16; -#end -#end -#end +Std.parseInt(' +0x10') == 16; +// binary and octal unsupported +Std.parseInt("010") == 10; +Std.parseInt("0b10") == 0; +// null +Std.parseInt(null) == null; +// no number +Std.parseInt("") == null; +Std.parseInt("abcd") == null; +Std.parseInt("a10") == null; +// invalid use of signs +Std.parseInt("++123") == null; +Std.parseInt("+-123") == null; +Std.parseInt("-+123") == null; +Std.parseInt("--123") == null; +Std.parseInt("+ 123") == null; +Std.parseInt("- 123") == null; +Std.parseInt("++0x123") == null; +Std.parseInt("+-0x123") == null; +Std.parseInt("-+0x123") == null; +Std.parseInt("--0x123") == null; +Std.parseInt("+ 0x123") == null; +Std.parseInt("- 0x123") == null; +// hexadecimal prefix with no number +unspec(Std.parseInt.bind("0x")); +unspec(Std.parseInt.bind("0x C")); +unspec(Std.parseInt.bind("0x+A")); // parseFloat + +// general Std.parseFloat("0") == 0.; -Std.parseFloat(" 5.3") == 5.3; +Std.parseFloat("0.0") == 0.; +// preceeding zeroes Std.parseFloat("0001") == 1.; -Std.parseFloat("100.45") == 100.45; -Std.parseFloat("-100.01") == -100.01; +Std.parseFloat("0010") == 10.; +// trailing text Std.parseFloat("100x123") == 100.; -Math.isNaN(Std.parseFloat("")) == true; -Math.isNaN(Std.parseFloat("abcd")) == true; -Math.isNaN(Std.parseFloat("a10")) == true; -Math.isNaN(Std.parseFloat(null)) == true; +Std.parseFloat("12foo13") == 12.; Std.parseFloat("5.3 ") == 5.3; -Std.parseFloat("0.0") == 0.; Std.parseFloat("5.3 1") == 5.3; +// signs +Std.parseFloat("123.45") == 123.45; +Std.parseFloat("+123.45") == 123.45; +Std.parseFloat("-123.45") == -123.45; +// whitespace: space, horizontal tab, newline, vertical tab, form feed, and carriage return +Std.parseFloat(" 5.2") == 5.2; +Std.parseFloat(" \t\n\x0b\x0c\r1.6") == 1.6; +// whitespace and signs +Std.parseFloat(' 1.6') == 1.6; +Std.parseFloat(' -1.6') == -1.6; +Std.parseFloat(' +1.6') == 1.6; +// exponent +Std.parseFloat("2.426670815e12") == 2.426670815e12; +Std.parseFloat("2.426670815E12") == 2.426670815e12; Std.parseFloat("2.426670815e+12") == 2.426670815e+12; Std.parseFloat("2.426670815E+12") == 2.426670815e+12; Std.parseFloat("2.426670815e-12") == 2.426670815e-12; Std.parseFloat("2.426670815E-12") == 2.426670815e-12; -// Std.parseInt("0x C") == 0; -// Std.parseInt("0x+A") == 0; +#if !interp +Std.parseFloat("6e") == 6; +Std.parseFloat("6E") == 6; +#end +// null +Math.isNaN(Std.parseFloat(null)) == true; +// no number +Math.isNaN(Std.parseFloat("")) == true; +Math.isNaN(Std.parseFloat("abcd")) == true; +Math.isNaN(Std.parseFloat("a10")) == true; +// invalid use of signs +Math.isNaN(Std.parseFloat("++12.3")) == true; +Math.isNaN(Std.parseFloat("+-12.3")) == true; +Math.isNaN(Std.parseFloat("-+12.3")) == true; +Math.isNaN(Std.parseFloat("--12.3")) == true; +Math.isNaN(Std.parseFloat("+ 12.3")) == true; +Math.isNaN(Std.parseFloat("- 12.3")) == true; // random var x = Std.random(2); diff --git a/tests/unit/src/unitstd/haxe/EnumFlags.unit.hx b/tests/unit/src/unitstd/haxe/EnumFlags.unit.hx index 03a944c8..2174ab59 100644 --- a/tests/unit/src/unitstd/haxe/EnumFlags.unit.hx +++ b/tests/unit/src/unitstd/haxe/EnumFlags.unit.hx @@ -42,6 +42,7 @@ flags.has(EA) == false; flags.has(EB) == true; flags.has(EC) == false; + // Big Enum (32) var bigFlags = new haxe.EnumFlags(1<<31); bigFlags.has( EF_31 ) == true; @@ -49,3 +50,12 @@ bigFlags.unset( EF_31 ); bigFlags.has( EF_31 ) == false; bigFlags.set( EF_31 ); bigFlags.has( EF_31 ) == true; + +// setTo +var flags = new haxe.EnumFlags(); +flags.setTo(EB, true); +flags.has(EB) == true; +flags.setTo(EB, false); +flags.has(EB) == false; +flags.setTo(EB, true); +flags.has(EB) == true; \ No newline at end of file diff --git a/tests/unit/src/unitstd/haxe/atomic/AtomicBool.unit.hx b/tests/unit/src/unitstd/haxe/atomic/AtomicBool.unit.hx new file mode 100644 index 00000000..0274f51b --- /dev/null +++ b/tests/unit/src/unitstd/haxe/atomic/AtomicBool.unit.hx @@ -0,0 +1,18 @@ +#if (target.atomics) +var a = new haxe.atomic.AtomicBool(true); + +a.load() == true; +a.store(false) == false; +a.load() == false; + +a.compareExchange(false, true) == false; +a.load() == true; + +a.compareExchange(false, false) == true; +a.load() == true; + +a.exchange(true) == true; +a.load() == true; +#else +0 == 0; // prevent "no assertions" warning +#end diff --git a/tests/unit/src/unitstd/haxe/atomic/AtomicInt.unit.hx b/tests/unit/src/unitstd/haxe/atomic/AtomicInt.unit.hx new file mode 100644 index 00000000..54bbffa9 --- /dev/null +++ b/tests/unit/src/unitstd/haxe/atomic/AtomicInt.unit.hx @@ -0,0 +1,33 @@ +#if target.atomics +var a = new haxe.atomic.AtomicInt(0); + +a.load() == 0; + +a.store(5) == 5; +a.load() == 5; + +a.add(5) == 5; +a.load() == 10; + +a.sub(5) == 10; +a.load() == 5; + +a.and(20) == 5; +a.load() == 4; + +a.or(3) == 4; +a.load() == 7; + +a.xor(2) == 7; +a.load() == 5; + +a.compareExchange(0, 0) == 5; +a.load() == 5; +a.compareExchange(5, 0) == 5; +a.load() == 0; + +a.exchange(10) == 0; +a.load() == 10; +#else +0 == 0; // prevent "no assertions" warning +#end \ No newline at end of file diff --git a/tests/unit/src/unitstd/haxe/atomic/AtomicObject.unit.hx b/tests/unit/src/unitstd/haxe/atomic/AtomicObject.unit.hx new file mode 100644 index 00000000..7f95e195 --- /dev/null +++ b/tests/unit/src/unitstd/haxe/atomic/AtomicObject.unit.hx @@ -0,0 +1,15 @@ +#if (target.atomics && !(js || cpp)) +var a = new haxe.atomic.AtomicObject("Hey World!"); + +a.load() == "Hey World!"; +a.store("Hello World!") == "Hello World!"; +a.load() == "Hello World!"; + +a.compareExchange("Hello World!", "Goodbye World!") == "Hello World!"; +a.load() == "Goodbye World!"; + +a.exchange("Hello World!") == "Goodbye World!"; +a.load() == "Hello World!"; +#else +0 == 0; // prevent "no assertions" warning +#end diff --git a/tests/unit/src/unitstd/haxe/crypto/Base64.unit.hx b/tests/unit/src/unitstd/haxe/crypto/Base64.unit.hx new file mode 100644 index 00000000..073b8b0f --- /dev/null +++ b/tests/unit/src/unitstd/haxe/crypto/Base64.unit.hx @@ -0,0 +1,45 @@ +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("The quick brown fox jumps over the lazy dog"), true) == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("The quick brown fox jumps over the lazy dog"), false) == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw"; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("The quick brown fox jumps over the lazy dog"), true) == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw=="; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("The quick brown fox jumps over the lazy dog"), false) == "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw"; + +haxe.crypto.Base64.decode("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==", true).toString() == "The quick brown fox jumps over the lazy dog"; +haxe.crypto.Base64.decode("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw", false).toString() == "The quick brown fox jumps over the lazy dog"; +haxe.crypto.Base64.urlDecode("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==", true).toString() == "The quick brown fox jumps over the lazy dog"; +haxe.crypto.Base64.urlDecode("VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw", false).toString() == "The quick brown fox jumps over the lazy dog"; + +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("a"), true) == "YQ=="; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab"), true) == "YWI="; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab?"), true) == "YWI/"; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab~c"), true) == "YWJ+Yw=="; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("a"), false) == "YQ"; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab"), false) == "YWI"; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab?"), false) == "YWI/"; +haxe.crypto.Base64.encode(haxe.io.Bytes.ofString("ab~c"), false) == "YWJ+Yw"; + +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("a"), true) == "YQ=="; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab"), true) == "YWI="; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab?"), true) == "YWI_"; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab~c"), true) == "YWJ-Yw=="; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("a"), false) == "YQ"; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab"), false) == "YWI"; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab?"), false) == "YWI_"; +haxe.crypto.Base64.urlEncode(haxe.io.Bytes.ofString("ab~c"), false) == "YWJ-Yw"; + +haxe.crypto.Base64.decode("YQ==", true).toString() == "a"; +haxe.crypto.Base64.decode("YWI=", true).toString() == "ab"; +haxe.crypto.Base64.decode("YWI/", true).toString() == "ab?"; +haxe.crypto.Base64.decode("YWJ+Yw==", true).toString() == "ab~c"; +haxe.crypto.Base64.decode("YQ", false).toString() == "a"; +haxe.crypto.Base64.decode("YWI", false).toString() == "ab"; +haxe.crypto.Base64.decode("YWI/", false).toString() == "ab?"; +haxe.crypto.Base64.decode("YWJ+Yw", false).toString() == "ab~c"; + +haxe.crypto.Base64.urlDecode("YQ==", true).toString() == "a"; +haxe.crypto.Base64.urlDecode("YWI=", true).toString() == "ab"; +haxe.crypto.Base64.urlDecode("YWI_", true).toString() == "ab?"; +haxe.crypto.Base64.urlDecode("YWJ-Yw==", true).toString() == "ab~c"; +haxe.crypto.Base64.urlDecode("YQ", false).toString() == "a"; +haxe.crypto.Base64.urlDecode("YWI", false).toString() == "ab"; +haxe.crypto.Base64.urlDecode("YWI_", false).toString() == "ab?"; +haxe.crypto.Base64.urlDecode("YWJ-Yw", false).toString() == "ab~c"; diff --git a/tests/unit/src/unitstd/haxe/ds/Vector.unit.hx b/tests/unit/src/unitstd/haxe/ds/Vector.unit.hx index cbd84f0b..2fd79750 100644 --- a/tests/unit/src/unitstd/haxe/ds/Vector.unit.hx +++ b/tests/unit/src/unitstd/haxe/ds/Vector.unit.hx @@ -93,6 +93,20 @@ vec3[4] == 4; vec3[5] == 5; vec3[6] == 6; +var vec5 = new haxe.ds.Vector(3, 5); +vec5[0] == 5; +vec5[1] == 5; +vec5[2] == 5; +vec5.fill(1); +vec5[0] == 1; +vec5[1] == 1; +vec5[2] == 1; + +var vec5 = new haxe.ds.Vector(3, true); +vec5[0] == true; +vec5[1] == true; +vec5[2] == true; + var vec5 = haxe.ds.Vector.fromArrayCopy([0,1,2,3,4]); haxe.ds.Vector.blit(vec5, 0, vec5, 1, 4); vec5[0] == 0; diff --git a/tests/unit/src/unitstd/haxe/macro/ExprTools.unit.hx b/tests/unit/src/unitstd/haxe/macro/ExprTools.unit.hx index af33a7a8..c276cb98 100644 --- a/tests/unit/src/unitstd/haxe/macro/ExprTools.unit.hx +++ b/tests/unit/src/unitstd/haxe/macro/ExprTools.unit.hx @@ -72,29 +72,29 @@ iter(econst, fail); iter(econtinue, fail); iter(ebreak, fail); iter(efield, seq.bind("EConst(CString(foo,DoubleQuotes))")); -iter(eparenthesis, seq.bind("EConst(CInt(1))")); -iter(euntyped, seq.bind("EConst(CInt(1))")); -iter(ethrow, seq.bind("EConst(CInt(1))")); -iter(eunop, seq.bind("EConst(CInt(1))")); -iter(ecast, seq.bind("EConst(CInt(1))")); -iter(emeta, seq.bind("EConst(CInt(1))")); -check(earray, ["EConst(CInt(1))", "EConst(CInt(0))"]); -check(ewhile1, ["EConst(CInt(1))", "EConst(CString(foo,DoubleQuotes))"]); -check(ewhile2, ["EConst(CInt(1))", "EConst(CString(foo,DoubleQuotes))"]); -check(ebinop, ["EConst(CInt(1))", "EConst(CInt(1))"]); -check(efor, ["EConst(CInt(1))", "EConst(CString(foo,DoubleQuotes))"]); -check(ein, ["EConst(CIdent(i))", "EConst(CInt(1))"]); -check(evars, ["EConst(CInt(1))", "EConst(CInt(2))"]); -check(etry, ["EConst(CInt(1))", "EConst(CString(foo,DoubleQuotes))", "EConst(CString(bar,DoubleQuotes))"]); -check(eternary, ["EConst(CInt(1))", "EConst(CInt(2))", "EConst(CInt(3))"]); -check(earraydecl, ["EConst(CInt(1))", "EConst(CInt(2))"]); -check(enew, ["EConst(CInt(1))", "EConst(CInt(2))"]); -check(eblock, ["EConst(CInt(1))", "EConst(CInt(2))"]); -check(eobjectdecl, ["EConst(CInt(1))", "EConst(CInt(2))"]); -check(ecall, ["EConst(CIdent(foo))", "EConst(CInt(1))", "EConst(CInt(2))"]); -check(ereturn, ["EConst(CInt(1))"]); -check(efunction, ["EConst(CInt(1))", "EConst(CInt(2))", "EConst(CInt(3))"]); -check(eswitch, ["EConst(CInt(1))", "EConst(CInt(2))", "EConst(CInt(3))", "EConst(CInt(4))", "EConst(CInt(5))", "EConst(CInt(6))"]); +iter(eparenthesis, seq.bind("EConst(CInt(1,null))")); +iter(euntyped, seq.bind("EConst(CInt(1,null))")); +iter(ethrow, seq.bind("EConst(CInt(1,null))")); +iter(eunop, seq.bind("EConst(CInt(1,null))")); +iter(ecast, seq.bind("EConst(CInt(1,null))")); +iter(emeta, seq.bind("EConst(CInt(1,null))")); +check(earray, ["EConst(CInt(1,null))", "EConst(CInt(0,null))"]); +check(ewhile1, ["EConst(CInt(1,null))", "EConst(CString(foo,DoubleQuotes))"]); +check(ewhile2, ["EConst(CInt(1,null))", "EConst(CString(foo,DoubleQuotes))"]); +check(ebinop, ["EConst(CInt(1,null))", "EConst(CInt(1,null))"]); +check(efor, ["EConst(CInt(1,null))", "EConst(CString(foo,DoubleQuotes))"]); +check(ein, ["EConst(CIdent(i))", "EConst(CInt(1,null))"]); +check(evars, ["EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(etry, ["EConst(CInt(1,null))", "EConst(CString(foo,DoubleQuotes))", "EConst(CString(bar,DoubleQuotes))"]); +check(eternary, ["EConst(CInt(1,null))", "EConst(CInt(2,null))", "EConst(CInt(3,null))"]); +check(earraydecl, ["EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(enew, ["EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(eblock, ["EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(eobjectdecl, ["EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(ecall, ["EConst(CIdent(foo))", "EConst(CInt(1,null))", "EConst(CInt(2,null))"]); +check(ereturn, ["EConst(CInt(1,null))"]); +check(efunction, ["EConst(CInt(1,null))", "EConst(CInt(2,null))", "EConst(CInt(3,null))"]); +check(eswitch, ["EConst(CInt(1,null))", "EConst(CInt(2,null))", "EConst(CInt(3,null))", "EConst(CInt(4,null))", "EConst(CInt(5,null))", "EConst(CInt(6,null))"]); // map function wrap(e) return macro ($e); diff --git a/tests/unit/src/unitstd/sys/net/Socket.unit.hx b/tests/unit/src/unitstd/sys/net/Socket.unit.hx index 7ab146fd..0a6191f1 100644 --- a/tests/unit/src/unitstd/sys/net/Socket.unit.hx +++ b/tests/unit/src/unitstd/sys/net/Socket.unit.hx @@ -1,7 +1,5 @@ #if sys -#if !(php && Windows) - // bind & listen var s = new sys.net.Socket(); var host = new sys.net.Host("127.0.0.1"); @@ -61,10 +59,6 @@ c.read() == "abc"; c.close(); s.close(); -#else //!(php && Windows) -1 == 1; -#end - #else 1 == 1; #end \ No newline at end of file diff --git a/tests/unit/unit.html b/tests/unit/unit.html index 71da94f6..799f5498 100644 --- a/tests/unit/unit.html +++ b/tests/unit/unit.html @@ -56,23 +56,23 @@ iframe {
-
Flash 9
+
Flash
- + -